# Customization Guide

**Note**: This is my personal CV website. While the code is open-source (MIT license), this guide documents my own customizations. The site is subject to modifications without notice, and I don't intend for others to use this as a template - it's publicly available code, but it's designed for my personal use.

## Table of Contents

- [Introduction](#introduction)
- [Prerequisites](#prerequisites)
- [Quick Customization](#quick-customization)
- [Content Customization](#content-customization)
  - [Personal Information](#personal-information)
  - [JSON Schema Explained](#json-schema-explained)
  - [Adding/Removing Sections](#addingremoving-sections)
- [Visual Customization](#visual-customization)
  - [Colors & Fonts](#colors--fonts)
  - [Layout Changes](#layout-changes)
  - [Branding](#branding)
- [Template Customization](#template-customization)
- [Analytics Configuration](#analytics-configuration)
  - [Option 1: Configure Your Own Analytics](#option-1-configure-your-own-analytics)
  - [Option 2: Remove Analytics Entirely](#option-2-remove-analytics-entirely)
  - [Option 3: Use Alternative Analytics Service](#option-3-use-alternative-analytics-service)
  - [Option 3: Use Alternative Analytics](#option-3-use-google-analytics-or-other-service)
- [Advanced Customization](#advanced-customization)
- [Testing Your Changes](#testing-your-changes)
- [Examples](#examples)

---

## Introduction

This CV/Resume application is designed to be easily customizable. You can adapt it for your own CV by modifying JSON files, templates, and styles without deep Go programming knowledge.

**Architecture Overview**:
- **Data**: JSON files (`data/cv-en.json`, `data/cv-es.json`)
- **Models**: Go structs (`internal/models/cv.go`) define data structure
- **Templates**: Go HTML templates (`templates/*.html`) render the CV
- **Styles**: CSS (`static/css/main.css`) controls appearance
- **Assets**: Images, fonts (`static/` directory)

**Customization Levels**:
1. **Basic**: Edit JSON files only (name, experience, skills)
2. **Intermediate**: Modify CSS styles and add images
3. **Advanced**: Change templates and Go models

---

## Prerequisites

### Knowledge Requirements
- **Basic**: JSON syntax, file editing
- **Intermediate**: HTML/CSS, command-line basics
- **Advanced**: Go templates, basic Go programming

### Tools Needed
- **Text editor**: VS Code, Sublime Text, or any editor
- **Go 1.25.1+**: For building and testing (see [8-DEPLOYMENT.md](8-DEPLOYMENT.md))
- **Git**: For version control (optional but recommended)
- **Browser**: For testing (Chrome/Firefox recommended)

### Optional Tools
- **JSON validator**: [JSONLint](https://jsonlint.com/)
- **Image editor**: For logo/photo preparation
- **Make**: For using Makefile commands

---

## Quick Customization

**Get started in 5 minutes**:

```bash
# 1. Clone or download the project
git clone https://github.com/juanatsap/cv-site.git my-cv
cd my-cv

# 2. Edit your information
nano data/cv-en.json  # or use your favorite editor

# 3. Replace your photo
cp ~/my-photo.jpg static/images/profile/dni.jpeg

# 4. Test locally
make dev

# 5. Open browser
open http://localhost:1999
```

**Minimal changes to make it yours**:
1. Replace `personal` section in `data/cv-en.json`
2. Replace `summary` section
3. Replace `experience` section with your jobs
4. Replace `education` section
5. Update `skills` section
6. Replace profile photo
7. **Configure or remove analytics** (see [Analytics Configuration](#analytics-configuration) below)

---

## Content Customization

### Personal Information

Edit `data/cv-en.json` (and `data/cv-es.json` for Spanish):

**Location**: Top of JSON file, `personal` object

```json
{
  "personal": {
    "name": "Your Full Name",
    "title": "Your Professional Title",
    "location": "Your City, Country",
    "email": "your.email@example.com",
    "phone": "+1 234 567 8900",
    "dateOfBirth": "1990-01-01",
    "placeOfBirth": "Your Birthplace",
    "citizenship": "Your Nationality",
    "linkedin": "https://www.linkedin.com/in/your-profile",
    "github": "https://github.com/yourusername",
    "domestika": "https://www.domestika.org/en/yourusername",
    "website": "https://yourwebsite.com",
    "photo": "/static/images/profile.jpg"
  }
}
```

**Field Descriptions**:
- `name`: Full name (displayed prominently)
- `title`: Job title or professional tagline
- `location`: Current location
- `email`: Contact email (clickable in CV)
- `phone`: Phone number with country code
- `dateOfBirth`: Birth date (YYYY-MM-DD format)
- `placeOfBirth`: Birthplace
- `citizenship`: Nationality/citizenship
- `linkedin`, `github`, `domestika`, `website`: Social/professional links
- `photo`: Path to profile photo (relative to project root)

**Tips**:
- Use **consistent formatting** across English and Spanish versions
- Keep URLs **absolute** (include `https://`)
- Use **international phone format** (+XX XXX XXX XXXX)
- Photo should be **square** (400x400px minimum) for best results

---

### JSON Schema Explained

The CV data follows a structured schema. Each section has specific fields.

#### Summary Section

```json
{
  "summary": "Your professional summary. 2-3 sentences highlighting your expertise, experience, and career goals. This appears prominently at the top of your CV."
}
```

**Tips**:
- Keep it **concise** (100-150 words)
- Highlight **key achievements** and expertise
- Tailor to your **target audience**

---

#### Experience Section

**Structure**:
```json
{
  "experience": [
    {
      "position": "Job Title",
      "company": "Company Name",
      "companyURL": "https://company.com",  // Optional
      "companyLogo": "company-logo.png",     // Optional, in static/images/companies/
      "location": "City, Country",
      "startDate": "2020-01",                // YYYY-MM format
      "endDate": "2023-06",                  // Or "present"
      "current": false,                      // true if still working here
      "expired": false,                      // Optional, true if company closed
      "shortDescription": "Brief one-line summary for compact view",
      "responsibilities": [
        "Responsibility or achievement 1",
        "Responsibility or achievement 2",
        "Use bullet points for clarity"
      ],
      "technologies": [
        "Technology 1",
        "Technology 2",
        "List relevant tech stack"
      ],
      "highlights": [                        // Optional
        "Major achievement 1",
        "Major achievement 2"
      ]
    }
  ]
}
```

**Field Details**:
- `position`: Job title
- `company`: Company name
- `companyURL`: Company website (optional, makes company name clickable)
- `companyLogo`: Logo filename (place in `static/images/companies/`)
- `location`: Office location
- `startDate`: Start date (YYYY-MM format)
- `endDate`: End date or `"present"` for current job
- `current`: Boolean, `true` if still employed
- `expired`: Boolean, `true` if company no longer exists (grays out logo)
- `shortDescription`: One-liner for short CV version (HTML allowed)
- `responsibilities`: Array of bullet points (HTML allowed)
- `technologies`: Array of technologies used
- `highlights`: Optional array of major achievements

**HTML in Descriptions**:
You can use HTML tags:
```json
"shortDescription": "Led development of <a href='https://example.com' target='_blank' rel='noopener noreferrer'>major platform</a> serving 1M+ users."
```

**Adding Company Logos**:
1. Place logo in `static/images/companies/`
2. Reference filename in `companyLogo` field
3. Recommended size: 100x100px, PNG with transparency
4. Fallback icon appears if logo missing

---

#### Education Section

```json
{
  "education": [
    {
      "degree": "Bachelor's Degree in Computer Science",
      "institution": "University Name",
      "location": "City, Country",
      "startDate": "2015-09",
      "endDate": "2019-06",
      "field": "Computer Science and Engineering"
    }
  ]
}
```

**Tips**:
- List **highest degree first**
- Include **relevant coursework** in degree name if needed
- Use `field` for specialization

---

#### Skills Section

**Two types**: Technical skills (with sidebar placement) and soft skills

```json
{
  "skills": {
    "technical": [
      {
        "category": "Programming Languages",
        "proficiency": 5,              // 1-5 scale (not displayed, for internal use)
        "sidebar": "left",             // "left" or "right" or omit for main content
        "items": [
          "JavaScript (ES6+)",
          "Python",
          "Go"
        ]
      },
      {
        "category": "Frontend Technologies",
        "proficiency": 5,
        "sidebar": "left",
        "items": [
          "React",
          "HTMX",
          "CSS3"
        ]
      }
    ],
    "soft_skills": [
      "Leadership & Team Management",
      "Problem-Solving",
      "Communication"
    ]
  }
}
```

**Sidebar Layout**:
- **Left sidebar**: Skills displayed on page 1 left side
- **Right sidebar**: Skills displayed on page 2 right side
- **No sidebar**: Skills displayed in main content area

**Organizing Skills**:
1. Group by **category** (e.g., "Programming Languages", "Databases")
2. Order by **importance** (most important categories first)
3. Use **specific names** (e.g., "PostgreSQL" not just "SQL")
4. Balance **left/right sidebars** for visual symmetry

---

#### Projects Section

```json
{
  "projects": [
    {
      "title": "Full Project Title",          // Used if no projectName
      "projectName": "Project Name",          // Optional: linkable part
      "projectDesc": "Project Description",   // Optional: non-linkable part
      "url": "https://project.com",           // Optional
      "projectLogo": "project-logo.png",      // Optional, in static/images/projects/
      "gitRepoUrl": "/path/to/local/repo",    // Optional: for dynamic dates
      "location": "City or 'Online'",
      "startDate": "2023",                    // Optional: YYYY or YYYY-MM
      "current": true,                        // true if ongoing
      "maintainedBy": "Company Name",         // Optional: if transferred
      "technologies": [
        "Tech 1",
        "Tech 2"
      ],
      "shortDescription": "One-line project summary",
      "responsibilities": [
        "What you built or contributed",
        "Use bullet points"
      ]
    }
  ]
}
```

**Dynamic Dates** (Advanced):
- Use `gitRepoUrl` to point to a local git repository
- Application will extract first commit date as start date
- Useful for open-source projects where you want automatic dating

**Project Logos**:
- Place in `static/images/projects/`
- Recommended: 80x80px, PNG with transparency

---

#### Languages Section

```json
{
  "languages": [
    {
      "language": "English",
      "proficiency": "Native",
      "level": 5,                           // 1-5 scale (not displayed)
      "detail": "Oral (Advanced) Written (Advanced)"  // Optional
    },
    {
      "language": "Spanish",
      "proficiency": "Professional Working Proficiency",
      "level": 4
    }
  ]
}
```

**Proficiency Levels** (suggested):
- Native
- Bilingual/Fluent
- Professional Working Proficiency
- Limited Working Proficiency
- Elementary/Comprehension

---

#### Courses/Certifications Sections

**Courses**:
```json
{
  "courses": [
    {
      "title": "Course Title",
      "institution": "Platform or Institution",
      "courseLogo": "platform-logo.png",      // Optional, in static/images/courses/
      "location": "Online or City",
      "date": "2024-03",                      // YYYY-MM or range
      "duration": "40 hours",
      "shortDescription": "Brief course overview",
      "responsibilities": [                    // Optional: detailed course content
        "Topic 1 covered",
        "Topic 2 covered"
      ]
    }
  ]
}
```

**Certifications**:
```json
{
  "certifications": [
    {
      "name": "Certification Name",
      "issuer": "Issuing Organization",
      "date": "2024-01",
      "description": "What this certification covers"
    }
  ]
}
```

**Course Logos**:
- Place in `static/images/courses/`
- Examples: Codecademy, LinkedIn Learning, Coursera logos

---

#### Awards Section

```json
{
  "awards": [
    {
      "title": "Award Title",
      "issuer": "Issuing Organization",
      "date": "09 2023",                    // MM YYYY format
      "shortDescription": "Brief description of award",
      "responsibilities": [                 // Optional: what you did to earn it
        "Achievement 1",
        "Achievement 2"
      ],
      "awardLogo": "award-logo.png"         // Optional, in static/images/companies/
    }
  ]
}
```

---

#### References Section

```json
{
  "references": [
    {
      "title": "Full reference text",
      "url": "https://example.com",
      "type": "recommendation",              // recommendation, portfolio, profile, cv, presentation
      "textBefore": "Text before link",      // Optional
      "linkText": "Clickable text",          // Optional: bold linked text
      "textAfter": "text after link"         // Optional
    }
  ]
}
```

**Types**:
- `recommendation`: Reference letters
- `portfolio`: Online portfolio
- `profile`: LinkedIn, GitHub, etc.
- `cv`: Other CV versions
- `presentation`: Presentation letters

**Example rendering**:
```
Text before link Clickable text text after link
```

---

#### Other Section

```json
{
  "other": {
    "driverLicense": "<strong>Type B</strong>"  // HTML allowed
  }
}
```

Add any miscellaneous information here.

---

#### Meta Section

```json
{
  "meta": {
    "version": "2025-11-09",              // Your CV version
    "lastUpdated": "2025-11-08",          // Last update date
    "format": "JSON Resume Extended",
    "language": "en"                      // "en" or "es"
  }
}
```

Update `lastUpdated` when you make changes.

---

### Adding/Removing Sections

#### Removing Sections

**Option 1**: Empty the array/object
```json
{
  "awards": [],
  "courses": []
}
```

Templates automatically hide empty sections.

**Option 2**: Remove from template
Edit `templates/cv-content.html` and delete the section:
```html
<!-- Remove this entire block -->
{{if .CV.Awards}}
<section id="awards" class="cv-section">
  ...
</section>
{{end}}
```

#### Adding New Sections

**Step 1**: Add to JSON
```json
{
  "volunteering": [
    {
      "organization": "Organization Name",
      "role": "Volunteer Role",
      "startDate": "2020-01",
      "endDate": "present",
      "description": "What you did"
    }
  ]
}
```

**Step 2**: Add to Go model (`internal/models/cv.go`)
```go
type CV struct {
    // ... existing fields ...
    Volunteering []Volunteering `json:"volunteering"`
}

type Volunteering struct {
    Organization string `json:"organization"`
    Role         string `json:"role"`
    StartDate    string `json:"startDate"`
    EndDate      string `json:"endDate"`
    Description  string `json:"description"`
}
```

**Step 3**: Add to template (`templates/cv-content.html`)
```html
<!-- Add wherever you want it to appear -->
{{if .CV.Volunteering}}
<section id="volunteering" class="cv-section">
    <details open>
        <summary>
            <h3 class="section-title">
                <iconify-icon icon="mdi:hand-heart" width="24" height="24" class="section-icon"></iconify-icon>
                {{if eq .Lang "es"}}Voluntariado{{else}}Volunteering{{end}}
            </h3>
        </summary>
    {{range .CV.Volunteering}}
    <div class="volunteering-item">
        <strong>{{.Role}} - {{.Organization}}</strong><br>
        <small>{{.StartDate}} / {{.EndDate}}</small>
        <p>{{.Description}}</p>
    </div>
    {{end}}
    </details>
</section>
{{end}}
```

**Step 4**: Rebuild and test
```bash
make build
make dev
```

---

## Visual Customization

### Colors & Fonts

**Location**: `static/css/main.css`

#### Color Scheme

**CSS Variables** (lines 6-15):
```css
:root {
    --bg-gray: rgb(82, 86, 89);          /* Page background */
    --sidebar-gray: #d1d4d2;             /* Sidebar background */
    --black-bar: #2b2b2b;                /* Top action bar */
    --paper-white: #ffffff;              /* CV paper background */
    --text-dark: rgb(0, 0, 0);           /* Main text */
    --text-gray: rgb(51, 51, 51);        /* Secondary text */
    --accent-blue: #0066cc;              /* Links and accents */
    --border-gray: #dddddd;              /* Borders */
}
```

**Changing Colors**:
```css
/* Example: Blue theme */
:root {
    --bg-gray: #1a365d;                  /* Dark blue background */
    --sidebar-gray: #e3f2fd;             /* Light blue sidebar */
    --accent-blue: #2196f3;              /* Bright blue links */
    --black-bar: #0d47a1;                /* Deep blue bar */
}

/* Example: Dark theme */
:root {
    --bg-gray: #1e1e1e;                  /* Dark background */
    --sidebar-gray: #2d2d2d;             /* Dark sidebar */
    --paper-white: #252526;              /* Dark paper */
    --text-dark: #d4d4d4;                /* Light text */
    --text-gray: #9d9d9d;                /* Gray text */
    --accent-blue: #569cd6;              /* Blue accent */
}
```

#### Fonts

**Current fonts** (line 4):
```css
@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;600;700&family=Source+Sans+Pro:wght@400;600&family=Inter:wght@400;500;600;700&display=swap');
```

**Changing fonts**:

1. **Choose fonts** from [Google Fonts](https://fonts.google.com/)
2. **Update import**:
```css
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&family=Open+Sans:wght@400;600&display=swap');
```

3. **Update font family** (line 24):
```css
body {
    font-family: 'Roboto', 'Open Sans', -apple-system, system-ui, sans-serif;
}
```

4. **Customize headings** (add after body):
```css
h1, h2, h3 {
    font-family: 'Playfair Display', serif;
}
```

**Font sizes**:
```css
/* Increase base font size */
body {
    font-size: 18px;  /* Default: 16px */
}

/* Adjust specific elements */
.cv-name {
    font-size: 2.5rem;  /* Larger name */
}

.section-title {
    font-size: 1.3rem;  /* Larger section titles */
}
```

---

### Layout Changes

#### Page Width

**Current**: A4 paper size (210mm x 297mm)

**Wider layout**:
```css
.cv-page {
    max-width: 250mm;  /* Wider (default: fit to A4) */
    min-height: 330mm; /* Taller */
}
```

**US Letter**:
```css
.cv-page {
    max-width: 8.5in;   /* Letter width */
    min-height: 11in;   /* Letter height */
}
```

#### Grid Adjustments

**Sidebar widths** (find `.page-content` section):
```css
.page-content {
    display: grid;
    grid-template-columns: 200px 1fr;  /* Left sidebar | Main */
}

/* Page 2: Main | Right sidebar */
.page-2 .page-content {
    grid-template-columns: 1fr 200px;  /* Make sidebar wider */
}
```

**Adjust gap**:
```css
.page-content {
    gap: 25px;  /* Space between sidebar and main (default: 20px) */
}
```

#### Responsive Breakpoints

**Mobile view** (add to end of CSS):
```css
@media screen and (max-width: 768px) {
    .page-content {
        grid-template-columns: 1fr;  /* Stack vertically */
    }

    .cv-sidebar {
        order: 2;  /* Sidebars after main content */
    }

    .cv-main {
        order: 1;  /* Main content first */
    }
}
```

#### Print Styles

**Current print styles** handle page breaks. Customize:
```css
@media print {
    body {
        background: white;  /* No background texture when printing */
    }

    .cv-page {
        box-shadow: none;   /* Remove shadow for printing */
        margin: 0;
        page-break-after: always;
    }

    /* Hide elements when printing */
    .action-bar {
        display: none;
    }

    /* Prevent page breaks inside elements */
    .experience-item,
    .project-item {
        page-break-inside: avoid;
    }
}
```

---

### Branding

#### Adding Your Logo

**Step 1**: Prepare logo
- Format: PNG with transparency preferred
- Size: 200x80px (width x height)
- Location: `static/images/logo.png`

**Step 2**: Add to template (`templates/cv-content.html`)

Add after header:
```html
<div class="cv-header">
    <div class="cv-logo">
        <img src="/static/images/logo.png" alt="Your Brand Logo">
    </div>
    <!-- Existing header content -->
</div>
```

**Step 3**: Style logo (`static/css/main.css`):
```css
.cv-logo {
    text-align: center;
    margin-bottom: 20px;
}

.cv-logo img {
    max-width: 200px;
    height: auto;
}
```

#### Favicon Replacement

**Current favicon**: Browser tab icon

**Replace**:
1. Create favicon (16x16, 32x32, 48x48 px)
2. Use [Favicon Generator](https://realfavicongenerator.net/)
3. Place in `static/images/favicon/`
4. Update in `templates/index.html`:

```html
<head>
    <link rel="icon" type="image/png" sizes="32x32" href="/static/images/favicon/favicon-32x32.png">
    <link rel="icon" type="image/png" sizes="16x16" href="/static/images/favicon/favicon-16x16.png">
    <link rel="apple-touch-icon" sizes="180x180" href="/static/images/favicon/apple-touch-icon.png">
</head>
```

#### Custom Icons

**Current**: Using [Iconify](https://iconify.design/)

**Change icons** in templates:
```html
<!-- Old -->
<iconify-icon icon="mdi:office-building" width="24" height="24"></iconify-icon>

<!-- New (browse icons at iconify.design) -->
<iconify-icon icon="fa6-solid:building" width="24" height="24"></iconify-icon>
<iconify-icon icon="carbon:user-avatar" width="24" height="24"></iconify-icon>
```

**Use custom icons**:
Replace with `<img>` tags:
```html
<img src="/static/images/icons/custom-icon.svg" alt="Icon" class="section-icon">
```

---

## Template Customization

Templates use **Go template syntax**. Basic knowledge helps but isn't required for simple changes.

### Go Template Syntax Basics

**Variables**:
```html
{{.CV.Personal.Name}}         <!-- Output variable -->
{{.CV.Personal.Email}}
```

**Conditionals**:
```html
{{if .CV.Awards}}
    <!-- Only show if awards exist -->
    <section>Awards here</section>
{{end}}

{{if eq .Lang "es"}}
    Español
{{else}}
    English
{{end}}
```

**Loops**:
```html
{{range .CV.Experience}}
    <div>{{.Position}} at {{.Company}}</div>
{{end}}
```

**Safe HTML** (allow HTML in content):
```html
{{.Description | safeHTML}}
```

### Modifying cv-content.html

**Location**: `templates/cv-content.html`

#### Example: Change Section Order

Move "Education" before "Experience":

1. Find Education section (around line 50)
2. Cut the entire `<section id="education">...</section>` block
3. Paste it before the Experience section (line 86)

#### Example: Change Header Layout

**Current** (lines 35-46):
```html
<div class="cv-header">
    <div class="cv-header-content">
        <div class="cv-header-left">
            <h1 class="cv-name">Moreno Rubio, Juan Andrés</h1>
            <p class="years-experience">{{.YearsOfExperience}} years</p>
            <div class="intro-text">{{.CV.Summary}}</div>
        </div>
        <div class="cv-photo">
            <img src="/static/images/profile/dni.jpeg" alt="{{.CV.Personal.Name}}">
        </div>
    </div>
</div>
```

**Customize**: Change name format
```html
<h1 class="cv-name">{{.CV.Personal.Name}}</h1>
<!-- Instead of hardcoded "Moreno Rubio, Juan Andrés" -->
```

**Add contact info to header**:
```html
<div class="cv-header-left">
    <h1 class="cv-name">{{.CV.Personal.Name}}</h1>
    <p class="cv-contact">
        {{.CV.Personal.Email}} | {{.CV.Personal.Phone}}
    </p>
    <div class="intro-text">{{.CV.Summary}}</div>
</div>
```

#### Example: Custom Section

Add "Hobbies" section:

```html
<section id="hobbies" class="cv-section">
    <h3 class="section-title">
        <iconify-icon icon="mdi:heart" width="24" height="24" class="section-icon"></iconify-icon>
        {{if eq .Lang "es"}}Aficiones{{else}}Hobbies{{end}}
    </h3>
    <ul>
        {{range .CV.Hobbies}}
        <li>{{.}}</li>
        {{end}}
    </ul>
</section>
```

Don't forget to add to JSON and Go model!

### Adding Custom Template Functions

**Location**: `main.go` (where templates are loaded)

**Example**: Add function to format dates
```go
// In main.go, find template loading section and add:

funcMap := template.FuncMap{
    "safeHTML": func(s string) template.HTML {
        return template.HTML(s)
    },
    "formatDate": func(dateStr string) string {
        // Parse YYYY-MM format and return "Month Year"
        parts := strings.Split(dateStr, "-")
        if len(parts) != 2 {
            return dateStr
        }
        months := []string{"", "January", "February", "March", "April", "May", "June",
            "July", "August", "September", "October", "November", "December"}
        monthNum, _ := strconv.Atoi(parts[1])
        return months[monthNum] + " " + parts[0]
    },
}

tmpl := template.New("").Funcs(funcMap)
```

**Use in template**:
```html
<small>{{formatDate .StartDate}} / {{formatDate .EndDate}}</small>
```

### Conditional Rendering

**Show section only in long version**:
```html
<div class="long-only">
    <!-- Only visible when user toggles to long CV -->
    {{range .Responsibilities}}
    <li>{{. | safeHTML}}</li>
    {{end}}
</div>
```

**Show section only in short version**:
```html
<div class="short-desc">
    {{.ShortDescription | safeHTML}}
</div>
```

**Language-specific content**:
```html
{{if eq .Lang "es"}}
    <p>Contenido en español</p>
{{else}}
    <p>Content in English</p>
{{end}}
```

---

## Analytics Configuration

This template includes a self-hosted analytics implementation as a learning example. You have three options:

### Option 1: Configure Your Own Analytics

If you want to use self-hosted analytics:

1. **Set up your analytics server** (Matomo, Plausible, or similar)

2. **Update tracking code** in `templates/index.html`:
   ```javascript
   // Find this section near the end of the file
   var _paq = window._paq = window._paq || [];
   _paq.push(['trackPageView']);
   _paq.push(['enableLinkTracking']);

   var u="https://YOUR-ANALYTICS-SERVER.COM/"; // Replace this
   _paq.push(['setTrackerUrl', u+'matomo.php']);
   _paq.push(['setSiteId', 'YOUR-SITE-ID']); // Replace this
   ```

3. **Update CSP headers** in `internal/middleware/security.go`:
   ```go
   // Find the CSP policy and update these directives:
   "script-src 'self' 'unsafe-inline' https://YOUR-ANALYTICS-SERVER.COM; " +
   "connect-src 'self' https://YOUR-ANALYTICS-SERVER.COM; "
   ```

4. **Update PRIVACY.md** with your own privacy policy details

### Option 2: Remove Analytics Entirely

If you don't want analytics:

1. **Remove tracking code** from `templates/index.html`:
   - Delete the entire `<!-- Matomo Analytics -->` section (usually at the end of `<body>`)

2. **Simplify CSP headers** in `internal/middleware/security.go`:
   ```go
   // Remove analytics domains from:
   "script-src 'self' 'unsafe-inline'; " +  // Remove analytics domain
   "connect-src 'self'; "  // Remove analytics domain
   ```

3. **Update PRIVACY.md**:
   - Simplify to state no tracking is used
   - Or remove the file if not needed

### Option 3: Use Alternative Analytics Service

If you prefer Google Analytics, Plausible Cloud, or another service:

1. **Replace tracking code** in `templates/index.html` with your provider's script
2. **Update CSP headers** with your provider's domains
3. **Update PRIVACY.md** according to your provider's data handling
4. **Note**: External services may require additional privacy disclosures (GDPR, CCPA)

### Testing Analytics

After configuration:

```bash
# Start development server
make dev

# Visit site in browser
open http://localhost:1999

# Check browser console for errors
# Verify analytics requests in Network tab
```

**Security Note:** Always use HTTPS in production to protect analytics data in transit.

### Privacy Compliance

**Important legal considerations:**

- ✅ Add cookie consent banner if required in your jurisdiction
- ✅ Create privacy policy explaining data collection
- ✅ Provide opt-out mechanism
- ✅ Comply with GDPR, CCPA, or local privacy laws
- ✅ Update privacy policy when changing analytics providers

**See [10-PRIVACY.md](10-PRIVACY.md) for privacy policy template.**

---

## Advanced Customization

### Adding New Languages (Beyond en/es)

**Step 1**: Create JSON file
```bash
cp data/cv-en.json data/cv-fr.json
# Edit cv-fr.json with French content
```

**Step 2**: Update Go model validation (`internal/models/cv.go`)
```go
func LoadCV(lang string) (*CV, error) {
    // Validate language
    if lang != "en" && lang != "es" && lang != "fr" {
        return nil, fmt.Errorf("unsupported language: %s", lang)
    }
    // ... rest of function
}
```

**Step 3**: Add language selector to template
```html
<div class="language-selector">
    <a href="/?lang=en">English</a> |
    <a href="/?lang=es">Español</a> |
    <a href="/?lang=fr">Français</a>
</div>
```

**Step 4**: Update all `{{if eq .Lang "es"}}` conditions:
```html
{{if eq .Lang "es"}}
    Español
{{else if eq .Lang "fr"}}
    Français
{{else}}
    English
{{end}}
```

### Custom PDF Styles

PDF generation uses same templates but renders with Chromium. Customize print styles:

**Add to `static/css/main.css`**:
```css
@media print {
    /* PDF-specific styles */
    body {
        background: white;
    }

    .cv-page {
        box-shadow: none;
        margin: 0;
    }

    /* Custom PDF header/footer */
    @page {
        margin: 20mm;

        @top-center {
            content: "Your Name - CV";
        }

        @bottom-right {
            content: "Page " counter(page) " of " counter(pages);
        }
    }

    /* Prevent orphans/widows */
    p, li {
        orphans: 3;
        widows: 3;
    }
}
```

### Additional Export Formats

#### Adding Word Export

**Step 1**: Install library
```bash
go get github.com/nguyenthenguyen/docx
```

**Step 2**: Create export handler (new file `internal/export/docx.go`)
```go
package export

import (
    "github.com/nguyenthenguyen/docx"
    "your-cv/internal/models"
)

func GenerateDOCX(cv *models.CV, filename string) error {
    doc := docx.NewFile()

    // Add content
    doc.AddHeading(cv.Personal.Name, 1)
    doc.AddParagraph(cv.Summary)

    // Add experience
    for _, exp := range cv.Experience {
        doc.AddHeading(exp.Position + " - " + exp.Company, 2)
        for _, resp := range exp.Responsibilities {
            doc.AddListItem(resp)
        }
    }

    return doc.Save(filename)
}
```

**Step 3**: Add route in `main.go`
```go
http.HandleFunc("/download/docx", func(w http.ResponseWriter, r *http.Request) {
    lang := r.URL.Query().Get("lang")
    cv, _ := models.LoadCV(lang)

    filename := "/tmp/cv.docx"
    export.GenerateDOCX(cv, filename)

    w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
    w.Header().Set("Content-Disposition", "attachment; filename=cv.docx")
    http.ServeFile(w, r, filename)
})
```

### API Endpoints

**Add JSON API** for CV data:

**In `main.go`**:
```go
// JSON API endpoint
http.HandleFunc("/api/cv", func(w http.ResponseWriter, r *http.Request) {
    lang := r.URL.Query().Get("lang")
    if lang == "" {
        lang = "en"
    }

    cv, err := models.LoadCV(lang)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    w.Header().Set("Access-Control-Allow-Origin", "*")  // CORS
    json.NewEncoder(w).Encode(cv)
})
```

**Usage**:
```bash
curl http://localhost:1999/api/cv?lang=en | jq .
```

### Database Integration (Replacing JSON)

**For dynamic CVs** that update frequently:

**Step 1**: Choose database (PostgreSQL example)
```bash
go get github.com/lib/pq
```

**Step 2**: Create database schema
```sql
CREATE TABLE cv_data (
    id SERIAL PRIMARY KEY,
    language VARCHAR(2) NOT NULL,
    content JSONB NOT NULL,
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_language ON cv_data(language);
```

**Step 3**: Update LoadCV function
```go
func LoadCVFromDB(lang string, db *sql.DB) (*CV, error) {
    var content []byte
    err := db.QueryRow(
        "SELECT content FROM cv_data WHERE language = $1 ORDER BY updated_at DESC LIMIT 1",
        lang,
    ).Scan(&content)

    if err != nil {
        return nil, err
    }

    var cv CV
    if err := json.Unmarshal(content, &cv); err != nil {
        return nil, err
    }

    return &cv, nil
}
```

**Step 4**: Add admin panel to update CV through web interface

---

## Testing Your Changes

### Local Testing Workflow

**1. Make changes**
```bash
# Edit JSON
nano data/cv-en.json

# Edit CSS
nano static/css/main.css

# Edit templates
nano templates/cv-content.html
```

**2. Test immediately** (with hot-reload):
```bash
# Start development server
make dev

# Or manually
GO_ENV=development TEMPLATE_HOT_RELOAD=true go run main.go
```

**3. Open browser**
```
http://localhost:1999
http://localhost:1999?lang=es
```

**4. Check for errors**
```bash
# Watch terminal for errors
# Check browser console (F12)
```

### Validating JSON

**Online validators**:
- [JSONLint](https://jsonlint.com/)
- [JSON Formatter](https://jsonformatter.org/)

**Command-line validation**:
```bash
# Using Python
python3 -m json.tool data/cv-en.json

# Using jq
jq . data/cv-en.json

# Should output formatted JSON without errors
```

**Common JSON errors**:
- Missing comma: `"name": "John" "title": "Dev"` ← Need comma
- Trailing comma: `["item1", "item2",]` ← Remove last comma
- Unescaped quotes: `"He said "hello""` ← Use `"He said \"hello\""`
- Wrong brackets: `{...]` ← Mismatched brackets

### Browser Testing Checklist

**Visual checks**:
- [ ] Profile photo displays correctly
- [ ] Company logos appear (or fallback icons)
- [ ] All sections render
- [ ] No overlapping text
- [ ] Colors look correct
- [ ] Links are clickable and work
- [ ] Icons display (Iconify loaded)

**Functionality checks**:
- [ ] Language toggle works (`?lang=en` vs `?lang=es`)
- [ ] Print view looks good (Cmd/Ctrl+P)
- [ ] PDF download works
- [ ] Long/short CV toggle works (if implemented)
- [ ] Responsive on mobile (if implemented)

**Testing commands**:
```bash
# Test health endpoint
curl http://localhost:1999/health

# Test English version
curl http://localhost:1999/?lang=en | head -100

# Test Spanish version
curl http://localhost:1999/?lang=es | head -100

# Test PDF generation
curl http://localhost:1999/download/pdf?lang=en --output test.pdf
open test.pdf
```

### PDF Export Verification

**Check PDF quality**:
```bash
# Generate PDF
curl http://localhost:1999/download/pdf?lang=en --output cv.pdf

# Open and verify
open cv.pdf  # macOS
xdg-open cv.pdf  # Linux
start cv.pdf  # Windows

# Check file size
ls -lh cv.pdf
# Should be ~500KB - 2MB depending on images
```

**PDF checklist**:
- [ ] All content visible (not cut off)
- [ ] Page breaks in correct places
- [ ] Images/logos render correctly
- [ ] Links are clickable (if viewing digitally)
- [ ] Text is selectable (not rasterized)
- [ ] Colors accurate
- [ ] No weird formatting issues

**Debug PDF issues**:
```bash
# If PDF generation fails, check:

# 1. Chromium installed
chromium-browser --version

# 2. Chromium path
which chromium-browser

# 3. Set environment variable if needed
export CHROME_BIN=/usr/bin/chromium-browser
```

---

## Examples

### Example 1: Complete Personal Rebrand

**Goal**: Replace all content with your own

**Steps**:
```bash
# 1. Backup original
cp data/cv-en.json data/cv-en.json.backup

# 2. Edit personal info
nano data/cv-en.json
# Update: personal, summary, experience, education, skills, projects

# 3. Replace photo
cp ~/my-headshot.jpg static/images/profile/dni.jpeg

# 4. Update colors to match your brand
nano static/css/main.css
# Change: --accent-blue to your brand color

# 5. Test
make dev
open http://localhost:1999

# 6. Generate PDF
curl http://localhost:1999/download/pdf?lang=en --output my-cv.pdf
```

### Example 2: Academic CV Style

**Goal**: Convert to academic CV format

**Changes needed**:
1. **Reorder sections**: Education first, then Publications, then Experience
2. **Add Publications section** (follow pattern from "Adding New Sections")
3. **Remove** "Projects" and "Awards" sections
4. **Change styling** to more conservative colors

**CSS changes**:
```css
:root {
    --accent-blue: #2c3e50;  /* Conservative dark blue */
    --bg-gray: #f4f4f4;      /* Light background */
}

body {
    font-family: 'Times New Roman', serif;  /* Traditional font */
}
```

### Example 3: Portfolio Website Integration

**Goal**: Use CV data to populate portfolio website

**Create new template** `templates/portfolio.html`:
```html
<!DOCTYPE html>
<html>
<head>
    <title>{{.CV.Personal.Name}} - Portfolio</title>
    <link rel="stylesheet" href="/static/css/portfolio.css">
</head>
<body>
    <nav>
        <a href="/">Home</a>
        <a href="/projects">Projects</a>
        <a href="/cv">CV</a>
    </nav>

    <header>
        <h1>{{.CV.Personal.Name}}</h1>
        <p>{{.CV.Personal.Title}}</p>
    </header>

    <section class="projects">
        {{range .CV.Projects}}
        <article>
            <h2>{{.Title}}</h2>
            <p>{{.ShortDescription}}</p>
            <a href="{{.URL}}">View Project</a>
        </article>
        {{end}}
    </section>
</body>
</html>
```

**Add route in `main.go`**:
```go
http.HandleFunc("/portfolio", func(w http.ResponseWriter, r *http.Request) {
    // Render portfolio template using same CV data
})
```

### Example 4: Multi-Language Support (Adding French)

**Complete implementation**:

**1. Create French JSON**:
```bash
cp data/cv-en.json data/cv-fr.json
# Translate all content to French
```

**2. Update model validation** (`internal/models/cv.go`):
```go
func LoadCV(lang string) (*CV, error) {
    if lang != "en" && lang != "es" && lang != "fr" {
        return nil, fmt.Errorf("unsupported language: %s", lang)
    }
    // ...
}
```

**3. Update template conditionals** (all instances):
```html
{{if eq .Lang "fr"}}
    Français text
{{else if eq .Lang "es"}}
    Texto en español
{{else}}
    English text
{{end}}
```

**4. Add language selector**:
```html
<div class="lang-selector">
    <a href="/?lang=en" {{if eq .Lang "en"}}class="active"{{end}}>EN</a>
    <a href="/?lang=es" {{if eq .Lang "es"}}class="active"{{end}}>ES</a>
    <a href="/?lang=fr" {{if eq .Lang "fr"}}class="active"{{end}}>FR</a>
</div>
```

---

## Common Customization Patterns

### Pattern 1: Responsive Sidebar

Make sidebars collapse on mobile:

```css
@media screen and (max-width: 768px) {
    .page-content {
        grid-template-columns: 1fr;
    }

    .cv-sidebar {
        display: none;  /* Hide sidebars on mobile */
    }

    /* Or show as accordion */
    .cv-sidebar details {
        margin-bottom: 15px;
    }
}
```

### Pattern 2: Dark Mode Toggle

Add dark mode switch:

**CSS**:
```css
/* Dark mode variables */
[data-theme="dark"] {
    --bg-gray: #1e1e1e;
    --sidebar-gray: #2d2d2d;
    --paper-white: #252526;
    --text-dark: #d4d4d4;
    --text-gray: #9d9d9d;
}
```

**JavaScript** (add to template):
```html
<script>
function toggleDarkMode() {
    const current = document.documentElement.getAttribute('data-theme');
    const next = current === 'dark' ? 'light' : 'dark';
    document.documentElement.setAttribute('data-theme', next);
    localStorage.setItem('theme', next);
}

// Load saved preference
const saved = localStorage.getItem('theme');
if (saved) {
    document.documentElement.setAttribute('data-theme', saved);
}
</script>
```

### Pattern 3: Skills Progress Bars

Add visual skill levels:

**Template** (in skills section):
```html
{{range $category.Items}}
<div class="skill-item">
    <span class="skill-name">{{.}}</span>
    <div class="skill-bar">
        <div class="skill-level" style="width: {{$category.Proficiency}}0%;"></div>
    </div>
</div>
{{end}}
```

**CSS**:
```css
.skill-bar {
    background: #e0e0e0;
    height: 8px;
    border-radius: 4px;
    overflow: hidden;
    margin-top: 4px;
}

.skill-level {
    background: var(--accent-blue);
    height: 100%;
    transition: width 0.3s ease;
}
```

---

## Troubleshooting Customization

### Issue: Changes Not Appearing

**Solutions**:
```bash
# 1. Hard refresh browser
# Press Cmd+Shift+R (Mac) or Ctrl+Shift+R (Windows/Linux)

# 2. Clear browser cache
# Or use private/incognito window

# 3. Restart server if Go code changed
pkill cv-server
make dev

# 4. Check for errors
# Look in terminal and browser console (F12)
```

### Issue: JSON Parse Error

**Solutions**:
```bash
# Validate JSON syntax
python3 -m json.tool data/cv-en.json

# Common fixes:
# - Add missing commas between items
# - Remove trailing commas in arrays/objects
# - Escape quotes in strings: \" instead of "
# - Check matching brackets: { } [ ]
```

### Issue: Template Rendering Error

**Solutions**:
```bash
# Check error message in terminal
# Common issues:
# - Undefined variable: Check spelling, case-sensitivity
# - Wrong field name: Verify against models/cv.go
# - Missing | safeHTML for HTML content
```

### Issue: Styling Not Applied

**Solutions**:
```bash
# 1. Verify CSS file loaded
# Check browser Network tab (F12) for main.css

# 2. Check CSS syntax
# Use browser DevTools to inspect elements

# 3. Check specificity
# Use !important to test: color: red !important;

# 4. Verify class names match
# Template: class="cv-name"
# CSS: .cv-name { ... }
```

---

## Next Steps

After customization:
1. **Test thoroughly** with checklist above
2. **Generate PDF** and verify quality
3. **Deploy** using [8-DEPLOYMENT.md](8-DEPLOYMENT.md) guide
4. **Set up CI/CD** for automatic deployments
5. **Share** your customized CV!

**Further Resources**:
- [Go Templates Documentation](https://pkg.go.dev/text/template)
- [CSS Grid Guide](https://css-tricks.com/snippets/css/complete-guide-grid/)
- [Iconify Icon Sets](https://icon-sets.iconify.design/)
- [Google Fonts](https://fonts.google.com/)

**Need Help?**
- Check existing issues on GitHub
- Open new issue with details
- Include error messages and screenshots

Happy customizing! 🎨
