diff --git a/Makefile b/Makefile index cc24347..1976839 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: test test-all test-unit test-integration lint build dev run clean css-dev css-prod css-watch css-clean +.PHONY: test test-all test-unit test-integration lint build dev run clean css-dev css-prod css-watch css-clean sprites sprites-clean # Default: Run unit tests only (fast, no Chrome needed) test: test-unit @@ -82,3 +82,19 @@ css-clean: @echo "🧹 Cleaning generated CSS..." rm -rf static/dist @echo "✅ Cleaned static/dist/" + +# ============================================================================ +# Sprite Generation Targets +# ============================================================================ + +# Generate CSS sprites from source images +sprites: + @echo "🖼️ Generating CSS sprites..." + @go build -o sprites ./cmd/sprites && ./sprites && rm -f sprites + @echo "✅ Sprites generated successfully!" + +# Clean generated sprite files +sprites-clean: + @echo "🧹 Cleaning generated sprites..." + rm -rf static/images/sprites/*.png static/images/sprites/sprite-map.json static/sprite-showcase.html + @echo "✅ Cleaned sprite files" diff --git a/cmd/sprites/main.go b/cmd/sprites/main.go new file mode 100644 index 0000000..fac0c11 --- /dev/null +++ b/cmd/sprites/main.go @@ -0,0 +1,525 @@ +// Package main provides a sprite generator tool for the CV website. +// It processes PNG images from source directories, normalizes them to standard +// icon sizes (80x80 for 1x, 160x160 for 2x), and combines them into horizontal +// sprite sheets. +package main + +import ( + "encoding/json" + "fmt" + "image" + "image/color" + "image/png" + "os" + "path/filepath" + "sort" + "strings" + + "golang.org/x/image/draw" + "golang.org/x/text/cases" + "golang.org/x/text/language" +) + +// SpriteCategory defines a category of icons to process +type SpriteCategory struct { + Name string // Category name (companies, projects, courses) + SourceDir string // Source directory for images + OutputName string // Output sprite filename (without extension) + Icons []string // List of icon filenames (populated during processing) +} + +// SpriteMapEntry represents a single icon in the sprite map +type SpriteMapEntry struct { + Index int `json:"index"` + Name string `json:"name"` +} + +// SpriteMap represents the complete mapping of icons to positions +type SpriteMap struct { + Companies []SpriteMapEntry `json:"companies"` + Projects []SpriteMapEntry `json:"projects"` + Courses []SpriteMapEntry `json:"courses"` +} + +// ShowcaseIcon represents an icon for the showcase page +type ShowcaseIcon struct { + Index int + Name string +} + +// ShowcaseCategory represents a category for the showcase page +type ShowcaseCategory struct { + Name string + CSSClass string + SpriteFile string + Icons []ShowcaseIcon +} + +const ( + baseIconSize = 60 // Base icon size (1x) - fits within 80px box with 10px padding + retinaIconSize = 120 // Retina icon size (2x) + staticDir = "static/images" + spritesDir = "static/images/sprites" +) + +func main() { + fmt.Println("CSS Sprite Generator for CV Website") + fmt.Println("====================================") + fmt.Println() + + // Define categories + categories := []SpriteCategory{ + {Name: "companies", SourceDir: filepath.Join(staticDir, "companies"), OutputName: "sprite-companies"}, + {Name: "projects", SourceDir: filepath.Join(staticDir, "projects"), OutputName: "sprite-projects"}, + {Name: "courses", SourceDir: filepath.Join(staticDir, "courses"), OutputName: "sprite-courses"}, + } + + // Process each category + spriteMap := SpriteMap{} + var showcaseCategories []ShowcaseCategory + + for i := range categories { + cat := &categories[i] + fmt.Printf("Processing %s...\n", cat.Name) + + // Scan source directory for PNG files + icons, err := scanDirectory(cat.SourceDir) + if err != nil { + fmt.Printf(" ERROR: Failed to scan %s: %v\n", cat.SourceDir, err) + continue + } + + cat.Icons = icons + fmt.Printf(" Found %d icons\n", len(icons)) + + if len(icons) == 0 { + fmt.Printf(" Skipping (no icons found)\n") + continue + } + + // Generate sprite sheets (1x and 2x) + err = generateSprite(cat, baseIconSize, "") + if err != nil { + fmt.Printf(" ERROR: Failed to generate 1x sprite: %v\n", err) + continue + } + + err = generateSprite(cat, retinaIconSize, "@2x") + if err != nil { + fmt.Printf(" ERROR: Failed to generate 2x sprite: %v\n", err) + continue + } + + // Build sprite map entry + entries := make([]SpriteMapEntry, len(icons)) + showcaseIcons := make([]ShowcaseIcon, len(icons)) + for idx, icon := range icons { + entries[idx] = SpriteMapEntry{Index: idx, Name: icon} + showcaseIcons[idx] = ShowcaseIcon{Index: idx, Name: strings.TrimSuffix(icon, filepath.Ext(icon))} + } + + switch cat.Name { + case "companies": + spriteMap.Companies = entries + case "projects": + spriteMap.Projects = entries + case "courses": + spriteMap.Courses = entries + } + + // Build showcase category + showcaseCategories = append(showcaseCategories, ShowcaseCategory{ + Name: cat.Name, + CSSClass: "icon-" + strings.TrimSuffix(cat.Name, "s"), // companies -> icon-company + SpriteFile: cat.OutputName + ".png", + Icons: showcaseIcons, + }) + + fmt.Printf(" Generated: %s.png and %s@2x.png\n", cat.OutputName, cat.OutputName) + } + + // Write sprite map JSON + err := writeSpriteMap(spriteMap) + if err != nil { + fmt.Printf("\nERROR: Failed to write sprite-map.json: %v\n", err) + os.Exit(1) + } + fmt.Println("\nGenerated: sprite-map.json") + + // Generate showcase HTML page + err = generateShowcasePage(showcaseCategories) + if err != nil { + fmt.Printf("\nERROR: Failed to generate showcase page: %v\n", err) + os.Exit(1) + } + fmt.Println("Generated: sprite-showcase.html") + + // Print summary + fmt.Println("\n====================================") + fmt.Println("Sprite generation complete!") + fmt.Printf(" Companies: %d icons\n", len(spriteMap.Companies)) + fmt.Printf(" Projects: %d icons\n", len(spriteMap.Projects)) + fmt.Printf(" Courses: %d icons\n", len(spriteMap.Courses)) + fmt.Printf(" Total: %d icons\n", len(spriteMap.Companies)+len(spriteMap.Projects)+len(spriteMap.Courses)) + fmt.Println("\nOutput files:") + fmt.Println(" - static/images/sprites/sprite-companies.png") + fmt.Println(" - static/images/sprites/sprite-companies@2x.png") + fmt.Println(" - static/images/sprites/sprite-projects.png") + fmt.Println(" - static/images/sprites/sprite-projects@2x.png") + fmt.Println(" - static/images/sprites/sprite-courses.png") + fmt.Println(" - static/images/sprites/sprite-courses@2x.png") + fmt.Println(" - static/images/sprites/sprite-map.json") + fmt.Println(" - static/sprite-showcase.html") +} + +// scanDirectory returns a sorted list of PNG files in the directory +func scanDirectory(dir string) ([]string, error) { + entries, err := os.ReadDir(dir) + if err != nil { + return nil, err + } + + var pngs []string + for _, entry := range entries { + if entry.IsDir() { + continue + } + name := entry.Name() + if strings.HasSuffix(strings.ToLower(name), ".png") { + pngs = append(pngs, name) + } + } + + // Sort alphabetically for consistent ordering + sort.Strings(pngs) + return pngs, nil +} + +// generateSprite creates a sprite sheet for the given category +func generateSprite(cat *SpriteCategory, iconSize int, suffix string) error { + if len(cat.Icons) == 0 { + return nil + } + + // Create sprite image (horizontal strip) + spriteWidth := iconSize * len(cat.Icons) + spriteHeight := iconSize + sprite := image.NewRGBA(image.Rect(0, 0, spriteWidth, spriteHeight)) + + // Process each icon + for idx, iconName := range cat.Icons { + srcPath := filepath.Join(cat.SourceDir, iconName) + + // Load source image + srcImg, err := loadImage(srcPath) + if err != nil { + fmt.Printf(" WARNING: Failed to load %s: %v\n", iconName, err) + continue + } + + // Resize and center icon + resized := resizeAndCenter(srcImg, iconSize) + + // Draw onto sprite at correct position + xOffset := idx * iconSize + destRect := image.Rect(xOffset, 0, xOffset+iconSize, iconSize) + draw.Draw(sprite, destRect, resized, image.Point{0, 0}, draw.Over) + } + + // Save sprite + outputPath := filepath.Join(spritesDir, cat.OutputName+suffix+".png") + return saveImage(sprite, outputPath) +} + +// loadImage loads a PNG image from the given path +func loadImage(path string) (image.Image, error) { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + img, err := png.Decode(file) + if err != nil { + return nil, err + } + + return img, nil +} + +// resizeAndCenter resizes an image to fit within the target size while maintaining +// aspect ratio, then centers it on a transparent background +func resizeAndCenter(src image.Image, targetSize int) *image.RGBA { + // Create transparent target image + dst := image.NewRGBA(image.Rect(0, 0, targetSize, targetSize)) + + // Fill with transparent background + for y := 0; y < targetSize; y++ { + for x := 0; x < targetSize; x++ { + dst.Set(x, y, color.Transparent) + } + } + + // Get source dimensions + srcBounds := src.Bounds() + srcWidth := srcBounds.Dx() + srcHeight := srcBounds.Dy() + + // Calculate scaling factor to fit within target while maintaining aspect ratio + scaleX := float64(targetSize) / float64(srcWidth) + scaleY := float64(targetSize) / float64(srcHeight) + scale := scaleX + if scaleY < scaleX { + scale = scaleY + } + + // Calculate new dimensions + newWidth := int(float64(srcWidth) * scale) + newHeight := int(float64(srcHeight) * scale) + + // Calculate offset to center + offsetX := (targetSize - newWidth) / 2 + offsetY := (targetSize - newHeight) / 2 + + // Create scaled image + scaled := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight)) + + // Use high-quality scaling (CatmullRom for smooth results) + draw.CatmullRom.Scale(scaled, scaled.Bounds(), src, srcBounds, draw.Over, nil) + + // Draw scaled image onto destination at centered position + destRect := image.Rect(offsetX, offsetY, offsetX+newWidth, offsetY+newHeight) + draw.Draw(dst, destRect, scaled, image.Point{0, 0}, draw.Over) + + return dst +} + +// saveImage saves an image to the given path as PNG +func saveImage(img image.Image, path string) error { + // Ensure directory exists + dir := filepath.Dir(path) + if err := os.MkdirAll(dir, 0755); err != nil { + return err + } + + file, err := os.Create(path) + if err != nil { + return err + } + defer file.Close() + + return png.Encode(file, img) +} + +// writeSpriteMap writes the sprite map to a JSON file +func writeSpriteMap(spriteMap SpriteMap) error { + data, err := json.MarshalIndent(spriteMap, "", " ") + if err != nil { + return err + } + + outputPath := filepath.Join(spritesDir, "sprite-map.json") + return os.WriteFile(outputPath, data, 0644) +} + +// generateShowcasePage creates an HTML showcase page for visual QA +func generateShowcasePage(categories []ShowcaseCategory) error { + html := ` + + + + + CSS Sprite Showcase + + + + +

CSS Sprite Showcase

+ +
+ Summary: + \n
\n\n" + + // Add each category + for _, cat := range categories { + html += fmt.Sprintf(`
+

%s (Full Sprite)

+
+ %s sprite +
+ +

Individual Icons

+
+`, titleCaser.String(cat.Name), cat.SpriteFile, cat.Name) + + for _, icon := range cat.Icons { + html += fmt.Sprintf(`
+ + +
+`, cat.CSSClass, icon.Index, icon.Index, icon.Name) + } + + html += "
\n
\n\n" + } + + // Add zoom test section + html += `
+

Zoom Test

+
+
100%:
+
200%:
+
300%:
+
+
+ +
+

Retina Test

+

On retina displays, the @2x sprite should load automatically for crisp rendering.

+
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+

Network Verification

+

Open DevTools (Network tab, filter by Images) to verify:

+ +
+ + +` + + outputPath := "static/sprite-showcase.html" + return os.WriteFile(outputPath, []byte(html), 0644) +} diff --git a/data/cv-en.json b/data/cv-en.json index 884fe9e..e2c3306 100644 --- a/data/cv-en.json +++ b/data/cv-en.json @@ -55,6 +55,7 @@ "API Integration" ], "companyLogo": "olympic-broadcasting.png", + "logoIndex": 15, "shortDescription": "SAP CDC solutions for international broadcasting events. Custom implementations and technical guidance.", "companyID": "olympic-broadcasting" }, @@ -83,6 +84,7 @@ "Authentication Systems" ], "companyLogo": "livgolf.png", + "logoIndex": 13, "shortDescription": "Technical consulting for SAP CDC implementation. Created authorization screens, backend endpoints, and comprehensive documentation.", "companyID": "livgolf" }, @@ -116,6 +118,7 @@ "Managed identity flows for millions of users across web and mobile platforms" ], "companyLogo": "aena.png", + "logoIndex": 2, "shortDescription": "Lead Technical Consultant for AENA Airports Authentication System serving millions of passengers across all Spanish airports.", "companyID": "aena" }, @@ -143,6 +146,7 @@ "Technical Documentation" ], "companyLogo": "sap.png", + "logoIndex": 18, "shortDescription": "SAP Customer Data Cloud technical consulting, troubleshooting, and stakeholder education on GDPR compliance.", "companyID": "sap" }, @@ -169,6 +173,7 @@ "System Monitoring" ], "companyLogo": "gigya.png", + "logoIndex": 10, "shortDescription": "Technical support and problem-solving for Gigya platform. System monitoring and training program development.", "companyID": "gigya" }, @@ -201,6 +206,7 @@ "DevOps" ], "companyLogo": "drosoloft-plain.png", + "logoIndex": 6, "shortDescription": "Freelance work for multiple clients (Megabanner, Ebantic, Everis, Indra) developing React applications, designing APIs, integrating video systems and managing projects.", "companyID": "drosoloft" }, @@ -230,6 +236,7 @@ "Successfully managed technical team and product development" ], "companyLogo": "emailing-network.png", + "logoIndex": 8, "shortDescription": "Technical Director leading development of backend and 5 websites. Reduced production times by 75%.", "companyID": "emailing-network" }, @@ -252,6 +259,7 @@ "JavaScript" ], "companyLogo": "twentic.png", + "logoIndex": 19, "shortDescription": "WordPress and PHP website development as freelance programmer.", "companyID": "twentic" }, @@ -260,6 +268,7 @@ "company": "Penta MSI", "companyURL": "http://pentamsi.com/", "companyLogo": "pentamsi.png", + "logoIndex": 17, "expired": true, "location": "Barcelona, Spain", "startDate": "2010-10", @@ -283,6 +292,7 @@ "company": "Homeria + WebRatio S.R.L.", "companyURL": "http://webratio.com/", "companyLogo": "webratio.png", + "logoIndex": 21, "location": "Cáceres (Spain) / Como (Italy)", "startDate": "2008-01", "endDate": "2008-12", @@ -305,6 +315,7 @@ "company": "Insa", "companyURL": "http://insags.com/", "companyLogo": "insa.png", + "logoIndex": 12, "expired": true, "location": "Cáceres, Spain", "startDate": "2006-09", @@ -549,6 +560,7 @@ "projectDesc": "Beach Cleaning Initiative", "url": "https://somosunaola.org", "projectLogo": "somosunaola.png", + "logoIndex": 10, "location": "La Palma, Canary Islands", "startDate": "2023-07", "current": true, @@ -571,6 +583,7 @@ "projectDesc": "Artist Portfolio Website", "url": "https://herrumbrevivoarte.com", "projectLogo": "herrumbre-vivo.png", + "logoIndex": 2, "location": "Fuencaliente, La Palma", "startDate": "2024", "current": true, @@ -592,6 +605,7 @@ "projectDesc": "Football Prediction Platform", "url": "https://laporra.club", "projectLogo": "laporra.png", + "logoIndex": 5, "gitRepoUrl": "", "location": "Online", "current": true, @@ -617,6 +631,7 @@ "projectDesc": "SAP Customer Data Cloud Demo", "url": "https://gigyademo.com/cdc-starter-kit/", "projectLogo": "sap.png", + "logoIndex": 8, "location": "Online", "startDate": "2018", "current": true, @@ -726,6 +741,7 @@ "title": "Codecademy Certifications", "institution": "Codecademy", "courseLogo": "codecademy.png", + "logoIndex": 1, "location": "Online", "date": "2022-2024", "duration": "Various", @@ -740,6 +756,7 @@ "title": "Udemy Certifications", "institution": "Udemy", "courseLogo": "udemy.png", + "logoIndex": 7, "location": "Online", "date": "2024-2025", "duration": "Various", @@ -757,6 +774,7 @@ "title": "LinkedIn Learning Certifications", "institution": "LinkedIn Learning", "courseLogo": "linkedin.png", + "logoIndex": 4, "location": "Online", "date": "2019-2020", "duration": "Various", @@ -774,6 +792,7 @@ "title": "Servoy World 2011", "institution": "Servoy", "courseLogo": "servoy.png", + "logoIndex": 6, "location": "Amsterdam", "date": "2011-02", "duration": "3 days", @@ -789,6 +808,7 @@ "title": "Train the Trainers", "institution": "FOREM Extremadura", "courseLogo": "forem.png", + "logoIndex": 2, "location": "Cáceres", "date": "2009-06", "duration": "150 hours", @@ -804,6 +824,7 @@ "title": "Windows 2003 Server", "institution": "Cáceres Chamber of Commerce", "courseLogo": "camaracomercio.png", + "logoIndex": 0, "location": "Cáceres", "date": "2006-01", "duration": "80 hours", @@ -819,6 +840,7 @@ "title": "1st Extremadura Conference on Software Industry", "institution": "University of Extremadura", "courseLogo": "uex.png", + "logoIndex": 8, "location": "Cáceres", "date": "2005-07", "duration": "3 days", @@ -834,6 +856,7 @@ "title": "Web Application Development: Apache, PHP and MySQL", "institution": "University of Extremadura", "courseLogo": "uex.png", + "logoIndex": 8, "location": "Cáceres", "date": "2002", "duration": "40 hours", diff --git a/data/cv-es.json b/data/cv-es.json index bf5bfae..0b71b01 100644 --- a/data/cv-es.json +++ b/data/cv-es.json @@ -55,6 +55,7 @@ "Integración de APIs" ], "companyLogo": "olympic-broadcasting.png", + "logoIndex": 15, "shortDescription": "Soluciones SAP CDC para eventos de transmisión internacional. Implementaciones personalizadas y orientación técnica.", "companyID": "olympic-broadcasting" }, @@ -83,6 +84,7 @@ "Sistemas de Autenticación" ], "companyLogo": "livgolf.png", + "logoIndex": 13, "shortDescription": "Consultoría técnica para implementación SAP CDC. Creación de pantallas de autorización, endpoints backend y documentación completa.", "companyID": "livgolf" }, @@ -116,6 +118,7 @@ "Gestión de flujos de identidad para millones de usuarios en plataformas web y móviles" ], "companyLogo": "aena.png", + "logoIndex": 2, "shortDescription": "Consultor Técnico Principal del Sistema de Autenticación de Aeropuertos AENA sirviendo a millones de pasajeros en todos los aeropuertos españoles.", "companyID": "aena" }, @@ -143,6 +146,7 @@ "Documentación Técnica" ], "companyLogo": "sap.png", + "logoIndex": 18, "shortDescription": "Consultoría técnica SAP Customer Data Cloud, resolución de problemas y educación de stakeholders en cumplimiento GDPR.", "companyID": "sap" }, @@ -169,6 +173,7 @@ "Monitoreo de Sistemas" ], "companyLogo": "gigya.png", + "logoIndex": 10, "shortDescription": "Soporte técnico y resolución de problemas para plataforma Gigya. Monitoreo de sistemas y desarrollo de programas de formación.", "companyID": "gigya" }, @@ -201,6 +206,7 @@ "DevOps" ], "companyLogo": "drosoloft-plain.png", + "logoIndex": 6, "shortDescription": "Trabajo freelance para múltiples clientes (Megabanner, Ebantic, Everis, Indra) desarrollando aplicaciones React, diseñando APIs, integrando sistemas de video y gestionando proyectos.", "companyID": "drosoloft" }, @@ -230,6 +236,7 @@ "Gestión exitosa de equipo técnico y desarrollo de productos" ], "companyLogo": "emailing-network.png", + "logoIndex": 8, "shortDescription": "Director Técnico liderando desarrollo de backend y 5 sitios web. Reducción del 75% en tiempos de producción.", "companyID": "emailing-network" }, @@ -252,6 +259,7 @@ "JavaScript" ], "companyLogo": "twentic.png", + "logoIndex": 19, "shortDescription": "Desarrollo de sitios web WordPress y PHP como programador freelance.", "companyID": "twentic" }, @@ -260,6 +268,7 @@ "company": "Penta MSI", "companyURL": "http://pentamsi.com/", "companyLogo": "pentamsi.png", + "logoIndex": 17, "expired": true, "location": "Barcelona, España", "startDate": "2010-10", @@ -283,6 +292,7 @@ "company": "Homeria + WebRatio S.R.L.", "companyURL": "http://webratio.com/", "companyLogo": "webratio.png", + "logoIndex": 21, "location": "Cáceres (España) / Como (Italia)", "startDate": "2008-01", "endDate": "2008-12", @@ -305,6 +315,7 @@ "company": "Insa", "companyURL": "http://insags.com/", "companyLogo": "insa.png", + "logoIndex": 12, "expired": true, "location": "Cáceres, España", "startDate": "2006-09", @@ -554,6 +565,7 @@ "projectDesc": "Iniciativa de Limpieza de Playas", "url": "https://somosunaola.org", "projectLogo": "somosunaola.png", + "logoIndex": 10, "location": "La Palma, Islas Canarias", "startDate": "2023-07", "current": true, @@ -576,6 +588,7 @@ "projectDesc": "Sitio Web Portfolio de Artista", "url": "https://herrumbrevivoarte.com", "projectLogo": "herrumbre-vivo.png", + "logoIndex": 2, "location": "Fuencaliente, La Palma", "startDate": "2024", "current": true, @@ -597,6 +610,7 @@ "projectDesc": "Plataforma de Predicción de Fútbol", "url": "https://laporra.club", "projectLogo": "laporra.png", + "logoIndex": 5, "gitRepoUrl": "", "location": "Online", "current": true, @@ -622,6 +636,7 @@ "projectDesc": "Demo de SAP Customer Data Cloud", "url": "https://gigyademo.com/cdc-starter-kit/", "projectLogo": "sap.png", + "logoIndex": 8, "location": "Online", "startDate": "2018", "current": true, @@ -731,6 +746,7 @@ "title": "Certificaciones Codecademy", "institution": "Codecademy", "courseLogo": "codecademy.png", + "logoIndex": 1, "location": "Online", "date": "2022-2024", "duration": "Varios", @@ -745,6 +761,7 @@ "title": "Certificaciones Udemy", "institution": "Udemy", "courseLogo": "udemy.png", + "logoIndex": 7, "location": "Online", "date": "2024-2025", "duration": "Varios", @@ -762,6 +779,7 @@ "title": "Certificaciones LinkedIn Learning", "institution": "LinkedIn Learning", "courseLogo": "linkedin.png", + "logoIndex": 4, "location": "Online", "date": "2019-2020", "duration": "Varios", @@ -779,6 +797,7 @@ "title": "Servoy World 2011", "institution": "Servoy", "courseLogo": "servoy.png", + "logoIndex": 6, "location": "Amsterdam", "date": "2011-02", "duration": "3 días", @@ -794,6 +813,7 @@ "title": "Formador de Formadores", "institution": "FOREM Extremadura", "courseLogo": "forem.png", + "logoIndex": 2, "location": "Cáceres", "date": "2009-06", "duration": "150 horas", @@ -809,6 +829,7 @@ "title": "Windows 2003 Server", "institution": "Cámara de Comercio de Cáceres", "courseLogo": "camaracomercio.png", + "logoIndex": 0, "location": "Cáceres", "date": "2006-01", "duration": "80 horas", @@ -824,6 +845,7 @@ "title": "I Jornada Extremeña sobre la Industria del Software", "institution": "Universidad de Extremadura", "courseLogo": "uex.png", + "logoIndex": 8, "location": "Cáceres", "date": "2005-07", "duration": "3 días", @@ -839,6 +861,7 @@ "title": "Desarrollo de aplicaciones Web: Apache, PHP y MySQL", "institution": "Universidad de Extremadura", "courseLogo": "uex.png", + "logoIndex": 8, "location": "Cáceres", "date": "2002", "duration": "40 horas", diff --git a/doc/2-MODERN-WEB-TECHNIQUES.md b/doc/2-MODERN-WEB-TECHNIQUES.md index d3b3d11..8362732 100644 --- a/doc/2-MODERN-WEB-TECHNIQUES.md +++ b/doc/2-MODERN-WEB-TECHNIQUES.md @@ -3749,4 +3749,105 @@ if elapsed < 2000 { // Less than 2 seconds --- +### 16. CSS Sprites - Image Request Optimization + +**Problem:** The CV page loads 44+ individual image files for company, project, and course logos. Each file requires a separate HTTP request, adding latency and overhead. + +**Solution:** CSS sprites combine all icons into horizontal strips, dramatically reducing HTTP requests from 44+ to just 3 (6 including retina versions). + +#### Architecture + +``` +Source: Generated: +static/images/ static/images/sprites/ +├── companies/ ├── sprite-companies.png (23 icons) +│ ├── olympic-broadcasting.png ├── sprite-companies@2x.png (retina) +│ ├── sap.png ├── sprite-projects.png (12 icons) +│ └── ... (23 files) ├── sprite-projects@2x.png (retina) +├── projects/ ├── sprite-courses.png (9 icons) +│ └── ... (12 files) ├── sprite-courses@2x.png (retina) +└── courses/ └── sprite-map.json (positions) + └── ... (9 files) +``` + +#### Go Sprite Generator + +A custom Go tool (`cmd/sprites/main.go`) handles: +- **Automatic normalization**: Any size image → 48x48px (1x) or 96x96px (2x) +- **Aspect ratio preservation**: Icons are centered on transparent background +- **High-quality scaling**: Uses CatmullRom interpolation for smooth results +- **Sprite map generation**: JSON file documenting icon positions + +#### CSS Implementation + +```css +.icon-sprite { + display: inline-block; + width: 48px; + height: 48px; + background-repeat: no-repeat; + background-size: auto 48px; +} + +.icon-company { + background-image: url('/static/images/sprites/sprite-companies.png'); + background-position-x: calc(var(--icon-index, 0) * -48px); +} + +/* Retina displays - automatic @2x sprite loading */ +@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + .icon-company { + background-image: url('/static/images/sprites/sprite-companies@2x.png'); + background-size: auto 48px; /* Display at 1x size */ + } +} +``` + +#### Template Integration + +```html +{{if .LogoIndex}} + +{{else if .CompanyLogo}} +{{.Company}} logo +{{end}} +``` + +#### Performance Impact + +| Metric | Before | After | Improvement | +|--------|--------|-------|-------------| +| Image Requests | 44+ | 3-6 | ~93% reduction | +| Total Image Size | Variable | Optimized | Single cache entry per category | +| HTTP Overhead | 44 round-trips | 3-6 round-trips | Dramatic reduction | + +#### Benefits + +1. **Reduced HTTP Requests**: ~93% reduction in image requests +2. **Simplified Caching**: Single cache invalidation per sprite category +3. **Retina Support**: Automatic @2x sprites for high-DPI displays +4. **Automatic Processing**: Drop any size image → automatic normalization +5. **Zoom Compatible**: Works perfectly at 100%, 200%, and 300% zoom levels +6. **Backward Compatible**: Falls back to individual images if logoIndex not set + +#### Usage + +```bash +# Generate sprites +make sprites + +# Clean generated files +make sprites-clean + +# Visual QA +open http://localhost:1999/static/sprite-showcase.html +``` + +See `doc/22-SPRITES.md` for complete documentation. + +--- + *This document serves as both a technical reference and a demonstration of modern web development practices that prioritize web standards, performance, progressive enhancement, AI-era SEO, and superior user experience over JavaScript-heavy solutions.* diff --git a/doc/22-SPRITES.md b/doc/22-SPRITES.md new file mode 100644 index 0000000..05a9e18 --- /dev/null +++ b/doc/22-SPRITES.md @@ -0,0 +1,207 @@ +# CSS Sprites - Image Request Optimization + +## Overview + +The CV website uses CSS sprites to dramatically reduce HTTP requests for company, project, and course logos. Instead of loading 44+ individual image files, we load only 3 sprite sheets (6 files total including retina versions). + +## Performance Impact + +| Metric | Before | After | Improvement | +|--------|--------|-------|-------------| +| Image Requests | 44+ | 3-6 | ~93% reduction | +| Cache Invalidation | Per image | Per sprite | Simplified | +| HTTP Overhead | 44 round-trips | 3-6 round-trips | Dramatic reduction | + +## Architecture + +### File Structure + +``` +static/ +├── images/ +│ ├── companies/ # Source images (any size) +│ ├── projects/ # Source images (any size) +│ ├── courses/ # Source images (any size) +│ └── sprites/ # Generated sprites +│ ├── sprite-companies.png +│ ├── sprite-companies@2x.png +│ ├── sprite-projects.png +│ ├── sprite-projects@2x.png +│ ├── sprite-courses.png +│ ├── sprite-courses@2x.png +│ └── sprite-map.json +├── sprite-showcase.html # Visual QA page +└── css/ + └── 04-interactive/ + └── _sprites.css # Sprite CSS classes +``` + +### Go Sprite Generator Tool + +Located at `cmd/sprites/main.go`, this tool: + +1. **Scans source directories** for PNG images +2. **Normalizes images** to standard sizes (60x60px for 1x, 120x120px for 2x) +3. **Maintains aspect ratio** and centers on transparent background +4. **Combines into horizontal strips** for each category +5. **Generates sprite-map.json** for documentation +6. **Creates sprite-showcase.html** for visual QA + +### Image Size Standards + +- **Base size**: 60x60px (optimal for 80px display box with 10px padding) +- **Retina size**: 120x120px (@2x for high-DPI displays) +- **Section display**: 80x80px box (60px icon + 10px padding each side) + +## Usage + +### Makefile Targets + +```bash +# Generate sprites from source images +make sprites + +# Clean generated sprite files +make sprites-clean +``` + +### JSON Data Structure + +Add `logoIndex` to entries in cv-en.json and cv-es.json: + +```json +{ + "company": "Olympic Broadcasting Services", + "companyLogo": "olympic-broadcasting.png", + "logoIndex": 15 +} +``` + +**Important**: Only add `logoIndex` when there's an actual PNG file. Entries without a logo file should not have `logoIndex`. + +### Template Integration + +Templates automatically use sprites when `logoIndex` is present: + +```html +{{if .LogoIndex}} + +{{else if .CompanyLogo}} +{{.Company}} logo +{{else}} + +{{end}} +``` + +### CSS Classes + +```css +/* Base sprite class */ +.icon-sprite { + display: inline-block; + width: 50px; + height: 50px; + background-repeat: no-repeat; + background-size: auto 50px; +} + +/* Category-specific classes */ +.icon-company { background-image: url('/static/images/sprites/sprite-companies.png'); } +.icon-project { background-image: url('/static/images/sprites/sprite-projects.png'); } +.icon-course { background-image: url('/static/images/sprites/sprite-courses.png'); } + +/* Size variants */ +.icon-sprite.icon-section { + width: 80px; + height: 80px; + padding: 10px; + background-size: auto 60px; + background-origin: content-box; + background-clip: content-box; +} +.icon-sprite.icon-small { width: 32px; height: 32px; } +.icon-sprite.icon-large { width: 64px; height: 64px; } +``` + +## Adding New Icons + +1. **Drop source image** into appropriate directory: + - `static/images/companies/` for company logos + - `static/images/projects/` for project logos + - `static/images/courses/` for course logos + +2. **Run sprite generation**: + ```bash + make sprites + ``` + +3. **Update JSON files** with new `logoIndex` based on sprite-map.json + +4. **Verify** in showcase page at `/static/sprite-showcase.html` + +## Retina Display Support + +The CSS automatically loads @2x sprites on retina displays: + +```css +@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + .icon-company { + background-image: url('/static/images/sprites/sprite-companies@2x.png'); + background-size: auto 60px; /* Display at 1x size */ + } +} +``` + +## Sprite Map JSON + +The `sprite-map.json` file documents icon positions: + +```json +{ + "companies": [ + {"index": 0, "name": "accenture.png"}, + {"index": 1, "name": "aena-long.png"}, + ... + ], + "projects": [...], + "courses": [...] +} +``` + +This file is for documentation/debugging only - CSS calculates offset from index using `calc(var(--icon-index) * -60px)`. + +## Verification + +### Showcase Page + +Visit `/static/sprite-showcase.html` to: +- View full sprite sheets +- See all individual icons with index labels +- Test zoom levels (100%, 200%, 300%) +- Verify retina rendering + +### Network Verification + +In browser DevTools (Network tab, filter Images): +- **Should see**: sprite-companies.png, sprite-projects.png, sprite-courses.png +- **Should NOT see**: individual logo files (unless fallback triggers) + +## Troubleshooting + +### Invalid PNG Warning + +If you see "png: invalid format: not a PNG file", the source file is not a valid PNG. Check the file with `file ` to verify format. + +### Icon Not Displaying + +1. Verify `logoIndex` is present in JSON +2. Check sprite-map.json for correct index +3. Verify CSS is loaded +4. Check browser console for errors + +### Wrong Icon Displayed + +Verify the `logoIndex` value matches the icon's position in sprite-map.json (0-indexed). diff --git a/go.mod b/go.mod index f5cd35c..e3842d1 100644 --- a/go.mod +++ b/go.mod @@ -7,17 +7,14 @@ require ( github.com/chromedp/chromedp v0.14.2 github.com/go-git/go-git/v5 v5.16.4 github.com/joho/godotenv v1.5.1 + golang.org/x/image v0.33.0 + golang.org/x/text v0.31.0 ) require ( dario.cat/mergo v1.0.0 // indirect - github.com/Masterminds/semver v1.4.2 // indirect - github.com/Masterminds/sprig v2.16.0+incompatible // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.1.6 // indirect - github.com/PuerkitoBio/goquery v1.5.0 // indirect - github.com/andybalholm/cascadia v1.0.0 // indirect - github.com/aokoli/goutils v1.0.1 // indirect github.com/chromedp/sysutil v1.1.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect github.com/cyphar/filepath-securejoin v0.4.1 // indirect @@ -29,24 +26,11 @@ require ( github.com/gobwas/pool v0.2.1 // indirect github.com/gobwas/ws v1.4.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect - github.com/google/uuid v1.0.0 // indirect - github.com/gorilla/css v1.0.0 // indirect - github.com/huandu/xstrings v1.2.0 // indirect - github.com/imdario/mergo v0.3.6 // indirect - github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/matcornic/hermes/v2 v2.1.0 // indirect - github.com/mattn/go-runewidth v0.0.3 // indirect - github.com/olekukonko/tablewriter v0.0.1 // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect - github.com/russross/blackfriday/v2 v2.0.1 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect - github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/skeema/knownhosts v1.3.1 // indirect - github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect - github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04 // indirect - github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect golang.org/x/crypto v0.37.0 // indirect golang.org/x/net v0.39.0 // indirect diff --git a/go.sum b/go.sum index 8639455..d45b7c5 100644 --- a/go.sum +++ b/go.sum @@ -1,22 +1,12 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= -github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/sprig v2.16.0+incompatible h1:QZbMUPxRQ50EKAq3LFMnxddMu88/EUUG3qmxwtDmPsY= -github.com/Masterminds/sprig v2.16.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk= -github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg= -github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o= -github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/aokoli/goutils v1.0.1 h1:7fpzNGoJ3VA8qcrm++XEE1QUe0mIwNeLa02Nwq7RDkg= -github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/chromedp/cdproto v0.0.0-20250724212937-08a3db8b4327 h1:UQ4AU+BGti3Sy/aLU8KVseYKNALcX9UXY6DfpwQ6J8E= @@ -46,7 +36,6 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.16.4 h1:7ajIEZHZJULcyJebDLo99bGgS0jRrOxzZG4uCk2Yb2Y= github.com/go-git/go-git/v5 v5.16.4/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= -github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df/go.mod h1:GJr+FCSXshIwgHBtLglIg9M2l2kQSi6QjVAngtzI08Y= github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 h1:iizUGZ9pEquQS5jTGkh4AqeeHCMbfbjeb0zMt0aEFzs= github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= @@ -59,16 +48,6 @@ github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8J github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= -github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= -github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= -github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= -github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 h1:xqgexXAGQgY3HAjNPSaCqn5Aahbo5TKsmhp8VRfr1iQ= -github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= @@ -84,12 +63,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= -github.com/matcornic/hermes/v2 v2.1.0 h1:9TDYFBPFv6mcXanaDmRDEp/RTWj0dTTi+LpFnnnfNWc= -github.com/matcornic/hermes/v2 v2.1.0/go.mod h1:2+ziJeoyRfaLiATIL8VZ7f9hpzH4oDHqTmn0bhrsgVI= -github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw= @@ -102,40 +75,28 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= -github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= -github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04 h1:L0rPdfzq43+NV8rfIx2kA4iSSLRj2jN5ijYHoeXRwvQ= -github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04/go.mod h1:tcnB1voG49QhCrwq1W0w5hhGasvOg+VQp9i9H1rCM1w= -github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe h1:9YnI5plmy+ad6BM+JCLJb2ZV7/TNiE5l7SNKfumYKgc= -github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe/go.mod h1:JTFJA/t820uFDoyPpErFQ3rb3amdZoPtxcKervG0OE4= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= -golang.org/x/crypto v0.0.0-20181029175232-7e6ffbd03851/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ= +golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= -golang.org/x/sys v0.0.0-20190225065934-cc5685c2db12/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -149,17 +110,15 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/models/cv.go b/internal/models/cv.go index be671c7..473ca44 100644 --- a/internal/models/cv.go +++ b/internal/models/cv.go @@ -48,6 +48,7 @@ type Experience struct { CompanyID string `json:"companyID,omitempty"` // Unique ID for scrolling/navigation CompanyURL string `json:"companyURL,omitempty"` // Optional URL for company website CompanyLogo string `json:"companyLogo"` + LogoIndex *int `json:"logoIndex,omitempty"` // Sprite sheet index (nil means no sprite) Location string `json:"location"` StartDate string `json:"startDate"` EndDate string `json:"endDate"` @@ -95,6 +96,7 @@ type Project struct { ProjectID string `json:"projectID,omitempty"` // Unique ID for scrolling/navigation URL string `json:"url"` ProjectLogo string `json:"projectLogo,omitempty"` // Optional logo filename + LogoIndex *int `json:"logoIndex,omitempty"` // Sprite sheet index (nil means no sprite) GitRepoUrl string `json:"gitRepoUrl,omitempty"` // Optional git repository URL for dynamic dates Location string `json:"location"` StartDate string `json:"startDate,omitempty"` // Optional static start date @@ -131,6 +133,7 @@ type Course struct { Institution string `json:"institution"` CourseID string `json:"courseID,omitempty"` // Unique ID for scrolling/navigation CourseLogo string `json:"courseLogo,omitempty"` // Optional logo filename + LogoIndex *int `json:"logoIndex,omitempty"` // Sprite sheet index (nil means no sprite) Location string `json:"location"` Date string `json:"date"` Duration string `json:"duration"` diff --git a/internal/models/cv/cv.go b/internal/models/cv/cv.go index f4c2d03..7c71c9a 100644 --- a/internal/models/cv/cv.go +++ b/internal/models/cv/cv.go @@ -55,6 +55,7 @@ type Experience struct { CompanyID string `json:"companyID,omitempty"` // Unique ID for scrolling/navigation CompanyURL string `json:"companyURL,omitempty"` // Optional URL for company website CompanyLogo string `json:"companyLogo"` + LogoIndex *int `json:"logoIndex,omitempty"` // Sprite sheet index (nil means no sprite) Location string `json:"location"` StartDate string `json:"startDate"` EndDate string `json:"endDate"` @@ -102,6 +103,7 @@ type Project struct { ProjectID string `json:"projectID,omitempty"` // Unique ID for scrolling/navigation URL string `json:"url"` ProjectLogo string `json:"projectLogo,omitempty"` // Optional logo filename + LogoIndex *int `json:"logoIndex,omitempty"` // Sprite sheet index (nil means no sprite) GitRepoUrl string `json:"gitRepoUrl,omitempty"` // Optional git repository URL for dynamic dates Location string `json:"location"` StartDate string `json:"startDate,omitempty"` // Optional static start date @@ -138,6 +140,7 @@ type Course struct { Institution string `json:"institution"` CourseID string `json:"courseID,omitempty"` // Unique ID for scrolling/navigation CourseLogo string `json:"courseLogo,omitempty"` // Optional logo filename + LogoIndex *int `json:"logoIndex,omitempty"` // Sprite sheet index (nil means no sprite) Location string `json:"location"` Date string `json:"date"` Duration string `json:"duration"` diff --git a/static/css/04-interactive/_sprites.css b/static/css/04-interactive/_sprites.css new file mode 100644 index 0000000..355917b --- /dev/null +++ b/static/css/04-interactive/_sprites.css @@ -0,0 +1,175 @@ +/* ============================================================================ + CSS SPRITES - Image Request Optimization + ============================================================================ + Reduces HTTP requests from 44+ individual images to just 3 sprite sheets. + Each sprite uses CSS custom property --icon-index for positioning. + ============================================================================ */ + +/* Base sprite class */ +.icon-sprite { + display: inline-block; + width: 50px; + height: 50px; + background-repeat: no-repeat; + background-size: auto 50px; + vertical-align: middle; +} + +/* Company icons */ +.icon-company { + background-image: url('/static/images/sprites/sprite-companies.png'); + background-position-x: calc(var(--icon-index, 0) * -50px); +} + +/* Project icons */ +.icon-project { + background-image: url('/static/images/sprites/sprite-projects.png'); + background-position-x: calc(var(--icon-index, 0) * -50px); +} + +/* Course icons */ +.icon-course { + background-image: url('/static/images/sprites/sprite-courses.png'); + background-position-x: calc(var(--icon-index, 0) * -50px); +} + +/* Retina displays - use @2x sprites for crisp rendering */ +@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + .icon-company { + background-image: url('/static/images/sprites/sprite-companies@2x.png'); + background-size: auto 50px; /* Display at 1x size */ + } + + .icon-project { + background-image: url('/static/images/sprites/sprite-projects@2x.png'); + background-size: auto 50px; + } + + .icon-course { + background-image: url('/static/images/sprites/sprite-courses@2x.png'); + background-size: auto 50px; + } +} + +/* Size variants for different contexts */ +.icon-sprite.icon-small { + width: 32px; + height: 32px; + background-size: auto 32px; +} + +.icon-sprite.icon-small.icon-company { + background-position-x: calc(var(--icon-index, 0) * -32px); +} + +.icon-sprite.icon-small.icon-project { + background-position-x: calc(var(--icon-index, 0) * -32px); +} + +.icon-sprite.icon-small.icon-course { + background-position-x: calc(var(--icon-index, 0) * -32px); +} + +.icon-sprite.icon-large { + width: 64px; + height: 64px; + background-size: auto 64px; +} + +.icon-sprite.icon-large.icon-company { + background-position-x: calc(var(--icon-index, 0) * -64px); +} + +.icon-sprite.icon-large.icon-project { + background-position-x: calc(var(--icon-index, 0) * -64px); +} + +.icon-sprite.icon-large.icon-course { + background-position-x: calc(var(--icon-index, 0) * -64px); +} + +/* For section logos - match .company-logo img styling */ +/* Use a wrapper approach: 80px box, 60px icon centered inside */ +.icon-sprite.icon-section { + /* Outer box matches img styling */ + width: 80px; + height: 80px; + border-radius: 4px; + border: 1px solid var(--icon-border, #ddd); + background-color: transparent; + box-sizing: border-box; + /* Inner content area for sprite */ + padding: 10px; + background-size: auto 60px; + background-origin: content-box; + background-clip: content-box; + background-position: 0 0; +} + +.icon-sprite.icon-section.icon-company { + background-position-x: calc(var(--icon-index, 0) * -60px); +} + +.icon-sprite.icon-section.icon-project { + background-position-x: calc(var(--icon-index, 0) * -60px); +} + +.icon-sprite.icon-section.icon-course { + background-position-x: calc(var(--icon-index, 0) * -60px); +} + +/* Retina overrides for size variants */ +@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + .icon-sprite.icon-small { + background-size: auto 32px; + } + + .icon-sprite.icon-small.icon-company { + background-position-x: calc(var(--icon-index, 0) * -32px); + } + + .icon-sprite.icon-small.icon-project { + background-position-x: calc(var(--icon-index, 0) * -32px); + } + + .icon-sprite.icon-small.icon-course { + background-position-x: calc(var(--icon-index, 0) * -32px); + } + + .icon-sprite.icon-large { + background-size: auto 64px; + } + + .icon-sprite.icon-large.icon-company { + background-position-x: calc(var(--icon-index, 0) * -64px); + } + + .icon-sprite.icon-large.icon-project { + background-position-x: calc(var(--icon-index, 0) * -64px); + } + + .icon-sprite.icon-large.icon-course { + background-position-x: calc(var(--icon-index, 0) * -64px); + } + + .icon-sprite.icon-section { + padding: 10px; + background-size: auto 60px; + background-position: 0 0; + } + + .icon-sprite.icon-section.icon-company { + background-size: auto 60px; + background-position-x: calc(var(--icon-index, 0) * -60px); + } + + .icon-sprite.icon-section.icon-project { + background-size: auto 60px; + background-position-x: calc(var(--icon-index, 0) * -60px); + } + + .icon-sprite.icon-section.icon-course { + background-size: auto 60px; + background-position-x: calc(var(--icon-index, 0) * -60px); + } +} diff --git a/static/css/main.css b/static/css/main.css index b9da403..1eb71c8 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -35,6 +35,7 @@ @import './04-interactive/_toasts.css'; @import './04-interactive/_zoom-control.css'; @import './04-interactive/_contact-form.css'; +@import './04-interactive/_sprites.css'; /* 05 - Responsive */ @import './05-responsive/_breakpoints.css'; diff --git a/static/dist/bundle.min.css b/static/dist/bundle.min.css index 057718d..5309ebe 100644 --- a/static/dist/bundle.min.css +++ b/static/dist/bundle.min.css @@ -1 +1 @@ -*{box-sizing:border-box;margin:0;padding:0}body{background-color:var(--page-bg,#d6d6d6);background-image:var(--page-bg-pattern,none);background-size:40px 40px;background-attachment:fixed;max-width:100vw;overflow-x:clip}html{scroll-behavior:smooth;max-width:100vw;scroll-padding-top:70px;overflow-x:clip}:root{--bg-gray:#525659;--sidebar-gray:#d1d4d2;--black-bar:#2b2b2b;--paper-white:#fff;--text-dark:#000;--text-gray:#333;--accent-blue:#06c;--border-gray:#ddd;--page-bg:#d6d6d6;--page-bg-pattern:repeating-linear-gradient(0deg,transparent,transparent 5px,#4b55630f 5px,#4b55630f 6px,transparent 6px,transparent 15px),repeating-linear-gradient(90deg,transparent,transparent 5px,#4b55630f 5px,#4b55630f 6px,transparent 6px,transparent 15px),repeating-linear-gradient(0deg,transparent,transparent 10px,#6b72800a 10px,#6b72800a 11px,transparent 11px,transparent 30px),repeating-linear-gradient(90deg,transparent,transparent 10px,#6b72800a 10px,#6b72800a 11px,transparent 11px,transparent 30px);--paper-bg:#fff;--paper-secondary-bg:#f5f5f5;--text-primary:#1a1a1a;--text-secondary:#333;--text-muted:#666;--text-light:#999;--action-bar-bg:#2b2b2b;--action-bar-text:#fff;--action-bar-text-muted:#ffffffd9;--border-color:#333;--border-light:#e0e0e0;--shadow-sm:0 1px 3px #0000001a;--shadow-md:0 2px 8px #00000026;--shadow-lg:2px 2px 9px #00000080;--button-bg:transparent;--button-bg-hover:#0000000d;--button-bg-active:#0000001a;--accent-green:#27ae60;--sidebar-bg:#d1d4d2}body{color:var(--text-secondary,#333);font-smoothing:antialiased;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:Quicksand,Source Sans Pro,-apple-system,system-ui,sans-serif;font-size:16px;font-weight:400;line-height:1.5}a{color:var(--accent-blue,#06c);text-decoration:none;&:hover{text-decoration:underline}}:root{--page-bg:#d6d6d6;--page-bg-pattern:repeating-linear-gradient(0deg,transparent,transparent 5px,#4b55630f 5px,#4b55630f 6px,transparent 6px,transparent 15px),repeating-linear-gradient(90deg,transparent,transparent 5px,#4b55630f 5px,#4b55630f 6px,transparent 6px,transparent 15px),repeating-linear-gradient(0deg,transparent,transparent 10px,#6b72800a 10px,#6b72800a 11px,transparent 11px,transparent 30px),repeating-linear-gradient(90deg,transparent,transparent 10px,#6b72800a 10px,#6b72800a 11px,transparent 11px,transparent 30px);--paper-bg:#fff;--paper-secondary-bg:#f5f5f5;--text-primary:#1a1a1a;--text-secondary:#333;--text-muted:#666;--text-light:#999;--action-bar-bg:#2b2b2b;--action-bar-text:#fff;--action-bar-text-muted:#ffffffd9;--border-color:#333;--border-light:#e0e0e0;--icon-border:#ddd;--item-separator:#0000001a;--shadow-sm:0 1px 3px #0000001a;--shadow-md:0 2px 8px #00000026;--shadow-lg:2px 2px 9px #00000080;--button-bg:transparent;--button-bg-hover:#0000000d;--button-bg-active:#0000001a;--accent-blue:#06c;--accent-green:#27ae60;--sidebar-bg:#d1d4d2;--text-dark:#1a1a1a;--text-gray:#333}[data-color-theme=dark]{--page-bg:#3a3a3a;--page-bg-pattern:repeating-linear-gradient(45deg,#00ff8026 0,#00ff8026 1px,transparent 1px,transparent 20px),repeating-linear-gradient(-45deg,#00ff8026 0,#00ff8026 1px,transparent 1px,transparent 20px);--paper-bg:#1a1a1a;--paper-secondary-bg:#2a2a2a;--text-primary:#e0e0e0;--text-secondary:#d0d0d0;--text-muted:#b0b0b0;--text-light:gray;--action-bar-bg:#1a1a1a;--action-bar-text:#e0e0e0;--action-bar-text-muted:#e0e0e0d9;--border-color:#404040;--border-light:#333;--icon-border:#5e5e5e;--item-separator:#ffffff0d;--shadow-sm:0 1px 3px #0000004d;--shadow-md:0 2px 8px #0006;--shadow-lg:0 4px 16px #0009;--button-bg:transparent;--button-bg-hover:#ffffff0d;--button-bg-active:#ffffff1a;--accent-blue:#39f;--accent-green:#2ecc71;--sidebar-bg:#3a3d3e;--text-dark:#e0e0e0;--text-gray:#d0d0d0}@media (prefers-color-scheme:dark){[data-color-theme=auto]{--page-bg:#3a3a3a;--page-bg-pattern:repeating-linear-gradient(45deg,#00ff8026 0,#00ff8026 1px,transparent 1px,transparent 20px),repeating-linear-gradient(-45deg,#00ff8026 0,#00ff8026 1px,transparent 1px,transparent 20px);--paper-bg:#1a1a1a;--paper-secondary-bg:#2a2a2a;--text-primary:#e0e0e0;--text-secondary:#d0d0d0;--text-muted:#b0b0b0;--text-light:gray;--action-bar-bg:#1a1a1a;--action-bar-text:#e0e0e0;--action-bar-text-muted:#e0e0e0d9;--border-color:#404040;--border-light:#333;--icon-border:#5e5e5e;--item-separator:#ffffff0d;--shadow-sm:0 1px 3px #0000004d;--shadow-md:0 2px 8px #0006;--shadow-lg:0 4px 16px #0009;--button-bg:transparent;--button-bg-hover:#ffffff0d;--button-bg-active:#ffffff1a;--accent-blue:#39f;--accent-green:#2ecc71;--sidebar-bg:#3a3d3e;--text-dark:#e0e0e0;--text-gray:#d0d0d0}}.color-theme-switcher{background:var(--black-bar,#2b2b2b);color:#fff;cursor:pointer;z-index:999;opacity:.6;border:none;border-radius:50%;justify-content:center;align-items:center;width:50px;height:50px;transition:all .3s;display:flex;bottom:14rem;left:2rem;box-shadow:0 4px 12px #0000004d;position:fixed!important}.color-theme-switcher:hover[data-theme-mode=light]{background:#d4b200!important}.color-theme-switcher:hover[data-theme-mode=dark]{background:#013c77!important}.color-theme-switcher:hover[data-theme-mode=auto]{background:#9b59b6!important}.color-theme-switcher:hover{transform:translateY(-3px);box-shadow:0 6px 16px #0006;opacity:1!important}.color-theme-switcher.at-bottom[data-theme-mode=light]{opacity:1;background:#d4b200!important}.color-theme-switcher.at-bottom[data-theme-mode=dark]{opacity:1;background:#013c77!important}.color-theme-switcher.at-bottom[data-theme-mode=auto]{opacity:1;background:#9b59b6!important}.color-theme-switcher iconify-icon{transition:color .3s;color:#fff!important}.color-theme-switcher:hover iconify-icon{color:#fff!important}.theme-option-btn{display:none}.section-icon iconify-icon,.project-icon iconify-icon,.course-icon iconify-icon,.default-project-icon iconify-icon{color:inherit!important}.site-icon iconify-icon,.site-icon-mobile iconify-icon{color:#fff!important}.cv-paper iconify-icon{color:inherit!important}.error-icon iconify-icon{color:#dc3545!important}@media (width<=900px){.color-theme-switcher{opacity:1!important;width:clamp(36px,2.69231vw + 25.7692px,50px)!important;height:clamp(36px,2.69231vw + 25.7692px,50px)!important;position:fixed!important;bottom:1.5rem!important;left:calc(50% + clamp(22px,2.11538vw + 13.9615px,33px))!important;right:auto!important;transform:none!important}.color-theme-switcher iconify-icon{width:clamp(18px,1.15385vw + 13.6154px,24px)!important;height:clamp(18px,1.15385vw + 13.6154px,24px)!important;font-size:clamp(18px,1.15385vw + 13.6154px,24px)!important}.color-theme-switcher[data-theme-mode=light]{background:#d4b200!important}.color-theme-switcher[data-theme-mode=dark]{background:#013c77!important}.color-theme-switcher[data-theme-mode=auto]{background:#9b59b6!important}.color-theme-switcher:hover[data-theme-mode=light],.color-theme-switcher:hover[data-theme-mode=dark],.color-theme-switcher:hover[data-theme-mode=auto]{transform:translateY(-3px)!important;box-shadow:0 6px 16px #0006!important}.is-mobile-device .color-theme-switcher{left:calc(50% + clamp(2px,.384615vw + .538462px,4px))!important}}[data-color-theme=dark] img[src*=livgolf],[data-color-theme=auto] img[src*=livgolf]{filter:invert();border-color:#a1a1a1!important}@media (prefers-color-scheme:light){[data-color-theme=auto] img[src*=livgolf]{filter:none;border-color:var(--icon-border,#ddd)!important}}.cv-container{width:100%;max-width:100%;margin:0 auto;padding:20px 0 0;display:block;&.theme-clean{padding:20px 0 0;transition:all .3s ease-in-out;& .cv-page{box-shadow:var(--shadow-lg,2px 2px 9px #00000080);border:none;max-width:900px;margin:0 auto;transition:all .3s ease-in-out}& .cv-sidebar,& .cv-title-badges-header,& .cv-footer{animation:.3s ease-in-out fadeOutShrink;display:none!important}& .page-content{transition:grid-template-columns .3s ease-in-out;grid-template-columns:1fr!important}& .cv-main{transition:all .3s ease-in-out;grid-column:1!important;padding:2rem 3rem!important}}}.cv-sidebar,.cv-title-badges-header,.cv-footer{transition:all .3s ease-in-out;overflow:hidden}.cv-page{background:var(--paper-bg,#fff);max-width:1200px;box-shadow:var(--shadow-lg,2px 2px 9px #00000080);transform-origin:top;border:none;margin:2rem auto;transition:transform .3s;transform:scale(.95)}.page-content{display:grid}.page-1 .page-content{grid-template-columns:300px 1fr}.page-2 .page-content{grid-template-columns:1fr 300px}.cv-sidebar-left{grid-area:1/1}.cv-sidebar-right{text-align:right;grid-area:1/2}.page-1 .cv-main{grid-area:1/2}.page-2 .cv-main{grid-area:1/1}.cv-footer{color:#ccc;background:#303030;grid-column:1/-1;margin:0;padding:20px 0}.footer-content{text-align:center;margin:0;padding:0;list-style:none}.footer-content li{margin:0;display:inline-block}.footer-content li>div{text-align:left;margin:0 20px;display:inline-block}.footer-label{width:200px;font-size:1.7em}.footer-value{width:450px;font-size:1em}.footer-value b{font-size:1.7em;font-weight:400}.footer-separator{font-size:.6em;position:relative;left:-4%}.footer-separator i{opacity:.3}.cv-footer a{color:inherit}.cv-footer a:hover{color:#0275d8;text-decoration:none}.cv-title-badges-header{border-bottom:2px solid #34495e;flex-wrap:wrap;grid-column:1/-1;justify-content:center;align-items:center;gap:0;padding:10px 20px;display:flex;background:#303030!important}.title-badge{color:#ccc;text-transform:uppercase;white-space:nowrap;font-size:.9em;font-weight:400}.badge-separator{color:#ccc;padding:0 15px;font-weight:400;position:relative;top:-1px}.cv-main{background:var(--paper-bg,#fff);padding:3rem 2.5rem 8rem}.cv-paper{width:100%;box-shadow:none;transform-origin:top;will-change:transform;background:0 0;min-height:auto;margin:0;transition:transform 80ms linear;display:block;position:relative}.page-break{page-break-after:always;break-after:page}.avoid-break{page-break-inside:avoid;break-inside:avoid}.action-bar{background:var(--action-bar-bg,#2b2b2b);color:var(--action-bar-text,#fff);z-index:100;box-shadow:var(--shadow-md,0 2px 8px #00000026);font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,system-ui,sans-serif;position:sticky;top:0;overflow:visible}.action-bar-content{grid-template-columns:1fr auto 1fr;align-items:stretch;gap:2rem;max-width:100%;height:50px;margin:0 auto;padding:0;display:grid;overflow:visible}.site-title{white-space:nowrap;justify-self:start;align-items:center;gap:.75rem;height:100%;padding:0;display:flex}.site-title-left{align-items:center;gap:.75rem;display:flex}.site-icon{color:#fff;flex-shrink:0;justify-content:center;align-items:center;height:36px;padding:0 .5rem 0 1.5rem;display:inline-flex}.site-icon-mobile{color:#fff;flex-shrink:0;margin-right:.5rem;display:none}.site-logo-link,.site-title-link{color:inherit;align-items:center;height:36px;text-decoration:none;transition:opacity .2s;display:flex}.site-logo-link:hover,.site-title-link:hover{opacity:.8;text-decoration:none}.site-logo-link{padding:0}.iconify,iconify-icon{vertical-align:middle;display:inline-block}.site-title-text{color:#fff;letter-spacing:-.01em;align-items:center;height:36px;padding:0 1rem 0 0;font-size:1.05rem;font-weight:500;line-height:1;display:flex}.view-controls-center{white-space:nowrap;flex-direction:row;flex-shrink:0;justify-self:center;align-items:center;gap:2.5rem;height:100%;display:flex}.selector-group{align-items:center;gap:.75rem;display:flex}.selector-label{color:#ffffffd9;white-space:nowrap;letter-spacing:-.01em;align-items:center;height:36px;font-size:.875rem;font-weight:500;line-height:1;display:flex}.selector-label span{color:#27ae60;font-weight:600}.language-toggle,.cv-length-toggle,.logo-toggle{flex-shrink:0}.action-buttons{flex-shrink:0;justify-self:end}.htmx-indicator{flex-shrink:0}.lang-btn{color:#fff;cursor:pointer;text-transform:capitalize;background:0 0;border:1px solid #ffffff4d;border-radius:3px;padding:.4rem 1rem;font-size:1rem;font-weight:400;transition:all .2s}.lang-btn:hover{background:#ffffff1a;border-color:#ffffff80}.lang-btn.active{font-weight:500;background:#27ae60!important;border-color:#27ae60!important}.icon-toggle{cursor:pointer;display:flex;position:relative}.icon-toggle input[type=checkbox]{opacity:0;width:0;height:0;position:absolute}.icon-toggle-slider{background:#e0e0e0;border:2px solid #d0d0d0;border-radius:15px;justify-content:space-between;align-items:center;width:75px;height:30px;padding:0 6px;transition:all .3s;display:inline-flex;position:relative}.icon-toggle-slider:before{content:"";z-index:2;pointer-events:none;background:#fff;border-radius:50%;width:24px;height:24px;transition:transform .3s;position:absolute;left:2px;box-shadow:0 2px 4px #0000004d}.icon-toggle input:checked+.icon-toggle-slider:before{transform:translate(43px)}.icon-toggle input:checked+.icon-toggle-slider{background:#27ae60;border-color:#229954}.icon-toggle-slider .icon-left,.icon-toggle-slider .icon-right{z-index:3;pointer-events:none;flex-shrink:0;transition:all .3s;position:absolute}.icon-toggle-slider .icon-left{left:6px}.icon-toggle-slider .icon-right{right:6px}.icon-toggle input:not(:checked)+.icon-toggle-slider .icon-left{font-weight:700;color:#333!important}.icon-toggle input:not(:checked)+.icon-toggle-slider .icon-right{opacity:.5;color:#999!important}.icon-toggle input:checked+.icon-toggle-slider .icon-left{opacity:.5;color:#fff6!important}.icon-toggle input:checked+.icon-toggle-slider .icon-right{font-weight:700;color:#fff!important}.icon-toggle input:focus+.icon-toggle-slider{box-shadow:0 0 0 3px #27ae6033}.language-selector-wrapper{width:fit-content;height:100%;display:inline-flex;position:relative}.language-selector{background:0 0;border-radius:0;align-items:stretch;gap:0;height:100%;margin-right:0;padding:0 0 0 1rem;display:inline-flex}#lang-indicator-en,#lang-indicator-es{pointer-events:none;z-index:10;position:absolute;top:50%;transform:translateY(-50%)}#lang-indicator-en{left:calc(1rem + 50px)}#lang-indicator-es{left:calc(1rem + 135px)}.selector-btn{color:#fff;cursor:pointer;white-space:nowrap;letter-spacing:-.01em;background:0 0;border:none;border-radius:0;justify-content:center;align-items:center;gap:0;height:100%;padding:0 1.5rem;font-size:1rem;font-weight:500;line-height:1;text-decoration:none;transition:all .2s;display:inline-flex;box-shadow:none!important;outline:none!important;min-width:50px!important}.selector-btn:focus,.selector-btn:focus-visible,.selector-btn:active{box-shadow:none!important;outline:none!important}.selector-btn:hover{background:#666}.selector-btn:hover iconify-icon{color:#27ae60}.selector-btn.active{color:#fff;background:#27ae60}.selector-btn:not(.active){color:#fff;background:0 0}.action-btn{color:#fff;cursor:pointer;white-space:nowrap;letter-spacing:-.01em;background:0 0;border:none;border-radius:0;justify-content:center;align-items:center;gap:.5rem;height:100%;padding:0 1.5rem;font-size:1rem;font-weight:500;line-height:1;text-decoration:none;transition:background-color .3s,color .3s;display:inline-flex}.action-btn iconify-icon{color:#fff;transition:color .3s}.action-btn:hover{color:#333;background:#ddd;text-decoration:none}.action-btn:hover iconify-icon{color:#27ae60}.pdf-btn{color:#fff!important;background:0 0!important}.pdf-btn:hover,.pdf-btn.pdf-hover-sync{color:#fff!important;background:#cd6060!important}.pdf-btn iconify-icon{filter:brightness(0)invert();transition:filter .3s;color:#fff!important}.pdf-btn:hover iconify-icon{filter:brightness(0)invert();color:#fff!important}.print-btn{color:#fff!important;background:0 0!important}.print-btn:hover,.print-btn.print-hover-sync{color:#27ae60!important;background:#fff!important}.print-btn iconify-icon{color:#fff}.print-btn:hover iconify-icon,.print-btn.print-hover-sync iconify-icon{color:#27ae60}.cv-length-toggle{justify-self:center;gap:.5rem;display:flex}.length-btn{color:#fff;cursor:pointer;background:#ffffff1a;border:1px solid #fff6;border-radius:4px;padding:.4rem 1rem;font-size:.9rem;font-weight:500;transition:all .2s}.length-btn:hover{background:#fff3;border-color:#fff9}.length-btn.active{color:#1a1a1a;background:#fff;border-color:#fff;font-weight:600}.action-buttons,.action-buttons-right{flex-wrap:nowrap;align-items:stretch;gap:0;height:100%;display:flex;overflow:visible}.action-buttons-right{justify-self:end;margin-left:auto}@media (width>=901px) and (width<=1400px){.action-buttons-right{flex-shrink:1;min-width:0}.action-buttons-right .action-btn{flex-shrink:1;min-width:40px;padding:0 .5rem}}@media (width>=541px) and (width<=900px){.action-buttons-right{flex-shrink:1;min-width:0;display:flex!important}.action-buttons-right .action-btn{flex-shrink:1;width:auto;min-width:36px;padding:0 .4rem;font-size:0}.action-buttons-right .action-btn iconify-icon{width:20px;height:20px}}@media (width>=901px) and (width<=1100px){.action-btn{width:clamp(35px,4vw,50px)!important;padding:0 clamp(.3rem,.8vw,1rem)!important}}.htmx-indicator{opacity:0;pointer-events:none;justify-content:center;align-items:center;transition:opacity .2s ease-in-out;display:inline-flex;position:absolute}.htmx-indicator.htmx-request,#lang-indicator-en.htmx-request,#lang-indicator-es.htmx-request{opacity:1!important}iconify-icon.htmx-indicator{justify-content:center;align-items:center;display:inline-flex}span.htmx-request.htmx-indicator,.htmx-request .htmx-indicator,.htmx-request.htmx-indicator{opacity:1!important}.htmx-indicator.spinning{animation:1s linear infinite htmx-spin}@keyframes htmx-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.htmx-indicator.small{width:14px;height:14px;font-size:14px}.htmx-indicator.medium{width:18px;height:18px;font-size:18px}.htmx-indicator.large{width:24px;height:24px;font-size:24px}.htmx-indicator.inline{vertical-align:middle;margin-left:8px;display:inline-flex}.htmx-indicator.inline-start{vertical-align:middle;margin-right:8px;display:inline-flex}.htmx-indicator.light{color:#ffffffe6}.htmx-indicator.dark{color:#000000b3}.htmx-indicator.accent{color:#27ae60}@media (prefers-reduced-motion:reduce){.htmx-indicator.spinning{animation:none}.htmx-indicator{transition:none}}.loader{border:2px solid #f3f3f3;border-top-color:#fff;border-radius:50%;width:20px;height:20px;animation:1s linear infinite htmx-spin}.cv-sidebar{background:var(--sidebar-bg,#d1d4d2);padding:4rem 1.5rem;font-size:.9rem}.sidebar-accordion-header{display:none}.sidebar-section{margin-bottom:2rem;&:has(details:not([open])){margin-top:0;margin-bottom:3rem}& details{margin:0;& summary~*{opacity:0;max-height:0;transition:max-height .5s ease-in-out,opacity .3s ease-in-out,transform .3s ease-in-out;overflow:hidden;transform:translateY(-8px)}&[open] summary~*{opacity:1;max-height:1500px;transform:translateY(0)}&[open] .sidebar-content{margin-top:.5rem}}& summary{cursor:pointer;-webkit-user-select:none;user-select:none;justify-content:space-between;align-items:center;list-style:none;display:flex;position:relative;&::-webkit-details-marker,&::marker{display:none}& .sidebar-title{margin-bottom:0}&:hover .sidebar-title{color:var(--accent-blue,#06c)}&:hover:after,details:not([open]) &:after{opacity:1}}}.sidebar-title{color:var(--text-primary,#1a1a1a);text-align:left;margin-bottom:10px;padding:0;font-family:Quicksand,sans-serif;font-size:1.4em;font-weight:700;line-height:1.3em}.sidebar-content{color:var(--text-primary,#1a1a1a);font-family:Quicksand,sans-serif;font-size:.95rem;font-weight:400;line-height:1.5}.skill-item{color:var(--text-primary,#1a1a1a);margin-bottom:.15rem;font-weight:400}.cv-sidebar-left{& .sidebar-section summary:after{content:"▶";color:var(--text-muted,#666);opacity:0;flex-shrink:0;margin-left:15px;font-size:.8em;transition:transform .2s,opacity .2s}& .sidebar-section details[open] summary:after{transform:rotate(90deg)}& .sidebar-content,& .skill-item{text-align:left}}.cv-sidebar-right{& .sidebar-section summary{flex-direction:row-reverse;justify-content:space-between;& .sidebar-title{text-align:right;width:100%}&:after{content:"▶";color:var(--text-muted,#666);opacity:0;flex-shrink:0;margin-right:15px;font-size:.8em;transition:transform .2s,opacity .2s}}& .sidebar-section details[open] summary:after{transform:rotate(90deg)}& .sidebar-content,& .skill-item{text-align:right}}.cv-header{margin-bottom:2rem}.cv-header-content{justify-content:space-between;align-items:flex-start;gap:2rem;display:flex}.cv-header-left{flex:1;padding-right:185px;position:relative}.cv-photo{border:3px solid #fff;flex-shrink:0;width:150px;height:200px;position:absolute;top:15px;right:15px;overflow:hidden;box-shadow:0 2px 8px #00000026}.cv-photo img{object-fit:cover;width:100%;height:100%}.cv-name{color:var(--text-primary,#1a1a1a);text-align:right;margin-bottom:8px;font-family:Quicksand,sans-serif;font-size:2.2em;font-weight:400;line-height:1.1}.cv-experience-years{color:var(--text-primary,#1a1a1a);margin:0;font-family:Quicksand,sans-serif;font-size:.9em;font-weight:500;line-height:1.5}.years-experience{color:var(--text-muted,#666);text-align:right;margin:4px 0 0;font-family:Quicksand,sans-serif;font-size:1.25em;font-weight:400;line-height:1.4}.intro-text{color:var(--text-secondary,#333);text-align:justify;-moz-text-align-last:justify;text-align-last:justify;text-justify:inter-word;word-spacing:-1px;overflow-wrap:break-word;word-wrap:break-word;-webkit-hyphens:auto;-moz-hyphens:auto;-ms-hyphens:auto;hyphens:auto;margin-top:20px;font-family:Quicksand,sans-serif;font-size:1em;font-style:italic;line-height:1.6}.cv-section{page-break-inside:avoid;margin-bottom:3rem}.cv-section:has(details:not([open])){margin-bottom:0}.section-title{color:var(--text-primary,#1a1a1a);margin:20px 0 25px;padding:0;font-family:Quicksand,sans-serif;font-size:1.4em;font-weight:500;line-height:1.2em}.cv-section details{margin:0}.cv-section details summary~*{opacity:0;max-height:0;transition:max-height .5s ease-in-out,opacity .3s ease-in-out,transform .3s ease-in-out;overflow:hidden;transform:translateY(-8px)}.cv-section details[open] summary~*{opacity:1;max-height:3000px;transform:translateY(0)}.cv-section summary{cursor:pointer;-webkit-user-select:none;user-select:none;list-style:none;position:relative}.cv-section summary::-webkit-details-marker,.cv-section summary::marker{display:none}.cv-section summary .section-title{align-items:center;gap:.5rem;display:inline-flex}.cv-section summary .section-title:after{content:"▼";color:var(--text-muted,#666);opacity:0;margin-left:.5rem;font-size:.8em;transition:transform .2s,opacity .2s}.cv-section summary:hover .section-title:after,.cv-section details:not([open]) summary .section-title:after{opacity:1}.cv-section details:not([open]) summary .section-title:after{transform:rotate(-90deg)}.cv-section summary:hover .section-title{color:var(--accent-blue,#06c)}.summary-text{text-align:justify;color:var(--text-primary,#1a1a1a);font-family:Quicksand,sans-serif;font-size:.9em;font-weight:400;line-height:1.5}.responsibilities li div iconify-icon,.responsibilities li strong+iconify-icon,.responsibilities li em+iconify-icon{vertical-align:middle;margin:0 .15em;font-size:1em;display:inline-block;width:1.2em!important;height:1.2em!important;color:inherit!important;background:0 0!important;border:none!important;padding:0!important}.experience-header{margin-bottom:.6rem}.experience-title-line{margin-bottom:.3em}.position{color:var(--text-dark,#1a1a1a);margin:0 0 4px;font-size:1rem;font-weight:500}.position .position-title{margin-right:.3em;display:inline-block}.position .company-name{display:inline-block}.current-badge{color:#fff;vertical-align:middle;letter-spacing:.5px;background:#27ae60;border-radius:3px;margin-left:.5em;padding:.2em .5em;font-size:.7em;font-weight:700;display:inline-block}.live-badge{color:#fff;vertical-align:middle;letter-spacing:.5px;background:#27ae60;border-radius:3px;align-items:center;gap:.3em;margin-left:.5em;padding:.2em .5em;font-size:.7em;font-weight:700;display:inline-flex}.live-badge iconify-icon{font-size:1.2em}.expired-badge{color:#fff;vertical-align:middle;letter-spacing:.5px;background:#e74c3c;border-radius:3px;margin-left:.5em;padding:.2em .5em;font-size:.7em;font-weight:700;display:inline-block}.maintained-badge{color:#fff;vertical-align:middle;letter-spacing:.5px;background:#3498db;border-radius:3px;margin-left:.5em;padding:.2em .5em;font-size:.7em;font-weight:700;display:inline-block}.experience-period,.experience-separator,.experience-location,.experience-duration{color:var(--text-muted,#666);font-size:1.05rem;font-weight:600;display:inline-block}.experience-duration{font-style:italic}.short-desc{color:var(--text-dark,#1a1a1a);margin-top:.5rem;font-size:.95rem;line-height:1.6}.duration-text{color:var(--text-light,#999);font-weight:500}.responsibilities{margin-top:1rem;padding-left:0;list-style:none}.responsibilities li{color:var(--text-dark,#1a1a1a);margin-bottom:.4rem;padding-left:1.2rem;font-size:.95rem;line-height:1.5;position:relative}.responsibilities li:before{content:"•";color:var(--text-gray,#333);position:absolute;left:0}.responsibilities li:has(img),.responsibilities li:has(iconify-icon){grid-template-columns:60px 1fr;align-items:start;gap:1rem;margin-bottom:1rem;padding-left:0;display:grid}.responsibilities li:has(img):before,.responsibilities li:has(iconify-icon):before{display:none}.responsibilities li img{object-fit:contain;border:1px solid var(--icon-border,#ddd);background:0 0;border-radius:4px;width:60px;height:60px;padding:4px}.responsibilities li iconify-icon.default-company-icon{border:1px solid var(--icon-border,#ddd);width:60px;height:60px;color:var(--text-light,#999);background:0 0;border-radius:4px;justify-content:center;align-items:center;padding:8px;display:flex}@keyframes fadeInGrow{0%{opacity:0;transform-origin:top;max-height:0;transform:scaleY(.8)}to{opacity:1;max-height:5000px;transform:scaleY(1)}}@keyframes fadeOutShrink{0%{opacity:1;max-height:5000px;transform:scaleY(1)}to{opacity:0;transform-origin:top;max-height:0;transform:scaleY(.8)}}.cv-long .long-only,.cv-long .responsibilities{animation:.3s ease-in-out fadeInGrow;display:block}.project-item{border-bottom:1px solid #0000001a;align-items:flex-start;gap:1.2rem;margin-bottom:2.5rem;padding-bottom:2rem;display:flex}.project-icon{flex-shrink:0;justify-content:center;align-items:center;width:80px;height:80px;display:flex}.project-icon img{object-fit:contain;border:1px solid var(--icon-border,#ddd);background:0 0;border-radius:4px;width:80px;height:80px;padding:4px}.default-project-icon{border:1px solid var(--icon-border,#ddd);width:80px;height:80px;color:var(--text-light,#999);background:0 0;border-radius:4px;justify-content:center;align-items:center;padding:10px;display:flex}.project-content{flex:1}.project-header{margin-bottom:.5rem}.project-title{color:var(--text-dark,#1a1a1a);margin:0 0 .3rem;font-size:1em;font-weight:600;line-height:1.4}.project-title-text{display:inline}.project-title-text a{color:var(--accent-blue,#06c);text-decoration:none}.project-title-text a:hover{text-decoration:underline}.project-period,.project-separator,.project-location{color:var(--text-muted,#666);font-size:.9em;font-weight:600}.project-separator{color:var(--text-light,#999)}.project-desc{color:var(--text-dark,#1a1a1a);text-align:justify;margin-top:.5rem;font-size:.95rem;line-height:1.6}.project-technologies{color:var(--text-gray,#333);margin-top:.5rem;font-size:.85em;line-height:1.4}.projects-footer{text-align:center;color:var(--text-gray,#333);margin-top:-1.5rem;padding-top:0;font-size:.95rem}.projects-footer p{margin:0}.projects-footer a{color:var(--accent-blue,#06c);text-decoration:none}.projects-footer a:hover{text-decoration:underline}.reference-item{margin-bottom:0!important;margin-left:2rem!important;font-size:.95rem!important;line-height:1.4!important}.reference-item a{color:var(--accent-blue,#06c);word-break:break-word;text-decoration:none}.reference-item a:hover{text-decoration:underline}.ref-type{color:var(--text-gray,#333);margin-top:.2rem;font-size:.8em;font-style:italic;display:block}footer{text-align:center;color:#ffffffb3;padding:2rem;font-size:.85rem}.github-repo-link{transition:color .2s ease-in-out;color:#f5f5f5!important}.github-repo-link:hover{color:#66b3ff!important}.long-only,.short-desc{transition:all .3s ease-in-out;overflow:hidden}.cv-short .long-only{animation:.3s ease-in-out fadeOutShrink;display:none}.cv-short .short-desc{animation:.3s ease-in-out fadeInGrow;display:block}.cv-long .short-desc,.short-desc{animation:.3s ease-in-out fadeOutShrink;display:none}.cv-long .long-only,.cv-long .responsibilities{animation:.3s ease-in-out fadeInGrow;display:block}.project-item .responsibilities li iconify-icon.default-company-icon{border:1px solid var(--icon-border,#ddd);border-radius:4px;justify-content:center;align-items:center;padding:8px;width:60px!important;height:60px!important;color:unset!important;background:0 0!important;display:flex!important}.project-desc iconify-icon,.project-technologies iconify-icon{vertical-align:middle;margin:0 .15em;font-size:1em;display:inline-block;width:1.2em!important;height:1.2em!important;color:inherit!important}.course-item{border-bottom:1px solid #0000001a;align-items:flex-start;gap:1.2rem;margin-bottom:2.5rem;padding-bottom:2rem;display:flex}.course-icon{flex-shrink:0;justify-content:center;align-items:center;width:80px;height:80px;display:flex}.course-icon img{object-fit:contain;border:1px solid var(--icon-border,#ddd);background:0 0;border-radius:4px;width:80px;height:80px;padding:4px}.default-course-icon{border:1px solid var(--icon-border,#ddd);width:80px;height:80px;color:var(--text-light,#999);background:0 0;border-radius:4px;justify-content:center;align-items:center;padding:10px;display:flex}.course-content{flex:1}.course-header{margin-bottom:.5rem}.course-title{color:var(--text-dark,#1a1a1a);margin:0 0 .3rem;font-size:1em;font-weight:600;line-height:1.4}.course-title-text{display:inline}.course-institution{margin-left:.5em;font-weight:400;display:inline}.course-period,.course-separator,.course-location,.course-duration{color:var(--text-muted,#666);font-size:.9em}.course-separator{color:var(--text-light,#999)}.course-desc{color:var(--text-gray,#333);text-align:justify;margin-top:.4rem;font-size:.85em;line-height:1.4}.course-item .responsibilities li iconify-icon.default-company-icon{border:1px solid var(--icon-border,#ddd);border-radius:4px;justify-content:center;align-items:center;padding:8px;width:60px!important;height:60px!important;color:unset!important;background:0 0!important;display:flex!important}.course-desc iconify-icon{vertical-align:middle;margin:0 .15em;font-size:1em;display:inline-block;width:1.2em!important;height:1.2em!important;color:inherit!important}.education-item{color:var(--text-dark,#1a1a1a);margin-bottom:1rem;font-size:.95rem;line-height:1.6}.languages-list{flex-wrap:wrap;gap:1.5rem;display:flex}.language-item{color:var(--text-dark,#1a1a1a);margin-bottom:.3rem!important;margin-left:2rem!important;font-size:.95rem!important;line-height:1.4!important}.language-item small{margin-top:.2rem;font-size:.8em;font-style:italic;display:block}.experience-item{border-bottom:1px solid #0000001a;margin-bottom:2.5rem;padding-bottom:2rem}.language-toggle,.cv-length-toggle,.logo-toggle{white-space:nowrap;justify-content:center;align-items:center;gap:.5rem;display:inline-flex}.toggle-switch{cursor:pointer;-webkit-user-select:none;user-select:none;display:inline-block;position:relative}.toggle-switch input[type=checkbox]{opacity:0;width:0;height:0;position:absolute}.toggle-slider{background-color:#555;border-radius:26px;width:50px;height:26px;transition:background-color .3s;display:inline-block;position:relative}.toggle-slider:after{content:"";background-color:#fff;border-radius:50%;width:20px;height:20px;transition:transform .3s;position:absolute;top:3px;left:3px;box-shadow:0 2px 4px #0003}.toggle-switch input[type=checkbox]:checked+.toggle-slider{background-color:var(--accent-blue,#06c)}.toggle-switch input[type=checkbox]:checked+.toggle-slider:after{transform:translate(24px)}.toggle-switch input[type=checkbox]:focus+.toggle-slider{box-shadow:0 0 0 3px #06c3}.toggle-label-left,.toggle-label-right{color:#999;white-space:nowrap;justify-content:center;align-items:center;height:28px;font-size:.8rem;font-weight:500;transition:all .3s;display:flex}.flag-icon{border-radius:50%;justify-content:center;align-items:center;display:flex;overflow:hidden}.language-toggle:has(#langToggle:not(:checked)) .toggle-label-left,.cv-length-toggle:has(#lengthToggle:not(:checked)) .toggle-label-left,.logo-toggle:has(#logoToggle:not(:checked)) .toggle-label-left,.language-toggle:has(#langToggle:checked) .toggle-label-right,.cv-length-toggle:has(#lengthToggle:checked) .toggle-label-right,.logo-toggle:has(#logoToggle:checked) .toggle-label-right{color:#fff;opacity:1}.language-toggle:has(#langToggle:not(:checked)) .toggle-label-right,.cv-length-toggle:has(#lengthToggle:not(:checked)) .toggle-label-right,.logo-toggle:has(#logoToggle:not(:checked)) .toggle-label-right,.language-toggle:has(#langToggle:checked) .toggle-label-left,.cv-length-toggle:has(#lengthToggle:checked) .toggle-label-left,.logo-toggle:has(#logoToggle:checked) .toggle-label-left{opacity:.4}.experience-item,.award-item{border-bottom:2px solid var(--icon-border,#ddd);page-break-inside:avoid;gap:1.2rem;margin-bottom:2.5rem;padding-bottom:2rem;transition:gap .3s ease-in-out;display:flex;position:relative}.experience-item:last-child,.award-item:last-child{border-bottom:none;padding-bottom:0}.cv-paper:not(.show-icons) .experience-item,.cv-paper:not(.show-icons) .award-item{gap:0}.company-logo,.award-logo,.project-icon,.course-icon{flex-shrink:0;display:block}.company-logo img,.award-logo img,.project-icon img,.course-icon img{object-fit:contain;border:1px solid var(--icon-border,#ddd);background:0 0;border-radius:4px;width:80px;height:80px;padding:10px}.default-company-icon,.default-award-icon,.default-project-icon,.default-course-icon{border:1px solid var(--icon-border,#ddd);color:#999;background:0 0;border-radius:4px;justify-content:center;align-items:center;padding:10px;display:flex;width:80px!important;height:80px!important}.experience-content,.award-content{flex:1;min-width:0}.company-logo,.award-logo,.section-icon,.default-company-icon,.project-icon,.default-project-icon,.course-icon,.default-course-icon{opacity:1;width:auto;height:auto;transition:opacity .3s ease-in-out,transform .3s ease-in-out,width .3s ease-in-out,height .3s ease-in-out,margin .3s ease-in-out;overflow:hidden;transform:scale(1)}.cv-paper:not(.show-icons) .company-logo,.cv-paper:not(.show-icons) .award-logo,.cv-paper:not(.show-icons) .section-icon,.cv-paper:not(.show-icons) .default-company-icon,.cv-paper:not(.show-icons) .project-icon,.cv-paper:not(.show-icons) .default-project-icon,.cv-paper:not(.show-icons) .course-icon,.cv-paper:not(.show-icons) .default-course-icon{opacity:0;pointer-events:none;width:0;height:0;margin:0;padding:0;overflow:hidden;transform:scale(.8)}.show-icons .company-logo,.show-icons .award-logo,.show-icons .section-icon,.show-icons .default-company-icon,.show-icons .project-icon,.show-icons .default-project-icon,.show-icons .course-icon,.show-icons .default-course-icon{opacity:1;width:auto;height:auto;transform:scale(1)}@media (width<=768px){.logo-toggle{order:3}.toggle-label{font-size:.85rem}.toggle-slider{width:38px;height:20px}.toggle-slider:after{width:14px;height:14px}.toggle-switch input[type=checkbox]:checked+.toggle-slider:after{transform:translate(18px)}.company-logo img{width:40px;height:40px}}.has-tooltip{position:relative}.has-tooltip:before{content:attr(data-tooltip);color:#fff;white-space:nowrap;letter-spacing:.01em;opacity:0;visibility:hidden;pointer-events:none;z-index:1000;background:#000000d9;border-radius:6px;padding:4px 8px;font-size:11px;font-weight:600;line-height:1.3;transition:opacity .2s,transform .2s cubic-bezier(.16,1,.3,1),visibility .2s;position:absolute;transform:scale(.8);box-shadow:0 2px 8px #0000004d}.has-tooltip:hover:before{opacity:1;visibility:visible;transform:scale(1)}.has-tooltip:before{top:50%;left:calc(100% + 12px);transform:translateY(-50%)scale(.8)}.has-tooltip:hover:before{transform:translateY(-50%)scale(1)}.has-tooltip.tooltip-left:before{top:50%;left:auto;right:calc(100% + 12px);transform:translateY(-50%)scale(.8)}.has-tooltip.tooltip-left:hover:before{transform:translateY(-50%)scale(1)}.has-tooltip.tooltip-top:before{inset:auto auto calc(100% + 12px) 50%;transform:translate(-50%)scale(.8)}.has-tooltip.tooltip-top:hover:before{transform:translate(-50%)scale(1)}.has-tooltip.tooltip-bottom:before{inset:calc(100% + 12px) auto auto 50%;transform:translate(-50%)scale(.8)}.has-tooltip.tooltip-bottom:hover:before{transform:translate(-50%)scale(1)}@media (width<=900px){.action-btn.has-tooltip:before{inset:auto auto calc(100% + 8px) 50%;transform:translate(-50%)scale(.8)}.action-btn.has-tooltip:hover:before{transform:translate(-50%)scale(1)}.fixed-btn.has-tooltip:before,.color-theme-switcher.has-tooltip:before,.info-button.has-tooltip:before{inset:auto auto calc(100% + 8px) 50%;transform:translate(-50%)scale(.8)}.fixed-btn.has-tooltip:hover:before,.color-theme-switcher.has-tooltip:hover:before,.info-button.has-tooltip:hover:before{transform:translate(-50%)scale(1)}.back-to-top.has-tooltip.tooltip-left:before{inset:50% calc(100% + 8px) auto auto;transform:translateY(-50%)scale(.8)}.back-to-top.has-tooltip.tooltip-left:hover:before{transform:translateY(-50%)scale(1)}}@media (width<=483px){.back-to-top.has-tooltip.tooltip-left:before{top:50%;right:calc(100% + 8px);transform:translateY(-50%)scale(.8)}.back-to-top.has-tooltip.tooltip-left:hover:before{transform:translateY(-50%)scale(1)}}@media (prefers-reduced-motion:reduce){.has-tooltip:before{transition:opacity .1s,visibility .1s;transform:scale(1)!important}.has-tooltip:hover:before{transform:scale(1)!important}.has-tooltip.tooltip-left:before,.has-tooltip.tooltip-left:hover:before{transform:translateY(-50%)scale(1)!important}.has-tooltip.tooltip-top:before,.has-tooltip.tooltip-top:hover:before{transform:translate(-50%)scale(1)!important}}@media (hover:none) and (pointer:coarse){.has-tooltip:before{display:none}}[data-color-theme=dark] .has-tooltip:before{background:#282828f2;box-shadow:0 2px 12px #00000080}[data-color-theme=light] .has-tooltip:before{background:#000000d9}.hamburger-btn{color:#fff;cursor:pointer;background:0 0;border:none;border-radius:4px;justify-content:center;align-items:center;margin:0 .5rem;padding:.5rem;transition:background-color .2s;display:flex;position:relative}.hamburger-btn:hover{background-color:#ffffff1a}.hamburger-btn:active{background-color:#fff3}.navigation-menu{z-index:1000;pointer-events:none;opacity:0;background:#fff;width:280px;max-height:0;transition:max-height .5s cubic-bezier(.4,0,.2,1),opacity .3s;position:fixed;top:50px;left:0;overflow-y:auto;box-shadow:2px 0 10px #00000026}.hamburger-btn:hover~.navigation-menu,.hamburger-btn:focus~.navigation-menu,.navigation-menu:hover,.navigation-menu.menu-hover,.navigation-menu.menu-open{pointer-events:auto;opacity:1;max-height:calc(100vh - 60px)}.menu-content{padding:1rem 0}.menu-item{color:var(--text-dark,#1a1a1a);border-left:3px solid #0000;align-items:center;gap:1rem;padding:.875rem 1.5rem;font-size:.95rem;font-weight:500;text-decoration:none;transition:background-color .2s,color .2s;display:flex}.menu-item:hover{color:var(--accent-blue,#06c);border-left-color:var(--accent-blue,#06c);background-color:#0066cc14;text-decoration:none}.menu-item iconify-icon{color:var(--text-gray,#333);flex-shrink:0;transition:color .2s}.menu-item:hover iconify-icon{color:var(--accent-blue,#06c)}.menu-item-submenu{border-bottom:1px solid #0000001a;padding:0 0 1rem;position:relative}.menu-item.has-submenu{justify-content:space-between;position:relative}.submenu-arrow{margin-left:auto;transition:transform .2s}.menu-item-submenu:hover .submenu-arrow{transform:translate(3px)}.submenu-content{opacity:0;visibility:hidden;z-index:1000;background:#fff;border-radius:8px;min-width:250px;max-width:300px;max-height:calc(100vh - 100px);padding:.5rem 0;transition:all .3s;position:fixed;left:232px;overflow-y:auto;transform:translate(-3px);box-shadow:2px 2px 10px #00000026}.menu-item-submenu:hover .submenu-content,.submenu-content:hover{opacity:1;visibility:visible;transform:translate(0)}.menu-item-submenu.submenu-open .submenu-arrow{transform:translate(3px)}.menu-item-submenu.submenu-open .submenu-content{opacity:1;visibility:visible;transform:translate(0)}.submenu-content .menu-item{border-left:3px solid #0000;border-radius:0;padding:.875rem 1.5rem;font-size:.9rem}.submenu-content .menu-item:first-child{border-top-left-radius:8px;border-top-right-radius:8px}.submenu-content .menu-item:last-child{border-bottom-right-radius:8px;border-bottom-left-radius:8px}.menu-section-wrapper{border-bottom:1px solid #0000001a;padding:.5rem 1.5rem 1rem}.menu-content>:last-child,.menu-content>div:last-child{border-bottom:none!important}.menu-controls-section,.menu-actions-section{border-bottom:1px solid #0000001a;padding:.5rem 1.5rem 1rem;display:block}.menu-item-header{color:var(--text-dark,#1a1a1a);text-transform:uppercase;letter-spacing:.5px;cursor:default;align-items:center;gap:1rem;padding:.875rem 0;font-size:.85rem;font-weight:700;display:flex}.menu-item-header:hover{color:var(--text-dark,#1a1a1a)!important;background-color:#0000!important;border-left-color:#0000!important}.menu-item-header iconify-icon{color:var(--text-gray,#333);flex-shrink:0}.menu-item-header:hover iconify-icon{color:var(--text-gray,#333)!important}.menu-item-header span{flex:1}.menu-control-item{justify-content:space-between;align-items:center;padding:.75rem 0;display:flex}.menu-control-label{color:var(--text-dark,#1a1a1a);align-items:center;gap:.75rem;font-size:.9rem;font-weight:500;display:flex}.menu-control-label iconify-icon{color:var(--text-gray,#333)}.menu-action-btn{color:var(--text-dark,#1a1a1a);cursor:pointer;background:#00000008;border:none;border-radius:8px;justify-content:center;align-items:center;gap:1rem;width:100%;margin:.25rem 0;padding:.875rem 1rem;font-size:.9rem;font-weight:500;text-decoration:none;transition:all .2s;display:flex}.menu-action-btn:hover{color:var(--accent-blue,#06c);background:#0066cc14;text-decoration:none}.menu-action-btn iconify-icon{color:var(--text-gray,#333);flex-shrink:0;transition:color .2s}.menu-action-btn:hover iconify-icon{color:var(--accent-blue,#06c)}.menu-pdf-btn:hover,.menu-pdf-btn.pdf-hover-sync{color:#e74c3c!important;background:#fff!important}.menu-pdf-btn:hover iconify-icon,.menu-pdf-btn.pdf-hover-sync iconify-icon{color:#e74c3c!important}.menu-print-btn:hover,.menu-print-btn.print-hover-sync{color:#27ae60!important;background:#fff!important}.menu-print-btn:hover iconify-icon,.menu-print-btn.print-hover-sync iconify-icon{color:#27ae60!important}.section-icon{vertical-align:middle;color:#7d7d7d;margin-right:.5rem}#experience .section-title,#awards .section-title,#courses .section-title,#projects .section-title{margin-bottom:40px!important}html{scroll-behavior:smooth;scroll-padding-top:70px}@media (width<=768px){.navigation-menu{width:240px}.menu-item{padding:.75rem 1rem;font-size:.9rem}.site-title{justify-content:space-between;width:100%}}@media print{.navigation-menu,.hamburger-btn{display:none!important}}[data-color-theme=dark] .navigation-menu,[data-color-theme=dark] .navigation-menu .submenu-content{--text-dark:#1a1a1a;--text-gray:#333}@media (prefers-color-scheme:dark){[data-color-theme=auto] .navigation-menu,[data-color-theme=auto] .navigation-menu .submenu-content{--text-dark:#1a1a1a;--text-gray:#333}}.action-bar,.navigation-menu{transition:transform .3s ease-in-out}.action-bar.header-hidden,.navigation-menu.header-hidden{transform:translateY(-100%)}.back-to-top{background:var(--black-bar,#2b2b2b);color:#fff;cursor:pointer;z-index:99;opacity:.2;border:none;border-radius:50%;justify-content:center;align-items:center;width:50px;height:50px;transition:all .3s;display:flex;position:fixed;bottom:2rem;right:2rem;box-shadow:0 4px 12px #0000004d}.back-to-top:hover{opacity:1;background:#27ae60;transform:translateY(-3px);box-shadow:0 6px 16px #0006}.back-to-top.at-bottom{opacity:1;background:#27ae60}.back-to-top:active{transform:translateY(-1px);box-shadow:0 3px 10px #0000004d}@media (width<=768px){.back-to-top{width:45px;height:45px;bottom:1.5rem;right:1.5rem}}.info-button{background:var(--black-bar,#2b2b2b);color:#fff;cursor:pointer;z-index:99;opacity:.6;border:none;border-radius:50%;justify-content:center;align-items:center;width:50px;height:50px;transition:all .3s;display:flex;position:fixed;bottom:2rem;left:2rem;box-shadow:0 4px 12px #0000004d}.info-button:hover{opacity:1;background:#3498db;transform:translateY(-3px);box-shadow:0 6px 16px #0006}.info-button.at-bottom{opacity:1;background:#3498db}.info-button:active{transform:translateY(-1px);box-shadow:0 3px 10px #0000004d}.download-btn iconify-icon,.print-friendly-btn iconify-icon,.fixed-btn.contact-btn iconify-icon,.shortcuts-btn iconify-icon,.info-button iconify-icon,.back-to-top iconify-icon,.color-theme-switcher iconify-icon{width:24px;height:24px;font-size:24px}.is-mobile-device .shortcuts-btn,.is-mobile-device .zoom-toggle-btn,.is-mobile-device .zoom-control{display:none!important}@media (width<=900px){.zoom-toggle-btn,.zoom-control{display:none!important}.download-btn,.print-friendly-btn,.fixed-btn.contact-btn,.shortcuts-btn,.info-button{width:clamp(36px,2.7vw + 25.7px,50px)!important;height:clamp(36px,2.7vw + 25.7px,50px)!important;position:fixed!important;bottom:1.5rem!important;left:auto!important;right:auto!important;transform:none!important}.back-to-top{width:clamp(36px,2.7vw + 25.7px,50px)!important;height:clamp(36px,2.7vw + 25.7px,50px)!important}.download-btn iconify-icon,.print-friendly-btn iconify-icon,.fixed-btn.contact-btn iconify-icon,.shortcuts-btn iconify-icon,.info-button iconify-icon,.back-to-top iconify-icon,.color-theme-switcher iconify-icon{width:clamp(18px,1.15vw + 13.6px,24px)!important;min-width:0!important;max-width:clamp(18px,1.15vw + 13.6px,24px)!important;height:clamp(18px,1.15vw + 13.6px,24px)!important;font-size:clamp(18px,1.15vw + 13.6px,24px)!important}.download-btn{opacity:1!important;background:#cd6060!important}.print-friendly-btn{opacity:1!important;background:#fff!important}.print-friendly-btn iconify-icon{color:#27ae60!important}.fixed-btn.contact-btn{opacity:1!important;background:#3498db!important}.shortcuts-btn{opacity:1!important;background:#f39c12!important}.info-button{opacity:1!important;background:#3498db!important}.back-to-top{opacity:1!important;background:#27ae60!important}.download-btn{left:calc(50% + -1*clamp(138px,11.7308vw + 93.4231px,199px))!important}.print-friendly-btn{left:calc(50% + -1*clamp(98px,8.26923vw + 66.5769px,141px))!important}.fixed-btn.contact-btn{left:calc(50% + -1*clamp(58px,4.80769vw + 39.7308px,83px))!important}.shortcuts-btn{left:calc(50% + -1*clamp(18px,1.34615vw + 12.8846px,25px))!important}.info-button{left:calc(50% + clamp(62px,5.57692vw + 40.8077px,91px))!important}.back-to-top{display:flex!important;position:fixed!important;bottom:1.5rem!important;left:calc(50% + clamp(102px,9.03846vw + 67.6539px,149px))!important;right:auto!important}.is-mobile-device .download-btn{left:calc(50% + -1*clamp(118px,10vw + 80px,170px))!important}.is-mobile-device .print-friendly-btn{left:calc(50% + -1*clamp(78px,6.53846vw + 53.1538px,112px))!important}.is-mobile-device .fixed-btn.contact-btn{left:calc(50% + -1*clamp(38px,3.07692vw + 26.3077px,54px))!important}.is-mobile-device .info-button{left:calc(50% + clamp(42px,3.84615vw + 27.3846px,62px))!important}.is-mobile-device .back-to-top{left:calc(50% + clamp(82px,7.30769vw + 54.2308px,120px))!important}.back-to-top:hover{opacity:1!important}.download-btn:hover,.download-btn.pdf-hover-sync{background:#cd6060!important;transform:translateY(-3px)!important;box-shadow:0 6px 16px #0006!important}.print-friendly-btn:hover,.print-friendly-btn.print-hover-sync{background:#fff!important;transform:translateY(-3px)!important;box-shadow:0 6px 16px #0006!important}.fixed-btn.contact-btn:hover{background:#3498db!important;transform:translateY(-3px)!important;box-shadow:0 6px 16px #0006!important}.shortcuts-btn:hover{background:#f39c12!important;transform:translateY(-3px)!important;box-shadow:0 6px 16px #0006!important}.info-button:hover{background:#3498db!important;transform:translateY(-3px)!important;box-shadow:0 6px 16px #0006!important}.back-to-top:hover{background:#27ae60!important;transform:translateY(-3px)!important;box-shadow:0 6px 16px #0006!important}.download-btn.at-bottom{opacity:1!important;background:#cd6060!important;transform:none!important}.print-friendly-btn.at-bottom{opacity:1!important;background:#fff!important;transform:none!important}.fixed-btn.contact-btn.at-bottom{opacity:1!important;background:#3498db!important;transform:none!important}.shortcuts-btn.at-bottom{opacity:1!important;background:#f39c12!important;transform:none!important}.info-button.at-bottom{opacity:1!important;background:#3498db!important;transform:none!important}.back-to-top.at-bottom{opacity:1!important;background:#27ae60!important;transform:none!important}.download-btn.footer-hovered,.print-friendly-btn.footer-hovered,.fixed-btn.contact-btn.footer-hovered,.shortcuts-btn.footer-hovered,.info-button.footer-hovered,.back-to-top.footer-hovered,.color-theme-switcher.footer-hovered{opacity:.2!important;pointer-events:none!important}.action-bar.header-hidden,.navigation-menu.header-hidden{transform:translateY(0)!important}footer.no-print{transition:all .3s;position:relative;z-index:1!important;padding-bottom:100px!important}footer.no-print.at-bottom{padding-bottom:110px!important}footer.no-print.at-bottom p,footer.no-print.at-bottom a{transition:all .3s;font-size:1.2em!important;font-weight:500!important}}.zoom-toggle-btn{background:var(--black-bar,#2b2b2b);color:#fff;cursor:pointer;z-index:999;opacity:.6;border:none;border-radius:50%;justify-content:center;align-items:center;width:50px;height:50px;transition:all .3s;display:flex;position:fixed;bottom:10rem;left:2rem;box-shadow:0 4px 12px #0000004d}.zoom-toggle-btn:hover{opacity:1;background:#5c59b6;transform:translateY(-3px);box-shadow:0 6px 16px #0006}.zoom-toggle-btn.at-bottom{opacity:1;background:#5c59b6}.shortcuts-btn{background:var(--black-bar,#2b2b2b);color:#fff;cursor:pointer;z-index:99;opacity:.6;border:none;border-radius:50%;justify-content:center;align-items:center;width:50px;height:50px;transition:all .3s;display:flex;position:fixed;bottom:6rem;left:2rem;box-shadow:0 4px 12px #0000004d}.shortcuts-btn:hover{opacity:1;background:#f39c12;transform:translateY(-3px);box-shadow:0 6px 16px #0006}.shortcuts-btn.at-bottom{opacity:1;background:#f39c12}.shortcuts-btn:active{transform:translateY(-1px)}.print-friendly-btn{background:var(--black-bar,#2b2b2b);color:#fff;cursor:pointer;z-index:999;opacity:.6;border:none;border-radius:50%;justify-content:center;align-items:center;width:50px;height:50px;transition:all .3s;display:flex;position:fixed;bottom:22rem;left:2rem;box-shadow:0 4px 12px #0000004d}.print-friendly-btn iconify-icon{color:#fff}.print-friendly-btn:hover,.print-friendly-btn.print-hover-sync{opacity:1;color:#27ae60;transform:translateY(-3px);box-shadow:0 6px 16px #0006;background:#fff!important}.print-friendly-btn:hover iconify-icon,.print-friendly-btn.print-hover-sync iconify-icon{color:#27ae60}.print-friendly-btn.at-bottom{opacity:1;color:#27ae60;background:#fff!important}.print-friendly-btn.at-bottom iconify-icon{color:#27ae60}.download-btn{background:var(--black-bar,#2b2b2b);color:#fff;cursor:pointer;z-index:999;opacity:.6;background:var(--black-bar,#2b2b2b);opacity:.6;border:none;border-radius:50%;justify-content:center;align-items:center;width:50px;height:50px;transition:all .3s;display:flex;position:fixed;bottom:26rem;left:2rem;box-shadow:0 4px 12px #0000004d}.download-btn:hover,.download-btn.pdf-hover-sync{opacity:1;transform:translateY(-3px);box-shadow:0 6px 16px #0006;background:#cd6060!important}.download-btn iconify-icon{filter:brightness(0)invert();transition:filter .3s}.download-btn:hover iconify-icon{filter:brightness(0)invert()}.download-btn.at-bottom{opacity:1;background:#cd6060!important}ninja-keys{--ninja-font-family:"Quicksand",-apple-system,BlinkMacSystemFont,sans-serif;--ninja-accent-color:#667eea;--ninja-z-index:10000;--ninja-width:640px;--ninja-backdrop-filter:blur(8px);--ninja-modal-background:#fffffff2;--ninja-modal-shadow:0 16px 70px #0003;--ninja-text-color:#1a1a1a;--ninja-secondary-text-color:#666;--ninja-actions-background:#f5f5f5;--ninja-selected-background:#667eea;--ninja-selected-text-color:white;--ninja-key-background:#e0e0e0;--ninja-key-text-color:#333;--ninja-footer-background:#f9f9f9;--ninja-placeholder-color:#999}[data-color-theme=dark] ninja-keys{--ninja-modal-background:#282828f2;--ninja-text-color:#e0e0e0;--ninja-secondary-text-color:#999;--ninja-actions-background:#2a2a2a;--ninja-key-background:#444;--ninja-key-text-color:#e0e0e0;--ninja-footer-background:#2a2a2a;--ninja-placeholder-color:#777}.shortcut-highlight{background:linear-gradient(135deg,#667eea1a 0%,#764ba21a 100%);border-radius:8px;margin:-.5rem;padding:.5rem}.info-modal{background:0 0;border:none;border-radius:24px;width:calc(100% - 2rem);max-width:420px;max-height:fit-content;margin:auto;padding:0;position:fixed;inset:0}.info-modal::backdrop{-webkit-backdrop-filter:blur(10px);background:#000000b3}.info-modal[open]{animation:.3s modalFadeIn}@keyframes modalFadeIn{0%{opacity:0;transform:scale(.9)translateY(20px)}to{opacity:1;transform:scale(1)translateY(0)}}@keyframes modalFadeInMobile{0%{opacity:0;transform:translate(-50%,-50%)scale(.9)}to{opacity:1;transform:translate(-50%,-50%)scale(1)}}.info-modal-content{-webkit-backdrop-filter:blur(20px);background:linear-gradient(135deg,#fffffff2 0%,#ffffffe6 100%);border:1px solid #fffc;border-radius:24px;width:100%;padding:2.5rem;position:relative;box-shadow:0 20px 60px #0000004d,0 0 100px #27ae601a}.info-modal-close{cursor:pointer;width:40px;height:40px;color:var(--text-primary,#1a1a1a);z-index:10;background:#0000000d;border:none;border-radius:50%;justify-content:center;align-items:center;transition:all .2s;display:flex;position:absolute;top:1rem;right:1rem}.info-modal-close:hover{background:#0000001a;transform:rotate(90deg)}.info-modal-header{text-align:center;margin-bottom:2rem}.info-modal-header h2{color:var(--text-primary,#1a1a1a);margin:0 0 1.5rem;font-size:1.5rem;font-weight:600}.info-modal-cv-title{color:#f39c12;letter-spacing:.05em;justify-content:center;align-items:center;gap:.5rem;margin-bottom:0;font-size:1.5rem;font-weight:700;display:flex}#info-modal .info-modal-cv-title{color:#27ae60}.info-modal-photo{object-fit:cover;width:40px;height:53px;box-shadow:none;border:none;border-radius:4px}.photo-bracket-wrapper{align-items:center;padding:0 22px;display:inline-flex;position:relative}.photo-bracket-wrapper:before{content:"{";color:#27ae60;font-size:2rem;font-weight:700;line-height:1;position:absolute;top:8px;left:2px}.photo-bracket-wrapper:after{content:"}";color:#27ae60;font-size:2rem;font-weight:700;line-height:1;position:absolute;top:8px;right:2px}.info-modal-body{color:#333}.info-modal-description{color:#444;margin-bottom:2rem;font-size:1rem;line-height:1.6}.info-modal-description strong{color:#27ae60;font-weight:600}.info-modal-tech{grid-template-columns:repeat(2,1fr);gap:1rem;margin-bottom:2rem;display:grid}.info-tech-item{background:#27ae600d;border:1px solid #27ae601a;border-radius:12px;justify-content:center;align-items:center;gap:.75rem;padding:.75rem;transition:all .3s;display:flex}.info-tech-item:hover{background:#27ae601a;transform:translateY(-2px);box-shadow:0 4px 12px #27ae6033}.info-tech-item iconify-icon{color:#27ae60;flex-shrink:0}.info-tech-item span{color:#333;font-size:.9rem;font-weight:500}.info-modal-github{color:#fff;background:linear-gradient(135deg,#27ae60 0%,#229954 100%);border-radius:12px;justify-content:center;align-items:center;gap:.75rem;padding:1rem 1.5rem;font-size:1rem;font-weight:600;text-decoration:none;transition:all .3s;display:flex;box-shadow:0 4px 15px #27ae604d}.info-modal-github:hover{background:linear-gradient(135deg,#229954 0%,#27ae60 100%);transform:translateY(-2px);box-shadow:0 8px 25px #27ae6066}.info-modal-github:active{transform:translateY(0);box-shadow:0 4px 15px #27ae604d}.info-modal-github-subtext{text-align:center;color:#666;margin-top:1.5rem;margin-bottom:1rem;font-size:.9rem;font-style:italic}@media (width<=768px){.info-modal{width:calc(100vw - 2rem)!important;max-width:calc(100vw - 2rem)!important;max-height:calc(100vh - 2rem)!important;margin:0!important;position:fixed!important;inset:50% auto auto 50%!important;transform:translate(-50%,-50%)!important}.info-modal[open]{animation:.3s modalFadeInMobile}.info-modal-content{max-width:100%;max-height:calc(100vh - 2rem);padding:1.5rem 1rem;overflow-y:auto}.info-modal-close{width:32px;height:32px;top:.5rem;right:.5rem}.info-modal-close iconify-icon{width:20px;height:20px}.info-modal-header{margin-bottom:1.5rem}.info-modal-header h2{margin-bottom:1rem;font-size:1.05rem}.info-modal-cv-title{font-size:.95rem}.info-modal-photo{width:30px;height:40px}.photo-bracket-wrapper{padding:0 18px}.photo-bracket-wrapper:before,.photo-bracket-wrapper:after{font-size:1.5rem;top:5px}.info-modal-description{margin-bottom:1.5rem;font-size:.85rem;line-height:1.5}.info-modal-tech{grid-template-columns:1fr;gap:.75rem;margin-bottom:1.5rem}.info-tech-item{gap:.6rem;padding:.6rem}.info-tech-item iconify-icon{width:24px;height:24px}.info-tech-item span{font-size:.8rem}.info-modal-github-subtext{margin-top:1rem;margin-bottom:.75rem;font-size:.8rem}.info-modal-github{gap:.5rem;padding:.75rem 1.25rem;font-size:.875rem}.info-modal-github iconify-icon{width:20px;height:20px}}#shortcuts-modal{max-width:900px;max-height:80vh}.keyboard-icon-wrapper{align-items:center;padding:0 22px;display:inline-flex;position:relative}.keyboard-icon-wrapper:before{content:"{";color:#575757;font-size:2rem;font-weight:700;line-height:1;position:absolute;top:-3px;left:2px}.keyboard-icon-wrapper:after{content:"}";color:#575757;font-size:2rem;font-weight:700;line-height:1;position:absolute;top:-3px;right:2px}.keyboard-icon-wrapper iconify-icon{color:#f39c12;position:relative;top:1px}#shortcuts-modal .info-modal-cv-title{margin-bottom:.5rem}#shortcuts-modal .info-modal-body{grid-template-columns:1fr 1fr;gap:1.2rem 1.5rem;margin-top:1.5rem;display:grid}.shortcuts-section{background:#f8f9fa;border:1px solid #e1e4e8;border-radius:8px;margin-top:0;padding:1rem;box-shadow:0 1px 3px #0000000d}.shortcuts-section:first-of-type{margin-top:0}.shortcuts-section-title{color:#827a6e;border-bottom:2px solid #827a6e33;align-items:center;gap:.5rem;margin-bottom:.75rem;padding-bottom:.5rem;font-size:1.05rem;font-weight:600;display:flex}.shortcuts-section-title iconify-icon{color:#f39c12}.shortcuts-list{flex-direction:column;gap:.5rem;display:flex}.shortcut-item{justify-content:space-between;align-items:center;gap:1rem;padding:.5rem 0;display:flex}.shortcut-keys{flex-wrap:wrap;align-items:center;gap:.4rem;display:flex}.shortcut-keys kbd{white-space:nowrap;text-align:center;color:#3498db;background:#3498db14;border:1px solid #3498db59;border-radius:6px;justify-content:center;align-items:center;gap:.2rem;min-width:2rem;padding:.3rem .6rem;font-family:Monaco,Courier New,monospace;font-size:.75rem;font-weight:600;transition:all .2s;display:inline-flex;box-shadow:0 2px 4px #3498db1f,inset 0 -1px #3498db40}.shortcut-keys kbd iconify-icon{color:inherit;vertical-align:middle;display:inline-flex}.shortcut-item:hover .shortcut-keys kbd{background:#3498db26;border-color:#3498db80;box-shadow:0 2px 6px #3498db40}.shortcut-desc{color:var(--text-gray,#333);flex:1;font-size:.95rem;line-height:1.4}@media (width<=768px){#shortcuts-modal{width:calc(100vw - 2rem)!important;max-width:calc(100vw - 2rem)!important;max-height:calc(100vh - 2rem)!important}#shortcuts-modal .info-modal-body{grid-template-columns:1fr;gap:1.5rem}}@media (width>=769px) and (width<=1024px){#shortcuts-modal{max-width:700px}#shortcuts-modal .info-modal-body{grid-template-columns:1fr 1fr;gap:1.2rem 1.5rem}.shortcuts-section-title{font-size:1rem}.shortcut-item{flex-direction:column;align-items:flex-start;gap:.35rem}.shortcut-keys kbd{padding:.2rem .4rem;font-size:.7rem}.shortcut-desc{font-size:.9rem}}.pdf-download-modal{width:calc(100% - 2rem);max-width:800px}.pdf-modal-subtitle{color:var(--text-gray,#333);margin-top:.5rem;font-size:.95rem;font-weight:400}.pdf-options-grid{grid-template-columns:repeat(3,1fr);gap:32px;margin:2rem 0 1.5rem;display:grid}.pdf-option-card{cursor:pointer;background:#fff;border:2px solid #0000;border-radius:12px;flex-direction:column;gap:10px;padding:12px;transition:all .25s;display:flex;position:relative}.pdf-option-card:hover{border-color:#e0e0e0;transform:translateY(-2px);box-shadow:0 4px 12px #0000001a}.pdf-option-card:focus{outline-offset:2px;outline:2px solid #0000}.pdf-option-recommended:focus{outline:none}.pdf-option-card.selected:not(.pdf-option-recommended){background:#fff5f5;border-color:#ef4444;box-shadow:0 6px 16px #ef444433}.pdf-thumbnail{background:#fff;border:1px solid #e0e0e0;border-radius:8px;flex-direction:column;gap:10px;height:220px;padding:12px;display:flex;position:relative;overflow:hidden}.pdf-thumbnail .skeleton-block{background:linear-gradient(90deg,#f0f0f0 25%,#e8e8e8 50%,#f0f0f0 75%) 0 0/200% 100%;border-radius:4px;animation:1.8s ease-in-out infinite skeleton-shimmer}.custom-placeholder{color:#999;text-align:center;flex-direction:column;justify-content:center;align-items:center;height:100%;display:flex}.custom-placeholder iconify-icon{opacity:.5;margin-bottom:12px}.custom-placeholder p{color:#666;margin:0;font-size:.9rem;font-weight:500}.thumbnail-badge{color:#fff;letter-spacing:.5px;text-transform:uppercase;background:#000000bf;border-radius:4px;padding:4px 8px;font-size:.7rem;font-weight:600;position:absolute;top:8px;right:8px}.thumbnail-badge.badge-recommended{background:linear-gradient(135deg,#f39c12 0%,#e67e22 100%);box-shadow:0 2px 8px #f39c124d}.recommended-ribbon{color:#fff;letter-spacing:.5px;text-transform:uppercase;z-index:2;background:linear-gradient(135deg,#f39c12 0%,#e67e22 100%);border-radius:0 0 8px 8px;padding:3px 12px;font-size:.65rem;font-weight:700;position:absolute;top:-4px;left:50%;transform:translate(-50%);box-shadow:0 2px 8px #f39c124d}.recommended-badge{margin-left:.25rem;font-size:1rem;display:inline-block}.pdf-option-recommended{z-index:1;position:relative;overflow:visible;transform:scale(1.12);box-shadow:0 2px 8px #f39c1214;border:2px solid #f39c1226!important}.pdf-option-recommended:hover{transform:scale(1.12)translateY(-2px);box-shadow:0 4px 16px #f39c1226}.pdf-option-recommended.selected{background:#fffbf5!important;border:2px solid #f39c12!important;box-shadow:0 6px 16px #f39c124d!important}.pdf-option-info{text-align:center}.pdf-option-info h3{color:var(--text-dark,#1a1a1a);margin:0 0 4px;font-size:1.1rem;font-weight:600}.pdf-option-info p{color:var(--text-gray,#333);margin:0;font-size:.875rem;line-height:1.4}.pdf-option-badge{opacity:0;color:#4caf50;transition:all .25s;position:absolute;top:8px;left:8px;transform:scale(.8)}.pdf-option-card.selected .pdf-option-badge{opacity:1;transform:scale(1)}.pdf-modal-footer{border-top:1px solid #e0e0e0;justify-content:center;margin-top:.5rem;padding-top:1rem;display:flex}.pdf-download-btn{cursor:pointer;border:none;border-radius:8px;align-items:center;gap:8px;padding:12px 32px;font-family:inherit;font-size:1rem;font-weight:600;transition:all .25s;display:inline-flex}.pdf-download-btn iconify-icon{flex-shrink:0}.pdf-download-btn:disabled{color:#999;cursor:not-allowed;opacity:.6;background:#e0e0e0}.pdf-download-btn:not(:disabled){color:#fff;background:#ef4444}.pdf-download-btn:not(:disabled):hover{background:#dc2626;transform:translateY(-1px);box-shadow:0 4px 12px #ef44444d}.pdf-download-btn:not(:disabled):active{transform:translateY(0)}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}@media (width>=480px) and (width<=767px){.pdf-options-grid{grid-template-columns:repeat(2,1fr);gap:16px}.pdf-option-card[data-cv-format=custom]{grid-column:1/-1}.pdf-thumbnail{height:220px}}@media (width<=768px){.pdf-download-modal{width:calc(100vw - 2rem)!important;max-width:calc(100vw - 2rem)!important;max-height:calc(100vh - 2rem)!important}.info-modal-content{padding:1.5rem 1rem}.pdf-modal-subtitle{display:none}.pdf-download-modal .info-modal-header{margin-bottom:1rem}.pdf-download-modal .info-modal-header h2{margin-bottom:0;font-size:1.25rem}.pdf-options-grid{flex-direction:column;gap:10px;margin:1rem 0;display:flex}.pdf-option-recommended{transform:none}.pdf-option-recommended:hover{transform:translateY(-2px)}.pdf-option-card{flex-direction:row;align-items:center;gap:12px;padding:12px}.pdf-thumbnail{display:none}.pdf-option-card:before{content:attr(data-cv-format);color:#666;text-transform:uppercase;background:#f5f5f5;border-radius:8px;flex-shrink:0;justify-content:center;align-items:center;width:50px;height:50px;font-size:.65rem;font-weight:700;display:flex}.pdf-option-card[data-cv-format=short]:before{content:"4\a PAGES";white-space:pre;line-height:1.3}.pdf-option-card[data-cv-format=default]:before{content:"5\a PAGES";white-space:pre;color:#f39c12;background:#fff8e6;line-height:1.3}.pdf-option-card[data-cv-format=long]:before{content:"9\a PAGES";white-space:pre;line-height:1.3}.pdf-option-info{text-align:left;flex:1}.pdf-option-info h3{margin-bottom:2px;font-size:.9rem}.pdf-option-info p{font-size:.75rem}.pdf-option-badge{margin-left:auto;position:static}.pdf-download-btn{justify-content:center;width:100%;padding:10px 20px;font-size:.9rem}.pdf-modal-footer{margin-top:.5rem;padding-top:.75rem}.info-modal-header h2{color:#000;opacity:1}.info-modal-close{color:#000;background:#00000014}.info-modal-close:hover{background:#00000026}}@media (prefers-reduced-motion:reduce){.pdf-thumbnail .skeleton-block{background:#e8e8e8;animation:none}.pdf-option-card,.pdf-option-badge,.pdf-download-btn{transition:none}.pdf-loading-overlay,.pdf-loading-spinner{animation:none}}.pdf-loading-overlay{-webkit-backdrop-filter:blur(8px);z-index:100;background:#fffffff2;border-radius:24px;flex-direction:column;justify-content:center;align-items:center;width:100%;height:100%;animation:.3s overlayFadeIn;display:none;position:absolute;top:0;left:0}.pdf-loading-overlay.active{display:flex}@keyframes overlayFadeIn{0%{opacity:0}to{opacity:1}}.pdf-loading-content{text-align:center;max-width:300px;padding:2rem}.pdf-loading-spinner{border:4px solid #ef444433;border-top-color:#ef4444;border-radius:50%;width:64px;height:64px;margin:0 auto 1.5rem;animation:1s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}.pdf-loading-title{color:var(--text-primary,#1a1a1a);margin:0 0 .5rem;font-size:1.25rem;font-weight:600}.pdf-loading-message{color:var(--text-gray,#333);margin:0 0 .5rem;font-size:.95rem;line-height:1.5}.pdf-loading-estimate{color:#999;margin:1.5rem 0 0;font-size:.85rem;font-style:italic}.info-modal-content.loading-active>:not(.pdf-loading-overlay){filter:blur(3px);pointer-events:none}[data-color-theme=dark] .pdf-download-modal .pdf-modal-subtitle{color:#333!important}[data-color-theme=dark] .pdf-download-modal .pdf-option-info h3{color:#1a1a1a!important}[data-color-theme=dark] .pdf-download-modal .pdf-option-info p{color:#333!important}[data-color-theme=dark] .pdf-download-modal .custom-placeholder p{color:#666!important}[data-color-theme=dark] .pdf-download-modal .pdf-loading-title{color:#1a1a1a!important}[data-color-theme=dark] .pdf-download-modal .pdf-loading-message{color:#333!important}[data-color-theme=dark] .pdf-download-modal .pdf-loading-estimate{color:#999!important}.error-toast,.success-toast,.toast{z-index:10000;-webkit-backdrop-filter:blur(10px);border-radius:12px;align-items:center;gap:.75rem;min-width:320px;max-width:420px;padding:1rem 1.25rem;font-size:.95rem;line-height:1.5;animation:.3s toastSlideIn;display:none;position:fixed;bottom:2rem;right:2rem;box-shadow:0 8px 24px #00000026,0 2px 6px #0000001a}.error-toast.show,.success-toast.show,.toast.show{animation:5s forwards toastLifecycle;display:flex}.error-toast{color:#fff;background:linear-gradient(135deg,#dc3545f2 0%,#c82333f2 100%);border-left:4px solid #fff}.success-toast{color:#fff;background:linear-gradient(135deg,#28a745f2 0%,#198754f2 100%);border-left:4px solid #fff}.info-toast{color:#fff;background:linear-gradient(135deg,#0d6efdf2 0%,#0a58caf2 100%);border-left:4px solid #fff}.warning-toast{color:#333;background:linear-gradient(135deg,#ffc107f2 0%,#ffa726f2 100%);border-left:4px solid #333}.toast-icon,.error-icon,.success-icon,.info-icon{flex-shrink:0;font-size:1.5rem;line-height:1}.toast-content{flex-direction:column;flex:1;gap:.25rem;display:flex}.toast-title{margin:0;font-size:1rem;font-weight:600}.toast-message{opacity:.95;margin:0;font-size:.875rem}.error-close,.toast-close{color:inherit;cursor:pointer;background:#fff3;border:none;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;width:28px;height:28px;font-size:1.5rem;font-weight:300;line-height:1;transition:all .2s;display:flex}.error-close:hover,.toast-close:hover{background:#ffffff4d;transform:rotate(90deg)}.toast-progress{background:#ffffff4d;border-radius:0 0 12px 12px;width:100%;height:3px;position:absolute;bottom:0;left:0;overflow:hidden}.toast-progress-bar{background:#fffc;border-radius:0 0 12px 12px;height:100%;animation:5s linear forwards progressShrink}@keyframes toastSlideIn{0%{opacity:0;transform:translate(100%)translateY(0)}to{opacity:1;transform:translate(0)translateY(0)}}@keyframes toastSlideOut{0%{opacity:1;transform:translate(0)translateY(0)}to{opacity:0;transform:translate(100%)translateY(0)}}@keyframes toastLifecycle{0%{opacity:1;transform:translate(0)translateY(0)}85%{opacity:1;transform:translate(0)translateY(0)}to{opacity:0;transform:translate(100%)translateY(0)}}@keyframes progressShrink{0%{width:100%}to{width:0%}}@media (width<=540px){.error-toast,.success-toast,.toast{min-width:unset;max-width:unset;padding:.875rem 1rem;font-size:.875rem;bottom:1rem;left:1rem;right:1rem}.toast-icon,.error-icon,.success-icon{font-size:1.25rem}.toast-title{font-size:.95rem}.toast-message{font-size:.8rem}}@media (prefers-reduced-motion:reduce){.error-toast,.success-toast,.toast{animation:none}.error-toast.show,.success-toast.show,.toast.show{opacity:1;animation:none}.toast-progress-bar{animation:none}}@media print{.error-toast,.success-toast,.toast{display:none!important}}.zoom-control{z-index:900;-webkit-backdrop-filter:blur(10px);opacity:.7;cursor:move;-webkit-user-select:none;user-select:none;background:#808080b3;border-radius:50px;align-items:center;gap:.75rem;padding:.65rem 1.25rem;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,system-ui,sans-serif;transition:all .3s;display:flex;position:fixed;bottom:100px;left:50%;transform:translate(-50%);box-shadow:0 3px 10px #0003}.zoom-control.zoom-highlight{opacity:1;background:#5b5b5b;box-shadow:0 0 10px 4px #0171bccc}.zoom-hidden{display:none!important}.zoom-close-btn{color:#fffc;cursor:pointer;z-index:1;opacity:.7;background:#80808099;border:2px solid #ffffff4d;border-radius:50%;justify-content:center;align-items:center;width:24px;height:24px;padding:0;transition:all .2s;display:flex;position:absolute;top:-8px;right:-8px}.zoom-close-btn:hover{color:#fff;opacity:1;background:#dc3545e6;transform:scale(1.1);box-shadow:0 2px 8px #dc354566}.zoom-control:hover{opacity:1;background:#5b5b5b;box-shadow:0 4px 15px #0000004d}.zoom-value{color:#fff;text-align:center;min-width:30px;font-size:.95rem;font-weight:500}.zoom-value-current{color:#fff;min-width:35px;font-size:1.05rem;font-weight:600}.zoom-slider{-webkit-appearance:none;appearance:none;cursor:pointer;background:#c8c8c880;border-radius:3px;outline:none;width:180px;height:5px;transition:all .3s}.zoom-control:hover .zoom-slider,.zoom-slider:hover{background:#91beec}.zoom-slider:focus{outline-offset:2px;outline:2px solid #fff9}.zoom-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;cursor:pointer;background:#fff;border:2px solid #b4b4b4cc;border-radius:50%;width:18px;height:18px;transition:all .2s;box-shadow:0 2px 6px #0000004d}.zoom-slider::-webkit-slider-thumb:hover{border-color:#c8c8c8;transform:scale(1.1);box-shadow:0 3px 8px #0006}.zoom-slider::-webkit-slider-thumb:active{transform:scale(1.05)}.zoom-slider::-moz-range-thumb{cursor:pointer;background:#fff;border:2px solid #b4b4b4cc;border-radius:50%;width:18px;height:18px;transition:all .2s;box-shadow:0 2px 6px #0000004d}.zoom-slider::-moz-range-thumb:hover{border-color:#c8c8c8;transform:scale(1.1);box-shadow:0 3px 8px #0006}.zoom-slider::-moz-range-thumb:active{transform:scale(1.05)}.zoom-slider::-moz-range-track{background:#c8c8c880;border-radius:3px;height:5px;transition:all .3s}.zoom-control:hover .zoom-slider::-moz-range-track,.zoom-slider:hover::-moz-range-track{background:#3b82f6}.zoom-reset-btn{color:#fffc;cursor:pointer;background:#c8c8c833;border:2px solid #dcdcdc4d;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;min-width:44px;min-height:44px;margin:0 -5px 0 10px;padding:.5rem;font-size:.85rem;font-weight:700;transition:all .3s;display:flex}.zoom-reset-btn #zoom-value-current{color:inherit;font-size:inherit;font-weight:inherit;min-width:auto}.zoom-reset-btn:hover{color:#fff;background:#dcdcdc66;border-color:#f0f0f099}.zoom-reset-btn.zoom-not-default:hover{color:#fff;background:#74aacd;border-color:#74aacd}.zoom-reset-btn:active{transform:scale(.95)}.zoom-reset-btn:focus{outline-offset:2px;outline:2px solid #fff9}@media (width<=480px){.zoom-control{gap:.35rem;padding:.35rem .7rem;bottom:40px}.zoom-slider{width:100px}.zoom-value-min,.zoom-value-max{display:none}}.cv-page-content-wrapper{position:relative}.hidden{display:none!important}#contact-modal{max-width:520px}#contact-modal .info-modal-cv-title,#contact-modal .info-modal-cv-title iconify-icon{color:#3498db}.contact-modal-description{color:#555;text-align:center;margin-bottom:1.5rem;font-size:.95rem;line-height:1.6}.form-group{margin-bottom:1.25rem}.form-group:last-of-type{margin-bottom:1rem}.form-label{color:#333;margin-bottom:.4rem;font-size:.9rem;font-weight:600;display:block}.required-indicator{color:#ef4444;margin-left:.2rem}.form-input,.form-textarea{color:#333;box-sizing:border-box;background:#fff;border:2px solid #e0e0e0;border-radius:8px;width:100%;padding:.75rem;font-family:inherit;font-size:.95rem;transition:all .2s}.form-input:focus,.form-textarea:focus{border-color:#3498db;outline:none;box-shadow:0 0 0 3px #3498db1a}.form-input::placeholder,.form-textarea::placeholder{color:#999;opacity:1}.form-textarea{resize:vertical;min-height:120px;line-height:1.5}.form-input:invalid:not(:placeholder-shown),.form-textarea:invalid:not(:placeholder-shown){border-color:#ef4444}.form-input:invalid:focus:not(:placeholder-shown),.form-textarea:invalid:focus:not(:placeholder-shown){box-shadow:0 0 0 3px #ef44441a}.contact-response{min-height:0;margin-bottom:1rem}.contact-message{border-radius:8px;align-items:flex-start;gap:.75rem;margin-bottom:1rem;padding:1rem;animation:.3s messageSlideIn;display:flex}@keyframes messageSlideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.contact-message iconify-icon{flex-shrink:0;margin-top:.1rem}.contact-message-content{flex:1}.contact-message-content strong{margin-bottom:.25rem;font-size:.95rem;display:block}.contact-message-content p{margin:0;font-size:.875rem;line-height:1.5}.contact-success{color:#155724;background:linear-gradient(135deg,#28a7451a 0%,#1987540d 100%);border:2px solid #28a7454d}.contact-success iconify-icon{color:#28a745}.contact-error{color:#721c24;background:linear-gradient(135deg,#dc35451a 0%,#c823330d 100%);border:2px solid #dc35454d}.contact-error iconify-icon{color:#dc3545}.form-actions{margin-bottom:.75rem}.contact-submit-btn{cursor:pointer;color:#fff;background:linear-gradient(135deg,#3498db 0%,#2980b9 100%);border:none;border-radius:8px;justify-content:center;align-items:center;gap:.5rem;width:100%;padding:.875rem 1.5rem;font-family:inherit;font-size:1rem;font-weight:600;transition:all .2s;display:flex;box-shadow:0 4px 12px #3498db4d}.contact-submit-btn:hover{background:linear-gradient(135deg,#2980b9 0%,#3498db 100%);transform:translateY(-2px);box-shadow:0 6px 16px #3498db66}.contact-submit-btn:active{transform:translateY(0);box-shadow:0 4px 12px #3498db4d}.contact-submit-btn:disabled{color:#999;cursor:not-allowed;box-shadow:none;background:#e0e0e0;transform:none}.contact-submit-btn .htmx-indicator{display:none}.contact-submit-btn.htmx-request .htmx-indicator{display:inline-flex}.contact-submit-btn.htmx-request>span{opacity:.7}.spinning{animation:1s linear infinite spin}.form-note{color:#666;text-align:center;margin:0;font-size:.8rem;font-style:italic}.fixed-btn.contact-btn{background:var(--black-bar,#2b2b2b);color:#fff;cursor:pointer;z-index:999;opacity:.6;border:none;border-radius:50%;justify-content:center;align-items:center;width:50px;height:50px;transition:all .3s;display:flex;bottom:18rem;left:2rem;box-shadow:0 4px 12px #0000004d;position:fixed!important}.fixed-btn.contact-btn:hover{opacity:1;background:#3498db;transform:translateY(-3px);box-shadow:0 6px 16px #0006}.fixed-btn.contact-btn.at-bottom{opacity:1;background:#3498db!important}@media (width<=768px){#contact-modal{width:calc(100vw - 2rem)!important;max-width:calc(100vw - 2rem)!important;max-height:calc(100vh - 2rem)!important}#contact-modal .info-modal-content{max-height:calc(100vh - 2rem);padding:1.5rem 1rem;overflow-y:auto}#contact-modal .info-modal-header h2{font-size:1.25rem}#contact-modal .info-modal-cv-title{font-size:1rem}.contact-modal-description{margin-bottom:1.25rem;font-size:.875rem}.form-group{margin-bottom:1rem}.form-label{font-size:.85rem}.form-input,.form-textarea{padding:.65rem;font-size:.9rem}.contact-submit-btn{padding:.75rem 1.25rem;font-size:.95rem}.form-note{font-size:.75rem}.fixed-btn.contact-btn{width:45px;height:45px;bottom:13.5rem;left:1.5rem}}@media (prefers-reduced-motion:reduce){.contact-message,.spinning{animation:none}.contact-submit-btn,.form-input,.form-textarea{transition:none}}@media print{.contact-btn,#contact-modal{display:none!important}}@media (width>=769px) and (width<=1280px){.cv-header-left{padding-right:0}.cv-photo{float:right;shape-outside:margin-box;margin:0 0 15px 20px;position:static}.cv-name,.years-experience{text-align:right}.intro-text{margin-top:15px}.cv-header-left:after{content:"";clear:both;display:table}}@media (width>=901px) and (width<=1023px){html{font-size:14px}.cv-name{font-size:1.8em}.sidebar-title{font-size:.95rem}.sidebar-content{font-size:.9rem}.selector-label{opacity:0;white-space:nowrap;max-width:0;transition:all .3s;overflow:hidden}.selector-group:hover .selector-label{opacity:1;max-width:200px;margin-right:.75rem}.language-selector .selector-btn{justify-content:center;align-items:center;min-width:50px;padding:.4rem 1rem;font-size:0;transition:font-size .3s;display:inline-flex;position:relative;overflow:visible}.language-selector .selector-btn:before{content:attr(data-short);opacity:1;text-align:center;width:100%;font-size:1rem;transition:opacity .3s;display:block}.language-selector .selector-btn:hover{min-width:auto;font-size:1rem}.language-selector .selector-btn:hover:before{content:"";opacity:0}.action-btn{white-space:nowrap;text-indent:0;width:45px;transition:width .3s,padding .3s;position:relative;overflow:hidden}.action-btn iconify-icon{flex-shrink:0}.action-btn{justify-content:center;padding:0 .65rem;font-size:0}.action-btn:hover{gap:.5rem;width:auto;padding:.65rem 1.5rem;font-size:.95rem}}@media (width>=1024px) and (width<=1280px){html{font-size:14px}.cv-name{font-size:1.8em}.sidebar-title{font-size:.95rem}.sidebar-content{font-size:.9rem}.selector-label{opacity:0;white-space:nowrap;max-width:0;transition:all .3s;overflow:hidden}.selector-group:hover .selector-label{opacity:1;max-width:200px;margin-right:.75rem}.language-selector .selector-btn{justify-content:center;align-items:center;min-width:50px;padding:.4rem 1rem;font-size:0;transition:font-size .3s;display:inline-flex;position:relative;overflow:visible}.language-selector .selector-btn:before{content:attr(data-short);opacity:1;text-align:center;width:100%;font-size:1rem;transition:opacity .3s;display:block}.language-selector .selector-btn:hover{min-width:auto;font-size:1rem}.language-selector .selector-btn:hover:before{content:"";opacity:0}.action-btn{white-space:nowrap;text-indent:0;width:45px;transition:width .3s,padding .3s;position:relative;overflow:hidden}.action-btn iconify-icon{flex-shrink:0}.action-btn{justify-content:center;padding:0 .65rem;font-size:0}.action-btn:hover{gap:.5rem;width:auto;padding:.65rem 1.5rem;font-size:.95rem}}@media (width<=768px){.page-1 .page-content,.page-2 .page-content{grid-template-rows:auto auto;grid-template-columns:1fr!important}.page-1 .cv-sidebar-left{order:1;grid-area:1/1}.page-1 .cv-main{order:2;grid-area:2/1}.page-2 .cv-main{order:1;grid-area:1/1}.page-2 .cv-sidebar-right{order:2;grid-area:2/1}.cv-name{text-align:center;font-size:1.6rem}.years-experience{text-align:center;font-size:1.1em}.section-title,.sidebar-title{font-size:1.2em}.experience-period,.experience-separator,.experience-location,.experience-duration,.position{font-size:.95rem}.short-desc,.responsibilities li{font-size:.85rem}.intro-text,.summary-text{text-align:justify;font-size:.85rem;line-height:1.5}.intro-text{width:100%;margin-top:0}.course-desc,.project-desc{line-height:1.5;text-align:left!important;font-size:.85rem!important}.cv-header-content{flex-direction:column;align-items:center;gap:1rem}.cv-header-left{width:100%;padding-right:0;position:static}.cv-photo{text-align:center;width:auto;max-width:250px;height:auto;margin:1.5rem auto;position:static;top:auto;right:auto}.cv-photo img{width:100%;height:auto;max-height:none}.company-logo,.course-icon,.project-icon,.award-logo{flex-shrink:0;width:60px!important;height:60px!important}.company-logo img,.course-icon img,.project-icon img,.award-logo img{object-fit:contain;width:60px!important;height:60px!important}.experience-item,.course-item,.project-item,.award-item{border-bottom:1px solid #0000001a;flex-direction:row;align-items:flex-start;display:flex;gap:1rem!important;margin-bottom:2rem!important;padding-bottom:1.5rem!important}.experience-item{margin-bottom:1.8rem!important}.experience-content,.course-content,.project-content,.award-content{flex:1;min-width:0}.course-title,.project-title,.award-item strong{line-height:1.4;font-size:.95rem!important}.course-item small,.project-item small,.award-item small{font-size:.8rem!important}.course-desc,.project-desc,.award-desc{line-height:1.5;font-size:.85rem!important}.responsibilities li:has(img),.responsibilities li:has(iconify-icon){grid-template-columns:60px 1fr!important;gap:.75rem!important;margin-bottom:.75rem!important}.responsibilities li img,.responsibilities li iconify-icon.default-company-icon{width:60px!important;height:60px!important}.language-item,.reference-item,.other-content{margin-bottom:0!important;margin-left:1rem!important;font-size:.85rem!important;line-height:1.4!important}.cv-sidebar{padding:0!important}.sidebar-accordion summary.sidebar-accordion-header{color:#ccc;cursor:pointer;text-transform:uppercase;-webkit-user-select:none;user-select:none;border-bottom:1px solid #34495e;border-radius:0;justify-content:space-between;align-items:center;gap:.3rem;margin-bottom:0;padding:8px 15px;font-size:.85em;font-weight:400;list-style:none;background:#303030!important;display:flex!important}.sidebar-accordion-content{margin:0;padding:.5rem 1rem;transition:max-height .3s ease-in-out;overflow:hidden}.sidebar-section{margin-bottom:.5rem!important}.sidebar-accordion summary.sidebar-accordion-header::-webkit-details-marker,.sidebar-accordion summary.sidebar-accordion-header::marker{display:none}.sidebar-accordion[open] summary.sidebar-accordion-header .chevron{transition:transform .3s;transform:rotate(180deg)}.sidebar-accordion summary.sidebar-accordion-header .chevron{color:#ccc;transition:transform .3s}.sidebar-accordion:not([open]) .sidebar-accordion-content{opacity:0;max-height:0}.sidebar-accordion[open] .sidebar-accordion-content{opacity:1;max-height:2000px}}@media (width<=540px){.action-bar-content{grid-template-columns:1fr;gap:0;padding:0}.view-controls-center,.action-buttons-right{display:none}.site-title{justify-content:space-between;align-items:center;gap:.5rem;width:100%;padding:0 .5rem;display:flex}.site-title-left{flex:1px;align-items:center;gap:.5rem;min-width:0;display:flex}.site-title-link{flex:auto;min-width:0;overflow:hidden}.site-title-text{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.language-selector{flex:0 0 35%;justify-content:flex-end;gap:.25rem;margin-left:auto;margin-right:0;padding-left:0;display:flex}.site-title-year,.site-logo-link{display:none}.site-icon-mobile{display:inline-flex}.language-selector .selector-btn{justify-content:center;align-items:center;min-width:40px;padding:.4rem .75rem;font-size:0;transition:font-size .3s;display:inline-flex;position:relative;overflow:visible}.language-selector .selector-btn:before{content:attr(data-short);opacity:1;text-align:center;width:100%;font-size:.95rem;transition:opacity .3s;display:block}.language-selector .selector-btn:hover{min-width:40px;font-size:0}.language-selector .selector-btn:hover:before{content:attr(data-short);opacity:1}@supports (backdrop-filter:blur(20px)){.fixed-buttons-backdrop{-webkit-backdrop-filter:blur(20px)saturate(180%);z-index:98;pointer-events:none;background:#fffc;border-top:.5px solid #0000001a;height:90px;position:fixed;bottom:0;left:0;right:0}[data-color-theme=dark] .fixed-buttons-backdrop,[data-color-theme=auto] .fixed-buttons-backdrop{background:#1e1e1ecc;border-top:.5px solid #ffffff1a}}}@media (width<=915px) and (orientation:landscape){@supports (backdrop-filter:blur(20px)){.fixed-buttons-backdrop{-webkit-backdrop-filter:blur(20px)saturate(180%);z-index:98;pointer-events:none;background:#fffc;border-top:.5px solid #0000001a;height:70px;position:fixed;bottom:0;left:0;right:0;display:block!important}[data-color-theme=dark] .fixed-buttons-backdrop,[data-color-theme=auto] .fixed-buttons-backdrop{background:#1e1e1ecc;border-top:.5px solid #ffffff1a}}*{max-width:100vw!important}html,body{width:100vw!important;max-width:100vw!important;overflow-x:hidden!important}.cv-container{width:100%!important;max-width:100%!important;margin:0!important;padding:0!important;overflow-x:hidden!important}.cv-page{width:100%!important;max-width:100%!important;box-shadow:none!important;margin:0!important;transform:scale(1)!important}.page-content{width:100%!important;max-width:100%!important;overflow-x:hidden!important}.action-bar,.cv-header,.cv-sidebar,.cv-main{max-width:100%!important;overflow-x:hidden!important}.cv-page .page-1 .page-content,.cv-page .page-2 .page-content,.page-1 .page-content,.page-2 .page-content{grid-template-rows:auto auto!important;grid-template-columns:1fr!important;max-width:100%!important}.page-1 .cv-sidebar-left{order:1;grid-area:1/1}.page-1 .cv-main{order:2;grid-area:2/1}.page-2 .cv-main{order:1;grid-area:1/1}.page-2 .cv-sidebar-right{order:2;grid-area:2/1}.cv-header{margin-bottom:1rem!important}.cv-name{text-align:left!important;font-size:1.4rem!important}.years-experience{text-align:left!important;font-size:1em!important}.cv-header-left{grid-template-rows:auto auto auto!important;grid-template-columns:1fr auto!important;align-items:start!important;gap:.5rem 1rem!important;display:grid!important}.cv-name{grid-area:1/1}.years-experience{grid-area:2/1;margin:0!important}.intro-text{grid-area:3/1;margin:0!important}.cv-photo{grid-area:1/2/4;align-self:start;width:auto!important;max-width:180px!important;height:auto!important;margin:0!important;position:static!important}.cv-photo img{border-radius:8px;width:100%!important;height:auto!important}.action-bar{padding:.5rem .75rem!important}.action-bar-content{gap:0;padding:0;grid-template-columns:1fr!important}.view-controls-center,.action-buttons-right{display:none!important}.site-title{justify-content:space-between;align-items:center;gap:.5rem;width:100%;padding:0 .5rem;display:flex}.site-title-left{flex:auto;align-items:center;gap:.5rem;min-width:0;display:flex}.site-title-text{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;font-size:.95rem!important}.language-selector{flex:none;gap:.25rem;margin-left:auto;display:flex}.language-selector .selector-btn{min-width:35px!important;padding:.3rem .6rem!important;font-size:.85rem!important}.site-title-year,.site-logo-link{display:none!important}.site-icon-mobile{display:inline-flex!important}.cv-sidebar,.cv-sidebar-left,.cv-sidebar-right{height:auto!important;max-height:none!important;padding:.75rem!important;overflow:visible!important}.cv-sidebar .actual-content,.cv-sidebar-left .actual-content,.cv-sidebar-right .actual-content{height:auto!important;max-height:none!important;overflow:visible!important}.sidebar-accordion,.sidebar-accordion[open],.sidebar-accordion:not([open]){height:auto!important;min-height:0!important;max-height:none!important;display:block!important;overflow:visible!important}.sidebar-accordion>*{display:block!important}.sidebar-accordion summary{pointer-events:none!important;list-style:none!important}.sidebar-accordion summary::-webkit-details-marker{display:none!important}.sidebar-accordion summary.sidebar-accordion-header .chevron{transform:rotate(0)!important}.sidebar-accordion .sidebar-accordion-content,.sidebar-accordion:not([open]) .sidebar-accordion-content{opacity:1!important;visibility:visible!important;max-height:none!important;display:block!important;overflow:visible!important}.sidebar-accordion details>summary:after{transform:rotate(0)!important}.sidebar-accordion details .sidebar-content{opacity:1!important;max-height:none!important;display:block!important}.sidebar-accordion summary.sidebar-accordion-header{padding:6px 12px!important;font-size:.8em!important}.section-title{margin-bottom:.5rem!important;font-size:1rem!important}.experience-item,.project-item,.course-item{margin-bottom:1rem!important;padding-bottom:1rem!important}.experience-period,.experience-location,.position{font-size:.85rem!important}.short-desc,.responsibilities li{font-size:.8rem!important}.hamburger-btn{z-index:1001!important;opacity:1!important;visibility:visible!important;display:flex!important;position:relative!important}.download-btn,.print-friendly-btn,.fixed-btn.contact-btn,.shortcuts-btn,.info-button,.back-to-top,.color-theme-switcher{width:clamp(32px,2.2vw + 19.6px,40px)!important;height:clamp(32px,2.2vw + 19.6px,40px)!important;bottom:1rem!important}.download-btn iconify-icon,.print-friendly-btn iconify-icon,.fixed-btn.contact-btn iconify-icon,.shortcuts-btn iconify-icon,.info-button iconify-icon,.back-to-top iconify-icon,.color-theme-switcher iconify-icon{width:clamp(16px,1.1vw + 9.8px,20px)!important;height:clamp(16px,1.1vw + 9.8px,20px)!important;font-size:clamp(16px,1.1vw + 9.8px,20px)!important}.download-btn{left:calc(50% - 170px)!important}.print-friendly-btn{left:calc(50% - 120px)!important}.fixed-btn.contact-btn{left:calc(50% - 70px)!important}.shortcuts-btn{left:calc(50% - 20px)!important}.color-theme-switcher{left:calc(50% + 30px)!important}.info-button{left:calc(50% + 80px)!important}.back-to-top{left:calc(50% + 130px)!important}.is-mobile-device .download-btn{left:calc(50% - 145px)!important}.is-mobile-device .print-friendly-btn{left:calc(50% - 95px)!important}.is-mobile-device .fixed-btn.contact-btn{left:calc(50% - 45px)!important}.is-mobile-device .color-theme-switcher{left:calc(50% + 5px)!important}.is-mobile-device .info-button{left:calc(50% + 55px)!important}.is-mobile-device .back-to-top{left:calc(50% + 105px)!important}.fixed-buttons-backdrop{height:70px!important}footer.no-print{padding-bottom:90px!important}}.skeleton{will-change:background-position;background:linear-gradient(90deg,#f0f0f0 0%,#e8e8e8 20%,#f0f0f0 40% 100%) 0 0/200% 100%;border-radius:4px;animation:1.8s ease-in-out infinite skeleton-shimmer}@keyframes skeleton-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.component-wrapper{position:relative}.component-wrapper .actual-content{opacity:1;transition:opacity .25s ease-out}.component-wrapper .skeleton-content{opacity:0;pointer-events:none;transition:opacity .25s ease-out;position:absolute;top:0;left:0;right:0}.component-wrapper.loading .actual-content,.loading .component-wrapper .actual-content{opacity:0;pointer-events:none}.component-wrapper.loading .skeleton-content,.loading .component-wrapper .skeleton-content{opacity:1;pointer-events:all}.skeleton-header{min-height:200px;padding-right:185px;position:relative}.skeleton-header-text{z-index:1;position:relative}.skeleton-name{width:75%;height:40px;margin-bottom:12px}.skeleton-experience-years{width:55%;height:24px;margin-bottom:24px}.skeleton-photo{border:3px solid #e8e8e8;border-radius:0;flex-shrink:0;width:150px;height:200px;position:absolute;top:15px;right:15px}.skeleton-intro{width:100%;height:90px;margin-top:12px}.skeleton-section-title{align-items:center;gap:8px;margin-bottom:16px;display:flex}.skeleton-icon{border-radius:4px;flex-shrink:0;width:24px;height:24px}.skeleton-title-text{width:40%;height:24px}.skeleton-skill-category{margin-bottom:20px}.skeleton-skill-title{width:60%;height:20px;margin-bottom:12px}.skeleton-skill-items{flex-direction:column;gap:8px;display:flex}.skeleton-skill-item{width:100%;height:32px}.skeleton-skill-item:nth-child(2){width:85%}.skeleton-skill-item:nth-child(3){width:90%}.skeleton-skill-item:nth-child(4){width:75%}.skeleton-experience-item{gap:16px;margin-bottom:24px;display:flex}.skeleton-company-logo{border-radius:8px;flex-shrink:0;width:60px;height:60px}.skeleton-experience-content{flex-direction:column;flex:1;gap:8px;display:flex}.skeleton-position-line{width:80%;height:20px}.skeleton-date-line{width:50%;height:14px}.skeleton-description-line{width:100%;height:16px;margin-top:4px}.skeleton-responsibility-line{width:100%;height:14px;margin-left:16px}.skeleton-position{width:80%;height:20px}.skeleton-company-info{width:60%;height:16px}.skeleton-description{width:100%;height:40px;margin-top:4px}.skeleton-description.short{width:85%}.skeleton-section{padding:16px 0}.skeleton-section-title{width:35%;height:28px;margin-bottom:20px}.skeleton-education-item{width:100%;height:48px;margin-bottom:12px}.skeleton-education-item:last-child{margin-bottom:0}.skeleton-summary-paragraph{width:100%;height:18px;margin-bottom:10px}.skeleton-summary-paragraph:last-child{margin-bottom:0}.skeleton-award-item{gap:16px;margin-bottom:24px;display:flex}.skeleton-award-logo{border-radius:8px;flex-shrink:0;width:60px;height:60px}.skeleton-award-content{flex-direction:column;flex:1;gap:8px;display:flex}.skeleton-award-title-line{width:70%;height:20px}.skeleton-award-info-line{width:50%;height:14px}.skeleton-award-title{width:70%;height:20px}.skeleton-award-info{width:50%;height:16px}.skeleton-award-description{width:100%;height:40px;margin-top:4px}.skeleton-project-item{gap:16px;margin-bottom:24px;display:flex}.skeleton-project-icon{border-radius:8px;flex-shrink:0;width:80px;height:80px}.skeleton-project-content{flex-direction:column;flex:1;gap:8px;display:flex}.skeleton-project-title-line{width:75%;height:20px}.skeleton-tech-line{width:85%;height:14px;margin-top:4px}.skeleton-footer-line{width:70%;height:16px;margin-top:16px}.skeleton-project-title{width:75%;height:20px}.skeleton-project-info{width:55%;height:16px}.skeleton-project-description{width:100%;height:40px;margin-top:4px}.skeleton-project-description.short{width:80%}.skeleton-course-item{gap:16px;margin-bottom:20px;display:flex}.skeleton-course-icon{border-radius:8px;flex-shrink:0;width:80px;height:80px}.skeleton-course-content{flex-direction:column;flex:1;gap:8px;display:flex}.skeleton-course-title-line{width:70%;height:18px}.skeleton-course-info-line{width:60%;height:14px}.skeleton-course-title{width:70%;height:18px}.skeleton-course-info{width:60%;height:16px}.skeleton-language-item{width:100%;height:20px;margin-bottom:12px}.skeleton-language-item:last-child{margin-bottom:0}.skeleton-reference-item{width:100%;height:22px;margin-bottom:10px}.skeleton-reference-item:last-child{margin-bottom:0}.skeleton-other-item{width:60%;height:20px}.skeleton-sidebar{padding:16px 0}.skeleton-sidebar-header{width:80%;height:28px;margin-bottom:20px}.skeleton-footer{flex-direction:column;gap:12px;padding:16px 0;display:flex}.skeleton-footer-item{width:100%;height:20px}.skeleton-footer-item:nth-child(2){width:90%}.skeleton-footer-item:nth-child(3){width:85%}.skeleton-footer-item:nth-child(4){width:80%}.skeleton-footer-item:nth-child(5){width:75%}.skeleton-text{height:16px;margin-bottom:8px}.skeleton-text.short{width:60%}.skeleton-text.medium{width:80%}.skeleton-text.long{width:95%}@media (width<=768px){.skeleton-header{flex-direction:column;align-items:center}.skeleton-header-text{text-align:center;width:100%}.skeleton-name,.skeleton-experience-years{width:80%;margin-left:auto;margin-right:auto}.skeleton-photo{border-radius:8px;width:100px;height:100px}.skeleton-experience-item{flex-direction:column;gap:12px}.skeleton-company-logo{width:50px;height:50px}}@media (prefers-reduced-motion:reduce){.skeleton{background:#e8e8e8;animation:none}.component-wrapper .actual-content,.component-wrapper .skeleton-content{transition:none}}@media print{.skeleton-content{display:none!important}.component-wrapper .actual-content{opacity:1!important}}.skeleton{backface-visibility:hidden;transform:translateZ(0)}.component-wrapper{contain:layout style}.skeleton-content{contain:layout paint} \ No newline at end of file +*{box-sizing:border-box;margin:0;padding:0}body{background-color:var(--page-bg,#d6d6d6);background-image:var(--page-bg-pattern,none);background-size:40px 40px;background-attachment:fixed;max-width:100vw;overflow-x:clip}html{scroll-behavior:smooth;max-width:100vw;scroll-padding-top:70px;overflow-x:clip}:root{--bg-gray:#525659;--sidebar-gray:#d1d4d2;--black-bar:#2b2b2b;--paper-white:#fff;--text-dark:#000;--text-gray:#333;--accent-blue:#06c;--border-gray:#ddd;--page-bg:#d6d6d6;--page-bg-pattern:repeating-linear-gradient(0deg,transparent,transparent 5px,#4b55630f 5px,#4b55630f 6px,transparent 6px,transparent 15px),repeating-linear-gradient(90deg,transparent,transparent 5px,#4b55630f 5px,#4b55630f 6px,transparent 6px,transparent 15px),repeating-linear-gradient(0deg,transparent,transparent 10px,#6b72800a 10px,#6b72800a 11px,transparent 11px,transparent 30px),repeating-linear-gradient(90deg,transparent,transparent 10px,#6b72800a 10px,#6b72800a 11px,transparent 11px,transparent 30px);--paper-bg:#fff;--paper-secondary-bg:#f5f5f5;--text-primary:#1a1a1a;--text-secondary:#333;--text-muted:#666;--text-light:#999;--action-bar-bg:#2b2b2b;--action-bar-text:#fff;--action-bar-text-muted:#ffffffd9;--border-color:#333;--border-light:#e0e0e0;--shadow-sm:0 1px 3px #0000001a;--shadow-md:0 2px 8px #00000026;--shadow-lg:2px 2px 9px #00000080;--button-bg:transparent;--button-bg-hover:#0000000d;--button-bg-active:#0000001a;--accent-green:#27ae60;--sidebar-bg:#d1d4d2}body{color:var(--text-secondary,#333);font-smoothing:antialiased;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:Quicksand,Source Sans Pro,-apple-system,system-ui,sans-serif;font-size:16px;font-weight:400;line-height:1.5}a{color:var(--accent-blue,#06c);text-decoration:none;&:hover{text-decoration:underline}}:root{--page-bg:#d6d6d6;--page-bg-pattern:repeating-linear-gradient(0deg,transparent,transparent 5px,#4b55630f 5px,#4b55630f 6px,transparent 6px,transparent 15px),repeating-linear-gradient(90deg,transparent,transparent 5px,#4b55630f 5px,#4b55630f 6px,transparent 6px,transparent 15px),repeating-linear-gradient(0deg,transparent,transparent 10px,#6b72800a 10px,#6b72800a 11px,transparent 11px,transparent 30px),repeating-linear-gradient(90deg,transparent,transparent 10px,#6b72800a 10px,#6b72800a 11px,transparent 11px,transparent 30px);--paper-bg:#fff;--paper-secondary-bg:#f5f5f5;--text-primary:#1a1a1a;--text-secondary:#333;--text-muted:#666;--text-light:#999;--action-bar-bg:#2b2b2b;--action-bar-text:#fff;--action-bar-text-muted:#ffffffd9;--border-color:#333;--border-light:#e0e0e0;--icon-border:#ddd;--item-separator:#0000001a;--shadow-sm:0 1px 3px #0000001a;--shadow-md:0 2px 8px #00000026;--shadow-lg:2px 2px 9px #00000080;--button-bg:transparent;--button-bg-hover:#0000000d;--button-bg-active:#0000001a;--accent-blue:#06c;--accent-green:#27ae60;--sidebar-bg:#d1d4d2;--text-dark:#1a1a1a;--text-gray:#333}[data-color-theme=dark]{--page-bg:#3a3a3a;--page-bg-pattern:repeating-linear-gradient(45deg,#00ff8026 0,#00ff8026 1px,transparent 1px,transparent 20px),repeating-linear-gradient(-45deg,#00ff8026 0,#00ff8026 1px,transparent 1px,transparent 20px);--paper-bg:#1a1a1a;--paper-secondary-bg:#2a2a2a;--text-primary:#e0e0e0;--text-secondary:#d0d0d0;--text-muted:#b0b0b0;--text-light:gray;--action-bar-bg:#1a1a1a;--action-bar-text:#e0e0e0;--action-bar-text-muted:#e0e0e0d9;--border-color:#404040;--border-light:#333;--icon-border:#5e5e5e;--item-separator:#ffffff0d;--shadow-sm:0 1px 3px #0000004d;--shadow-md:0 2px 8px #0006;--shadow-lg:0 4px 16px #0009;--button-bg:transparent;--button-bg-hover:#ffffff0d;--button-bg-active:#ffffff1a;--accent-blue:#39f;--accent-green:#2ecc71;--sidebar-bg:#3a3d3e;--text-dark:#e0e0e0;--text-gray:#d0d0d0}@media (prefers-color-scheme:dark){[data-color-theme=auto]{--page-bg:#3a3a3a;--page-bg-pattern:repeating-linear-gradient(45deg,#00ff8026 0,#00ff8026 1px,transparent 1px,transparent 20px),repeating-linear-gradient(-45deg,#00ff8026 0,#00ff8026 1px,transparent 1px,transparent 20px);--paper-bg:#1a1a1a;--paper-secondary-bg:#2a2a2a;--text-primary:#e0e0e0;--text-secondary:#d0d0d0;--text-muted:#b0b0b0;--text-light:gray;--action-bar-bg:#1a1a1a;--action-bar-text:#e0e0e0;--action-bar-text-muted:#e0e0e0d9;--border-color:#404040;--border-light:#333;--icon-border:#5e5e5e;--item-separator:#ffffff0d;--shadow-sm:0 1px 3px #0000004d;--shadow-md:0 2px 8px #0006;--shadow-lg:0 4px 16px #0009;--button-bg:transparent;--button-bg-hover:#ffffff0d;--button-bg-active:#ffffff1a;--accent-blue:#39f;--accent-green:#2ecc71;--sidebar-bg:#3a3d3e;--text-dark:#e0e0e0;--text-gray:#d0d0d0}}.color-theme-switcher{background:var(--black-bar,#2b2b2b);color:#fff;cursor:pointer;z-index:999;opacity:.6;border:none;border-radius:50%;justify-content:center;align-items:center;width:50px;height:50px;transition:all .3s;display:flex;bottom:14rem;left:2rem;box-shadow:0 4px 12px #0000004d;position:fixed!important}.color-theme-switcher:hover[data-theme-mode=light]{background:#d4b200!important}.color-theme-switcher:hover[data-theme-mode=dark]{background:#013c77!important}.color-theme-switcher:hover[data-theme-mode=auto]{background:#9b59b6!important}.color-theme-switcher:hover{transform:translateY(-3px);box-shadow:0 6px 16px #0006;opacity:1!important}.color-theme-switcher.at-bottom[data-theme-mode=light]{opacity:1;background:#d4b200!important}.color-theme-switcher.at-bottom[data-theme-mode=dark]{opacity:1;background:#013c77!important}.color-theme-switcher.at-bottom[data-theme-mode=auto]{opacity:1;background:#9b59b6!important}.color-theme-switcher iconify-icon{transition:color .3s;color:#fff!important}.color-theme-switcher:hover iconify-icon{color:#fff!important}.theme-option-btn{display:none}.section-icon iconify-icon,.project-icon iconify-icon,.course-icon iconify-icon,.default-project-icon iconify-icon{color:inherit!important}.site-icon iconify-icon,.site-icon-mobile iconify-icon{color:#fff!important}.cv-paper iconify-icon{color:inherit!important}.error-icon iconify-icon{color:#dc3545!important}@media (width<=900px){.color-theme-switcher{opacity:1!important;width:clamp(36px,2.69231vw + 25.7692px,50px)!important;height:clamp(36px,2.69231vw + 25.7692px,50px)!important;position:fixed!important;bottom:1.5rem!important;left:calc(50% + clamp(22px,2.11538vw + 13.9615px,33px))!important;right:auto!important;transform:none!important}.color-theme-switcher iconify-icon{width:clamp(18px,1.15385vw + 13.6154px,24px)!important;height:clamp(18px,1.15385vw + 13.6154px,24px)!important;font-size:clamp(18px,1.15385vw + 13.6154px,24px)!important}.color-theme-switcher[data-theme-mode=light]{background:#d4b200!important}.color-theme-switcher[data-theme-mode=dark]{background:#013c77!important}.color-theme-switcher[data-theme-mode=auto]{background:#9b59b6!important}.color-theme-switcher:hover[data-theme-mode=light],.color-theme-switcher:hover[data-theme-mode=dark],.color-theme-switcher:hover[data-theme-mode=auto]{transform:translateY(-3px)!important;box-shadow:0 6px 16px #0006!important}.is-mobile-device .color-theme-switcher{left:calc(50% + clamp(2px,.384615vw + .538462px,4px))!important}}[data-color-theme=dark] img[src*=livgolf],[data-color-theme=auto] img[src*=livgolf]{filter:invert();border-color:#a1a1a1!important}@media (prefers-color-scheme:light){[data-color-theme=auto] img[src*=livgolf]{filter:none;border-color:var(--icon-border,#ddd)!important}}.cv-container{width:100%;max-width:100%;margin:0 auto;padding:20px 0 0;display:block;&.theme-clean{padding:20px 0 0;transition:all .3s ease-in-out;& .cv-page{box-shadow:var(--shadow-lg,2px 2px 9px #00000080);border:none;max-width:900px;margin:0 auto;transition:all .3s ease-in-out}& .cv-sidebar,& .cv-title-badges-header,& .cv-footer{animation:.3s ease-in-out fadeOutShrink;display:none!important}& .page-content{transition:grid-template-columns .3s ease-in-out;grid-template-columns:1fr!important}& .cv-main{transition:all .3s ease-in-out;grid-column:1!important;padding:2rem 3rem!important}}}.cv-sidebar,.cv-title-badges-header,.cv-footer{transition:all .3s ease-in-out;overflow:hidden}.cv-page{background:var(--paper-bg,#fff);max-width:1200px;box-shadow:var(--shadow-lg,2px 2px 9px #00000080);transform-origin:top;border:none;margin:2rem auto;transition:transform .3s;transform:scale(.95)}.page-content{display:grid}.page-1 .page-content{grid-template-columns:300px 1fr}.page-2 .page-content{grid-template-columns:1fr 300px}.cv-sidebar-left{grid-area:1/1}.cv-sidebar-right{text-align:right;grid-area:1/2}.page-1 .cv-main{grid-area:1/2}.page-2 .cv-main{grid-area:1/1}.cv-footer{color:#ccc;background:#303030;grid-column:1/-1;margin:0;padding:20px 0}.footer-content{text-align:center;margin:0;padding:0;list-style:none}.footer-content li{margin:0;display:inline-block}.footer-content li>div{text-align:left;margin:0 20px;display:inline-block}.footer-label{width:200px;font-size:1.7em}.footer-value{width:450px;font-size:1em}.footer-value b{font-size:1.7em;font-weight:400}.footer-separator{font-size:.6em;position:relative;left:-4%}.footer-separator i{opacity:.3}.cv-footer a{color:inherit}.cv-footer a:hover{color:#0275d8;text-decoration:none}.cv-title-badges-header{border-bottom:2px solid #34495e;flex-wrap:wrap;grid-column:1/-1;justify-content:center;align-items:center;gap:0;padding:10px 20px;display:flex;background:#303030!important}.title-badge{color:#ccc;text-transform:uppercase;white-space:nowrap;font-size:.9em;font-weight:400}.badge-separator{color:#ccc;padding:0 15px;font-weight:400;position:relative;top:-1px}.cv-main{background:var(--paper-bg,#fff);padding:3rem 2.5rem 8rem}.cv-paper{width:100%;box-shadow:none;transform-origin:top;will-change:transform;background:0 0;min-height:auto;margin:0;transition:transform 80ms linear;display:block;position:relative}.page-break{page-break-after:always;break-after:page}.avoid-break{page-break-inside:avoid;break-inside:avoid}.action-bar{background:var(--action-bar-bg,#2b2b2b);color:var(--action-bar-text,#fff);z-index:100;box-shadow:var(--shadow-md,0 2px 8px #00000026);font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,system-ui,sans-serif;position:sticky;top:0;overflow:visible}.action-bar-content{grid-template-columns:1fr auto 1fr;align-items:stretch;gap:2rem;max-width:100%;height:50px;margin:0 auto;padding:0;display:grid;overflow:visible}.site-title{white-space:nowrap;justify-self:start;align-items:center;gap:.75rem;height:100%;padding:0;display:flex}.site-title-left{align-items:center;gap:.75rem;display:flex}.site-icon{color:#fff;flex-shrink:0;justify-content:center;align-items:center;height:36px;padding:0 .5rem 0 1.5rem;display:inline-flex}.site-icon-mobile{color:#fff;flex-shrink:0;margin-right:.5rem;display:none}.site-logo-link,.site-title-link{color:inherit;align-items:center;height:36px;text-decoration:none;transition:opacity .2s;display:flex}.site-logo-link:hover,.site-title-link:hover{opacity:.8;text-decoration:none}.site-logo-link{padding:0}.iconify,iconify-icon{vertical-align:middle;display:inline-block}.site-title-text{color:#fff;letter-spacing:-.01em;align-items:center;height:36px;padding:0 1rem 0 0;font-size:1.05rem;font-weight:500;line-height:1;display:flex}.view-controls-center{white-space:nowrap;flex-direction:row;flex-shrink:0;justify-self:center;align-items:center;gap:2.5rem;height:100%;display:flex}.selector-group{align-items:center;gap:.75rem;display:flex}.selector-label{color:#ffffffd9;white-space:nowrap;letter-spacing:-.01em;align-items:center;height:36px;font-size:.875rem;font-weight:500;line-height:1;display:flex}.selector-label span{color:#27ae60;font-weight:600}.language-toggle,.cv-length-toggle,.logo-toggle{flex-shrink:0}.action-buttons{flex-shrink:0;justify-self:end}.htmx-indicator{flex-shrink:0}.lang-btn{color:#fff;cursor:pointer;text-transform:capitalize;background:0 0;border:1px solid #ffffff4d;border-radius:3px;padding:.4rem 1rem;font-size:1rem;font-weight:400;transition:all .2s}.lang-btn:hover{background:#ffffff1a;border-color:#ffffff80}.lang-btn.active{font-weight:500;background:#27ae60!important;border-color:#27ae60!important}.icon-toggle{cursor:pointer;display:flex;position:relative}.icon-toggle input[type=checkbox]{opacity:0;width:0;height:0;position:absolute}.icon-toggle-slider{background:#e0e0e0;border:2px solid #d0d0d0;border-radius:15px;justify-content:space-between;align-items:center;width:75px;height:30px;padding:0 6px;transition:all .3s;display:inline-flex;position:relative}.icon-toggle-slider:before{content:"";z-index:2;pointer-events:none;background:#fff;border-radius:50%;width:24px;height:24px;transition:transform .3s;position:absolute;left:2px;box-shadow:0 2px 4px #0000004d}.icon-toggle input:checked+.icon-toggle-slider:before{transform:translate(43px)}.icon-toggle input:checked+.icon-toggle-slider{background:#27ae60;border-color:#229954}.icon-toggle-slider .icon-left,.icon-toggle-slider .icon-right{z-index:3;pointer-events:none;flex-shrink:0;transition:all .3s;position:absolute}.icon-toggle-slider .icon-left{left:6px}.icon-toggle-slider .icon-right{right:6px}.icon-toggle input:not(:checked)+.icon-toggle-slider .icon-left{font-weight:700;color:#333!important}.icon-toggle input:not(:checked)+.icon-toggle-slider .icon-right{opacity:.5;color:#999!important}.icon-toggle input:checked+.icon-toggle-slider .icon-left{opacity:.5;color:#fff6!important}.icon-toggle input:checked+.icon-toggle-slider .icon-right{font-weight:700;color:#fff!important}.icon-toggle input:focus+.icon-toggle-slider{box-shadow:0 0 0 3px #27ae6033}.language-selector-wrapper{width:fit-content;height:100%;display:inline-flex;position:relative}.language-selector{background:0 0;border-radius:0;align-items:stretch;gap:0;height:100%;margin-right:0;padding:0 0 0 1rem;display:inline-flex}#lang-indicator-en,#lang-indicator-es{pointer-events:none;z-index:10;position:absolute;top:50%;transform:translateY(-50%)}#lang-indicator-en{left:calc(1rem + 50px)}#lang-indicator-es{left:calc(1rem + 135px)}.selector-btn{color:#fff;cursor:pointer;white-space:nowrap;letter-spacing:-.01em;background:0 0;border:none;border-radius:0;justify-content:center;align-items:center;gap:0;height:100%;padding:0 1.5rem;font-size:1rem;font-weight:500;line-height:1;text-decoration:none;transition:all .2s;display:inline-flex;box-shadow:none!important;outline:none!important;min-width:50px!important}.selector-btn:focus,.selector-btn:focus-visible,.selector-btn:active{box-shadow:none!important;outline:none!important}.selector-btn:hover{background:#666}.selector-btn:hover iconify-icon{color:#27ae60}.selector-btn.active{color:#fff;background:#27ae60}.selector-btn:not(.active){color:#fff;background:0 0}.action-btn{color:#fff;cursor:pointer;white-space:nowrap;letter-spacing:-.01em;background:0 0;border:none;border-radius:0;justify-content:center;align-items:center;gap:.5rem;height:100%;padding:0 1.5rem;font-size:1rem;font-weight:500;line-height:1;text-decoration:none;transition:background-color .3s,color .3s;display:inline-flex}.action-btn iconify-icon{color:#fff;transition:color .3s}.action-btn:hover{color:#333;background:#ddd;text-decoration:none}.action-btn:hover iconify-icon{color:#27ae60}.pdf-btn{color:#fff!important;background:0 0!important}.pdf-btn:hover,.pdf-btn.pdf-hover-sync{color:#fff!important;background:#cd6060!important}.pdf-btn iconify-icon{filter:brightness(0)invert();transition:filter .3s;color:#fff!important}.pdf-btn:hover iconify-icon{filter:brightness(0)invert();color:#fff!important}.print-btn{color:#fff!important;background:0 0!important}.print-btn:hover,.print-btn.print-hover-sync{color:#27ae60!important;background:#fff!important}.print-btn iconify-icon{color:#fff}.print-btn:hover iconify-icon,.print-btn.print-hover-sync iconify-icon{color:#27ae60}.cv-length-toggle{justify-self:center;gap:.5rem;display:flex}.length-btn{color:#fff;cursor:pointer;background:#ffffff1a;border:1px solid #fff6;border-radius:4px;padding:.4rem 1rem;font-size:.9rem;font-weight:500;transition:all .2s}.length-btn:hover{background:#fff3;border-color:#fff9}.length-btn.active{color:#1a1a1a;background:#fff;border-color:#fff;font-weight:600}.action-buttons,.action-buttons-right{flex-wrap:nowrap;align-items:stretch;gap:0;height:100%;display:flex;overflow:visible}.action-buttons-right{justify-self:end;margin-left:auto}@media (width>=901px) and (width<=1400px){.action-buttons-right{flex-shrink:1;min-width:0}.action-buttons-right .action-btn{flex-shrink:1;min-width:40px;padding:0 .5rem}}@media (width>=541px) and (width<=900px){.action-buttons-right{flex-shrink:1;min-width:0;display:flex!important}.action-buttons-right .action-btn{flex-shrink:1;width:auto;min-width:36px;padding:0 .4rem;font-size:0}.action-buttons-right .action-btn iconify-icon{width:20px;height:20px}}@media (width>=901px) and (width<=1100px){.action-btn{width:clamp(35px,4vw,50px)!important;padding:0 clamp(.3rem,.8vw,1rem)!important}}.htmx-indicator{opacity:0;pointer-events:none;justify-content:center;align-items:center;transition:opacity .2s ease-in-out;display:inline-flex;position:absolute}.htmx-indicator.htmx-request,#lang-indicator-en.htmx-request,#lang-indicator-es.htmx-request{opacity:1!important}iconify-icon.htmx-indicator{justify-content:center;align-items:center;display:inline-flex}span.htmx-request.htmx-indicator,.htmx-request .htmx-indicator,.htmx-request.htmx-indicator{opacity:1!important}.htmx-indicator.spinning{animation:1s linear infinite htmx-spin}@keyframes htmx-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.htmx-indicator.small{width:14px;height:14px;font-size:14px}.htmx-indicator.medium{width:18px;height:18px;font-size:18px}.htmx-indicator.large{width:24px;height:24px;font-size:24px}.htmx-indicator.inline{vertical-align:middle;margin-left:8px;display:inline-flex}.htmx-indicator.inline-start{vertical-align:middle;margin-right:8px;display:inline-flex}.htmx-indicator.light{color:#ffffffe6}.htmx-indicator.dark{color:#000000b3}.htmx-indicator.accent{color:#27ae60}@media (prefers-reduced-motion:reduce){.htmx-indicator.spinning{animation:none}.htmx-indicator{transition:none}}.loader{border:2px solid #f3f3f3;border-top-color:#fff;border-radius:50%;width:20px;height:20px;animation:1s linear infinite htmx-spin}.cv-sidebar{background:var(--sidebar-bg,#d1d4d2);padding:4rem 1.5rem;font-size:.9rem}.sidebar-accordion-header{display:none}.sidebar-section{margin-bottom:2rem;&:has(details:not([open])){margin-top:0;margin-bottom:3rem}& details{margin:0;& summary~*{opacity:0;max-height:0;transition:max-height .5s ease-in-out,opacity .3s ease-in-out,transform .3s ease-in-out;overflow:hidden;transform:translateY(-8px)}&[open] summary~*{opacity:1;max-height:1500px;transform:translateY(0)}&[open] .sidebar-content{margin-top:.5rem}}& summary{cursor:pointer;-webkit-user-select:none;user-select:none;justify-content:space-between;align-items:center;list-style:none;display:flex;position:relative;&::-webkit-details-marker,&::marker{display:none}& .sidebar-title{margin-bottom:0}&:hover .sidebar-title{color:var(--accent-blue,#06c)}&:hover:after,details:not([open]) &:after{opacity:1}}}.sidebar-title{color:var(--text-primary,#1a1a1a);text-align:left;margin-bottom:10px;padding:0;font-family:Quicksand,sans-serif;font-size:1.4em;font-weight:700;line-height:1.3em}.sidebar-content{color:var(--text-primary,#1a1a1a);font-family:Quicksand,sans-serif;font-size:.95rem;font-weight:400;line-height:1.5}.skill-item{color:var(--text-primary,#1a1a1a);margin-bottom:.15rem;font-weight:400}.cv-sidebar-left{& .sidebar-section summary:after{content:"▶";color:var(--text-muted,#666);opacity:0;flex-shrink:0;margin-left:15px;font-size:.8em;transition:transform .2s,opacity .2s}& .sidebar-section details[open] summary:after{transform:rotate(90deg)}& .sidebar-content,& .skill-item{text-align:left}}.cv-sidebar-right{& .sidebar-section summary{flex-direction:row-reverse;justify-content:space-between;& .sidebar-title{text-align:right;width:100%}&:after{content:"▶";color:var(--text-muted,#666);opacity:0;flex-shrink:0;margin-right:15px;font-size:.8em;transition:transform .2s,opacity .2s}}& .sidebar-section details[open] summary:after{transform:rotate(90deg)}& .sidebar-content,& .skill-item{text-align:right}}.cv-header{margin-bottom:2rem}.cv-header-content{justify-content:space-between;align-items:flex-start;gap:2rem;display:flex}.cv-header-left{flex:1;padding-right:185px;position:relative}.cv-photo{border:3px solid #fff;flex-shrink:0;width:150px;height:200px;position:absolute;top:15px;right:15px;overflow:hidden;box-shadow:0 2px 8px #00000026}.cv-photo img{object-fit:cover;width:100%;height:100%}.cv-name{color:var(--text-primary,#1a1a1a);text-align:right;margin-bottom:8px;font-family:Quicksand,sans-serif;font-size:2.2em;font-weight:400;line-height:1.1}.cv-experience-years{color:var(--text-primary,#1a1a1a);margin:0;font-family:Quicksand,sans-serif;font-size:.9em;font-weight:500;line-height:1.5}.years-experience{color:var(--text-muted,#666);text-align:right;margin:4px 0 0;font-family:Quicksand,sans-serif;font-size:1.25em;font-weight:400;line-height:1.4}.intro-text{color:var(--text-secondary,#333);text-align:justify;-moz-text-align-last:justify;text-align-last:justify;text-justify:inter-word;word-spacing:-1px;overflow-wrap:break-word;word-wrap:break-word;-webkit-hyphens:auto;-moz-hyphens:auto;-ms-hyphens:auto;hyphens:auto;margin-top:20px;font-family:Quicksand,sans-serif;font-size:1em;font-style:italic;line-height:1.6}.cv-section{page-break-inside:avoid;margin-bottom:3rem}.cv-section:has(details:not([open])){margin-bottom:0}.section-title{color:var(--text-primary,#1a1a1a);margin:20px 0 25px;padding:0;font-family:Quicksand,sans-serif;font-size:1.4em;font-weight:500;line-height:1.2em}.cv-section details{margin:0}.cv-section details summary~*{opacity:0;max-height:0;transition:max-height .5s ease-in-out,opacity .3s ease-in-out,transform .3s ease-in-out;overflow:hidden;transform:translateY(-8px)}.cv-section details[open] summary~*{opacity:1;max-height:3000px;transform:translateY(0)}.cv-section summary{cursor:pointer;-webkit-user-select:none;user-select:none;list-style:none;position:relative}.cv-section summary::-webkit-details-marker,.cv-section summary::marker{display:none}.cv-section summary .section-title{align-items:center;gap:.5rem;display:inline-flex}.cv-section summary .section-title:after{content:"▼";color:var(--text-muted,#666);opacity:0;margin-left:.5rem;font-size:.8em;transition:transform .2s,opacity .2s}.cv-section summary:hover .section-title:after,.cv-section details:not([open]) summary .section-title:after{opacity:1}.cv-section details:not([open]) summary .section-title:after{transform:rotate(-90deg)}.cv-section summary:hover .section-title{color:var(--accent-blue,#06c)}.summary-text{text-align:justify;color:var(--text-primary,#1a1a1a);font-family:Quicksand,sans-serif;font-size:.9em;font-weight:400;line-height:1.5}.responsibilities li div iconify-icon,.responsibilities li strong+iconify-icon,.responsibilities li em+iconify-icon{vertical-align:middle;margin:0 .15em;font-size:1em;display:inline-block;width:1.2em!important;height:1.2em!important;color:inherit!important;background:0 0!important;border:none!important;padding:0!important}.experience-header{margin-bottom:.6rem}.experience-title-line{margin-bottom:.3em}.position{color:var(--text-dark,#1a1a1a);margin:0 0 4px;font-size:1rem;font-weight:500}.position .position-title{margin-right:.3em;display:inline-block}.position .company-name{display:inline-block}.current-badge{color:#fff;vertical-align:middle;letter-spacing:.5px;background:#27ae60;border-radius:3px;margin-left:.5em;padding:.2em .5em;font-size:.7em;font-weight:700;display:inline-block}.live-badge{color:#fff;vertical-align:middle;letter-spacing:.5px;background:#27ae60;border-radius:3px;align-items:center;gap:.3em;margin-left:.5em;padding:.2em .5em;font-size:.7em;font-weight:700;display:inline-flex}.live-badge iconify-icon{font-size:1.2em}.expired-badge{color:#fff;vertical-align:middle;letter-spacing:.5px;background:#e74c3c;border-radius:3px;margin-left:.5em;padding:.2em .5em;font-size:.7em;font-weight:700;display:inline-block}.maintained-badge{color:#fff;vertical-align:middle;letter-spacing:.5px;background:#3498db;border-radius:3px;margin-left:.5em;padding:.2em .5em;font-size:.7em;font-weight:700;display:inline-block}.experience-period,.experience-separator,.experience-location,.experience-duration{color:var(--text-muted,#666);font-size:1.05rem;font-weight:600;display:inline-block}.experience-duration{font-style:italic}.short-desc{color:var(--text-dark,#1a1a1a);margin-top:.5rem;font-size:.95rem;line-height:1.6}.duration-text{color:var(--text-light,#999);font-weight:500}.responsibilities{margin-top:1rem;padding-left:0;list-style:none}.responsibilities li{color:var(--text-dark,#1a1a1a);margin-bottom:.4rem;padding-left:1.2rem;font-size:.95rem;line-height:1.5;position:relative}.responsibilities li:before{content:"•";color:var(--text-gray,#333);position:absolute;left:0}.responsibilities li:has(img),.responsibilities li:has(iconify-icon){grid-template-columns:60px 1fr;align-items:start;gap:1rem;margin-bottom:1rem;padding-left:0;display:grid}.responsibilities li:has(img):before,.responsibilities li:has(iconify-icon):before{display:none}.responsibilities li img{object-fit:contain;border:1px solid var(--icon-border,#ddd);background:0 0;border-radius:4px;width:60px;height:60px;padding:4px}.responsibilities li iconify-icon.default-company-icon{border:1px solid var(--icon-border,#ddd);width:60px;height:60px;color:var(--text-light,#999);background:0 0;border-radius:4px;justify-content:center;align-items:center;padding:8px;display:flex}@keyframes fadeInGrow{0%{opacity:0;transform-origin:top;max-height:0;transform:scaleY(.8)}to{opacity:1;max-height:5000px;transform:scaleY(1)}}@keyframes fadeOutShrink{0%{opacity:1;max-height:5000px;transform:scaleY(1)}to{opacity:0;transform-origin:top;max-height:0;transform:scaleY(.8)}}.cv-long .long-only,.cv-long .responsibilities{animation:.3s ease-in-out fadeInGrow;display:block}.project-item{border-bottom:1px solid #0000001a;align-items:flex-start;gap:1.2rem;margin-bottom:2.5rem;padding-bottom:2rem;display:flex}.project-icon{flex-shrink:0;justify-content:center;align-items:center;width:80px;height:80px;display:flex}.project-icon img{object-fit:contain;border:1px solid var(--icon-border,#ddd);background:0 0;border-radius:4px;width:80px;height:80px;padding:4px}.default-project-icon{border:1px solid var(--icon-border,#ddd);width:80px;height:80px;color:var(--text-light,#999);background:0 0;border-radius:4px;justify-content:center;align-items:center;padding:10px;display:flex}.project-content{flex:1}.project-header{margin-bottom:.5rem}.project-title{color:var(--text-dark,#1a1a1a);margin:0 0 .3rem;font-size:1em;font-weight:600;line-height:1.4}.project-title-text{display:inline}.project-title-text a{color:var(--accent-blue,#06c);text-decoration:none}.project-title-text a:hover{text-decoration:underline}.project-period,.project-separator,.project-location{color:var(--text-muted,#666);font-size:.9em;font-weight:600}.project-separator{color:var(--text-light,#999)}.project-desc{color:var(--text-dark,#1a1a1a);text-align:justify;margin-top:.5rem;font-size:.95rem;line-height:1.6}.project-technologies{color:var(--text-gray,#333);margin-top:.5rem;font-size:.85em;line-height:1.4}.projects-footer{text-align:center;color:var(--text-gray,#333);margin-top:-1.5rem;padding-top:0;font-size:.95rem}.projects-footer p{margin:0}.projects-footer a{color:var(--accent-blue,#06c);text-decoration:none}.projects-footer a:hover{text-decoration:underline}.reference-item{margin-bottom:0!important;margin-left:2rem!important;font-size:.95rem!important;line-height:1.4!important}.reference-item a{color:var(--accent-blue,#06c);word-break:break-word;text-decoration:none}.reference-item a:hover{text-decoration:underline}.ref-type{color:var(--text-gray,#333);margin-top:.2rem;font-size:.8em;font-style:italic;display:block}footer{text-align:center;color:#ffffffb3;padding:2rem;font-size:.85rem}.github-repo-link{transition:color .2s ease-in-out;color:#f5f5f5!important}.github-repo-link:hover{color:#66b3ff!important}.long-only,.short-desc{transition:all .3s ease-in-out;overflow:hidden}.cv-short .long-only{animation:.3s ease-in-out fadeOutShrink;display:none}.cv-short .short-desc{animation:.3s ease-in-out fadeInGrow;display:block}.cv-long .short-desc,.short-desc{animation:.3s ease-in-out fadeOutShrink;display:none}.cv-long .long-only,.cv-long .responsibilities{animation:.3s ease-in-out fadeInGrow;display:block}.project-item .responsibilities li iconify-icon.default-company-icon{border:1px solid var(--icon-border,#ddd);border-radius:4px;justify-content:center;align-items:center;padding:8px;width:60px!important;height:60px!important;color:unset!important;background:0 0!important;display:flex!important}.project-desc iconify-icon,.project-technologies iconify-icon{vertical-align:middle;margin:0 .15em;font-size:1em;display:inline-block;width:1.2em!important;height:1.2em!important;color:inherit!important}.course-item{border-bottom:1px solid #0000001a;align-items:flex-start;gap:1.2rem;margin-bottom:2.5rem;padding-bottom:2rem;display:flex}.course-icon{flex-shrink:0;justify-content:center;align-items:center;width:80px;height:80px;display:flex}.course-icon img{object-fit:contain;border:1px solid var(--icon-border,#ddd);background:0 0;border-radius:4px;width:80px;height:80px;padding:4px}.default-course-icon{border:1px solid var(--icon-border,#ddd);width:80px;height:80px;color:var(--text-light,#999);background:0 0;border-radius:4px;justify-content:center;align-items:center;padding:10px;display:flex}.course-content{flex:1}.course-header{margin-bottom:.5rem}.course-title{color:var(--text-dark,#1a1a1a);margin:0 0 .3rem;font-size:1em;font-weight:600;line-height:1.4}.course-title-text{display:inline}.course-institution{margin-left:.5em;font-weight:400;display:inline}.course-period,.course-separator,.course-location,.course-duration{color:var(--text-muted,#666);font-size:.9em}.course-separator{color:var(--text-light,#999)}.course-desc{color:var(--text-gray,#333);text-align:justify;margin-top:.4rem;font-size:.85em;line-height:1.4}.course-item .responsibilities li iconify-icon.default-company-icon{border:1px solid var(--icon-border,#ddd);border-radius:4px;justify-content:center;align-items:center;padding:8px;width:60px!important;height:60px!important;color:unset!important;background:0 0!important;display:flex!important}.course-desc iconify-icon{vertical-align:middle;margin:0 .15em;font-size:1em;display:inline-block;width:1.2em!important;height:1.2em!important;color:inherit!important}.education-item{color:var(--text-dark,#1a1a1a);margin-bottom:1rem;font-size:.95rem;line-height:1.6}.languages-list{flex-wrap:wrap;gap:1.5rem;display:flex}.language-item{color:var(--text-dark,#1a1a1a);margin-bottom:.3rem!important;margin-left:2rem!important;font-size:.95rem!important;line-height:1.4!important}.language-item small{margin-top:.2rem;font-size:.8em;font-style:italic;display:block}.experience-item{border-bottom:1px solid #0000001a;margin-bottom:2.5rem;padding-bottom:2rem}.language-toggle,.cv-length-toggle,.logo-toggle{white-space:nowrap;justify-content:center;align-items:center;gap:.5rem;display:inline-flex}.toggle-switch{cursor:pointer;-webkit-user-select:none;user-select:none;display:inline-block;position:relative}.toggle-switch input[type=checkbox]{opacity:0;width:0;height:0;position:absolute}.toggle-slider{background-color:#555;border-radius:26px;width:50px;height:26px;transition:background-color .3s;display:inline-block;position:relative}.toggle-slider:after{content:"";background-color:#fff;border-radius:50%;width:20px;height:20px;transition:transform .3s;position:absolute;top:3px;left:3px;box-shadow:0 2px 4px #0003}.toggle-switch input[type=checkbox]:checked+.toggle-slider{background-color:var(--accent-blue,#06c)}.toggle-switch input[type=checkbox]:checked+.toggle-slider:after{transform:translate(24px)}.toggle-switch input[type=checkbox]:focus+.toggle-slider{box-shadow:0 0 0 3px #06c3}.toggle-label-left,.toggle-label-right{color:#999;white-space:nowrap;justify-content:center;align-items:center;height:28px;font-size:.8rem;font-weight:500;transition:all .3s;display:flex}.flag-icon{border-radius:50%;justify-content:center;align-items:center;display:flex;overflow:hidden}.language-toggle:has(#langToggle:not(:checked)) .toggle-label-left,.cv-length-toggle:has(#lengthToggle:not(:checked)) .toggle-label-left,.logo-toggle:has(#logoToggle:not(:checked)) .toggle-label-left,.language-toggle:has(#langToggle:checked) .toggle-label-right,.cv-length-toggle:has(#lengthToggle:checked) .toggle-label-right,.logo-toggle:has(#logoToggle:checked) .toggle-label-right{color:#fff;opacity:1}.language-toggle:has(#langToggle:not(:checked)) .toggle-label-right,.cv-length-toggle:has(#lengthToggle:not(:checked)) .toggle-label-right,.logo-toggle:has(#logoToggle:not(:checked)) .toggle-label-right,.language-toggle:has(#langToggle:checked) .toggle-label-left,.cv-length-toggle:has(#lengthToggle:checked) .toggle-label-left,.logo-toggle:has(#logoToggle:checked) .toggle-label-left{opacity:.4}.experience-item,.award-item{border-bottom:2px solid var(--icon-border,#ddd);page-break-inside:avoid;gap:1.2rem;margin-bottom:2.5rem;padding-bottom:2rem;transition:gap .3s ease-in-out;display:flex;position:relative}.experience-item:last-child,.award-item:last-child{border-bottom:none;padding-bottom:0}.cv-paper:not(.show-icons) .experience-item,.cv-paper:not(.show-icons) .award-item{gap:0}.company-logo,.award-logo,.project-icon,.course-icon{flex-shrink:0;display:block}.company-logo img,.award-logo img,.project-icon img,.course-icon img{object-fit:contain;border:1px solid var(--icon-border,#ddd);background:0 0;border-radius:4px;width:80px;height:80px;padding:10px}.default-company-icon,.default-award-icon,.default-project-icon,.default-course-icon{border:1px solid var(--icon-border,#ddd);color:#999;background:0 0;border-radius:4px;justify-content:center;align-items:center;padding:10px;display:flex;width:80px!important;height:80px!important}.experience-content,.award-content{flex:1;min-width:0}.company-logo,.award-logo,.section-icon,.default-company-icon,.project-icon,.default-project-icon,.course-icon,.default-course-icon{opacity:1;width:auto;height:auto;transition:opacity .3s ease-in-out,transform .3s ease-in-out,width .3s ease-in-out,height .3s ease-in-out,margin .3s ease-in-out;overflow:hidden;transform:scale(1)}.cv-paper:not(.show-icons) .company-logo,.cv-paper:not(.show-icons) .award-logo,.cv-paper:not(.show-icons) .section-icon,.cv-paper:not(.show-icons) .default-company-icon,.cv-paper:not(.show-icons) .project-icon,.cv-paper:not(.show-icons) .default-project-icon,.cv-paper:not(.show-icons) .course-icon,.cv-paper:not(.show-icons) .default-course-icon{opacity:0;pointer-events:none;width:0;height:0;margin:0;padding:0;overflow:hidden;transform:scale(.8)}.show-icons .company-logo,.show-icons .award-logo,.show-icons .section-icon,.show-icons .default-company-icon,.show-icons .project-icon,.show-icons .default-project-icon,.show-icons .course-icon,.show-icons .default-course-icon{opacity:1;width:auto;height:auto;transform:scale(1)}@media (width<=768px){.logo-toggle{order:3}.toggle-label{font-size:.85rem}.toggle-slider{width:38px;height:20px}.toggle-slider:after{width:14px;height:14px}.toggle-switch input[type=checkbox]:checked+.toggle-slider:after{transform:translate(18px)}.company-logo img{width:40px;height:40px}}.has-tooltip{position:relative}.has-tooltip:before{content:attr(data-tooltip);color:#fff;white-space:nowrap;letter-spacing:.01em;opacity:0;visibility:hidden;pointer-events:none;z-index:1000;background:#000000d9;border-radius:6px;padding:4px 8px;font-size:11px;font-weight:600;line-height:1.3;transition:opacity .2s,transform .2s cubic-bezier(.16,1,.3,1),visibility .2s;position:absolute;transform:scale(.8);box-shadow:0 2px 8px #0000004d}.has-tooltip:hover:before{opacity:1;visibility:visible;transform:scale(1)}.has-tooltip:before{top:50%;left:calc(100% + 12px);transform:translateY(-50%)scale(.8)}.has-tooltip:hover:before{transform:translateY(-50%)scale(1)}.has-tooltip.tooltip-left:before{top:50%;left:auto;right:calc(100% + 12px);transform:translateY(-50%)scale(.8)}.has-tooltip.tooltip-left:hover:before{transform:translateY(-50%)scale(1)}.has-tooltip.tooltip-top:before{inset:auto auto calc(100% + 12px) 50%;transform:translate(-50%)scale(.8)}.has-tooltip.tooltip-top:hover:before{transform:translate(-50%)scale(1)}.has-tooltip.tooltip-bottom:before{inset:calc(100% + 12px) auto auto 50%;transform:translate(-50%)scale(.8)}.has-tooltip.tooltip-bottom:hover:before{transform:translate(-50%)scale(1)}@media (width<=900px){.action-btn.has-tooltip:before{inset:auto auto calc(100% + 8px) 50%;transform:translate(-50%)scale(.8)}.action-btn.has-tooltip:hover:before{transform:translate(-50%)scale(1)}.fixed-btn.has-tooltip:before,.color-theme-switcher.has-tooltip:before,.info-button.has-tooltip:before{inset:auto auto calc(100% + 8px) 50%;transform:translate(-50%)scale(.8)}.fixed-btn.has-tooltip:hover:before,.color-theme-switcher.has-tooltip:hover:before,.info-button.has-tooltip:hover:before{transform:translate(-50%)scale(1)}.back-to-top.has-tooltip.tooltip-left:before{inset:50% calc(100% + 8px) auto auto;transform:translateY(-50%)scale(.8)}.back-to-top.has-tooltip.tooltip-left:hover:before{transform:translateY(-50%)scale(1)}}@media (width<=483px){.back-to-top.has-tooltip.tooltip-left:before{top:50%;right:calc(100% + 8px);transform:translateY(-50%)scale(.8)}.back-to-top.has-tooltip.tooltip-left:hover:before{transform:translateY(-50%)scale(1)}}@media (prefers-reduced-motion:reduce){.has-tooltip:before{transition:opacity .1s,visibility .1s;transform:scale(1)!important}.has-tooltip:hover:before{transform:scale(1)!important}.has-tooltip.tooltip-left:before,.has-tooltip.tooltip-left:hover:before{transform:translateY(-50%)scale(1)!important}.has-tooltip.tooltip-top:before,.has-tooltip.tooltip-top:hover:before{transform:translate(-50%)scale(1)!important}}@media (hover:none) and (pointer:coarse){.has-tooltip:before{display:none}}[data-color-theme=dark] .has-tooltip:before{background:#282828f2;box-shadow:0 2px 12px #00000080}[data-color-theme=light] .has-tooltip:before{background:#000000d9}.hamburger-btn{color:#fff;cursor:pointer;background:0 0;border:none;border-radius:4px;justify-content:center;align-items:center;margin:0 .5rem;padding:.5rem;transition:background-color .2s;display:flex;position:relative}.hamburger-btn:hover{background-color:#ffffff1a}.hamburger-btn:active{background-color:#fff3}.navigation-menu{z-index:1000;pointer-events:none;opacity:0;background:#fff;width:280px;max-height:0;transition:max-height .5s cubic-bezier(.4,0,.2,1),opacity .3s;position:fixed;top:50px;left:0;overflow-y:auto;box-shadow:2px 0 10px #00000026}.hamburger-btn:hover~.navigation-menu,.hamburger-btn:focus~.navigation-menu,.navigation-menu:hover,.navigation-menu.menu-hover,.navigation-menu.menu-open{pointer-events:auto;opacity:1;max-height:calc(100vh - 60px)}.menu-content{padding:1rem 0}.menu-item{color:var(--text-dark,#1a1a1a);border-left:3px solid #0000;align-items:center;gap:1rem;padding:.875rem 1.5rem;font-size:.95rem;font-weight:500;text-decoration:none;transition:background-color .2s,color .2s;display:flex}.menu-item:hover{color:var(--accent-blue,#06c);border-left-color:var(--accent-blue,#06c);background-color:#0066cc14;text-decoration:none}.menu-item iconify-icon{color:var(--text-gray,#333);flex-shrink:0;transition:color .2s}.menu-item:hover iconify-icon{color:var(--accent-blue,#06c)}.menu-item-submenu{border-bottom:1px solid #0000001a;padding:0 0 1rem;position:relative}.menu-item.has-submenu{justify-content:space-between;position:relative}.submenu-arrow{margin-left:auto;transition:transform .2s}.menu-item-submenu:hover .submenu-arrow{transform:translate(3px)}.submenu-content{opacity:0;visibility:hidden;z-index:1000;background:#fff;border-radius:8px;min-width:250px;max-width:300px;max-height:calc(100vh - 100px);padding:.5rem 0;transition:all .3s;position:fixed;left:232px;overflow-y:auto;transform:translate(-3px);box-shadow:2px 2px 10px #00000026}.menu-item-submenu:hover .submenu-content,.submenu-content:hover{opacity:1;visibility:visible;transform:translate(0)}.menu-item-submenu.submenu-open .submenu-arrow{transform:translate(3px)}.menu-item-submenu.submenu-open .submenu-content{opacity:1;visibility:visible;transform:translate(0)}.submenu-content .menu-item{border-left:3px solid #0000;border-radius:0;padding:.875rem 1.5rem;font-size:.9rem}.submenu-content .menu-item:first-child{border-top-left-radius:8px;border-top-right-radius:8px}.submenu-content .menu-item:last-child{border-bottom-right-radius:8px;border-bottom-left-radius:8px}.menu-section-wrapper{border-bottom:1px solid #0000001a;padding:.5rem 1.5rem 1rem}.menu-content>:last-child,.menu-content>div:last-child{border-bottom:none!important}.menu-controls-section,.menu-actions-section{border-bottom:1px solid #0000001a;padding:.5rem 1.5rem 1rem;display:block}.menu-item-header{color:var(--text-dark,#1a1a1a);text-transform:uppercase;letter-spacing:.5px;cursor:default;align-items:center;gap:1rem;padding:.875rem 0;font-size:.85rem;font-weight:700;display:flex}.menu-item-header:hover{color:var(--text-dark,#1a1a1a)!important;background-color:#0000!important;border-left-color:#0000!important}.menu-item-header iconify-icon{color:var(--text-gray,#333);flex-shrink:0}.menu-item-header:hover iconify-icon{color:var(--text-gray,#333)!important}.menu-item-header span{flex:1}.menu-control-item{justify-content:space-between;align-items:center;padding:.75rem 0;display:flex}.menu-control-label{color:var(--text-dark,#1a1a1a);align-items:center;gap:.75rem;font-size:.9rem;font-weight:500;display:flex}.menu-control-label iconify-icon{color:var(--text-gray,#333)}.menu-action-btn{color:var(--text-dark,#1a1a1a);cursor:pointer;background:#00000008;border:none;border-radius:8px;justify-content:center;align-items:center;gap:1rem;width:100%;margin:.25rem 0;padding:.875rem 1rem;font-size:.9rem;font-weight:500;text-decoration:none;transition:all .2s;display:flex}.menu-action-btn:hover{color:var(--accent-blue,#06c);background:#0066cc14;text-decoration:none}.menu-action-btn iconify-icon{color:var(--text-gray,#333);flex-shrink:0;transition:color .2s}.menu-action-btn:hover iconify-icon{color:var(--accent-blue,#06c)}.menu-pdf-btn:hover,.menu-pdf-btn.pdf-hover-sync{color:#e74c3c!important;background:#fff!important}.menu-pdf-btn:hover iconify-icon,.menu-pdf-btn.pdf-hover-sync iconify-icon{color:#e74c3c!important}.menu-print-btn:hover,.menu-print-btn.print-hover-sync{color:#27ae60!important;background:#fff!important}.menu-print-btn:hover iconify-icon,.menu-print-btn.print-hover-sync iconify-icon{color:#27ae60!important}.section-icon{vertical-align:middle;color:#7d7d7d;margin-right:.5rem}#experience .section-title,#awards .section-title,#courses .section-title,#projects .section-title{margin-bottom:40px!important}html{scroll-behavior:smooth;scroll-padding-top:70px}@media (width<=768px){.navigation-menu{width:240px}.menu-item{padding:.75rem 1rem;font-size:.9rem}.site-title{justify-content:space-between;width:100%}}@media print{.navigation-menu,.hamburger-btn{display:none!important}}[data-color-theme=dark] .navigation-menu,[data-color-theme=dark] .navigation-menu .submenu-content{--text-dark:#1a1a1a;--text-gray:#333}@media (prefers-color-scheme:dark){[data-color-theme=auto] .navigation-menu,[data-color-theme=auto] .navigation-menu .submenu-content{--text-dark:#1a1a1a;--text-gray:#333}}.action-bar,.navigation-menu{transition:transform .3s ease-in-out}.action-bar.header-hidden,.navigation-menu.header-hidden{transform:translateY(-100%)}.back-to-top{background:var(--black-bar,#2b2b2b);color:#fff;cursor:pointer;z-index:99;opacity:.2;border:none;border-radius:50%;justify-content:center;align-items:center;width:50px;height:50px;transition:all .3s;display:flex;position:fixed;bottom:2rem;right:2rem;box-shadow:0 4px 12px #0000004d}.back-to-top:hover{opacity:1;background:#27ae60;transform:translateY(-3px);box-shadow:0 6px 16px #0006}.back-to-top.at-bottom{opacity:1;background:#27ae60}.back-to-top:active{transform:translateY(-1px);box-shadow:0 3px 10px #0000004d}@media (width<=768px){.back-to-top{width:45px;height:45px;bottom:1.5rem;right:1.5rem}}.info-button{background:var(--black-bar,#2b2b2b);color:#fff;cursor:pointer;z-index:99;opacity:.6;border:none;border-radius:50%;justify-content:center;align-items:center;width:50px;height:50px;transition:all .3s;display:flex;position:fixed;bottom:2rem;left:2rem;box-shadow:0 4px 12px #0000004d}.info-button:hover{opacity:1;background:#3498db;transform:translateY(-3px);box-shadow:0 6px 16px #0006}.info-button.at-bottom{opacity:1;background:#3498db}.info-button:active{transform:translateY(-1px);box-shadow:0 3px 10px #0000004d}.download-btn iconify-icon,.print-friendly-btn iconify-icon,.fixed-btn.contact-btn iconify-icon,.shortcuts-btn iconify-icon,.info-button iconify-icon,.back-to-top iconify-icon,.color-theme-switcher iconify-icon{width:24px;height:24px;font-size:24px}.is-mobile-device .shortcuts-btn,.is-mobile-device .zoom-toggle-btn,.is-mobile-device .zoom-control{display:none!important}@media (width<=900px){.zoom-toggle-btn,.zoom-control{display:none!important}.download-btn,.print-friendly-btn,.fixed-btn.contact-btn,.shortcuts-btn,.info-button{width:clamp(36px,2.7vw + 25.7px,50px)!important;height:clamp(36px,2.7vw + 25.7px,50px)!important;position:fixed!important;bottom:1.5rem!important;left:auto!important;right:auto!important;transform:none!important}.back-to-top{width:clamp(36px,2.7vw + 25.7px,50px)!important;height:clamp(36px,2.7vw + 25.7px,50px)!important}.download-btn iconify-icon,.print-friendly-btn iconify-icon,.fixed-btn.contact-btn iconify-icon,.shortcuts-btn iconify-icon,.info-button iconify-icon,.back-to-top iconify-icon,.color-theme-switcher iconify-icon{width:clamp(18px,1.15vw + 13.6px,24px)!important;min-width:0!important;max-width:clamp(18px,1.15vw + 13.6px,24px)!important;height:clamp(18px,1.15vw + 13.6px,24px)!important;font-size:clamp(18px,1.15vw + 13.6px,24px)!important}.download-btn{opacity:1!important;background:#cd6060!important}.print-friendly-btn{opacity:1!important;background:#fff!important}.print-friendly-btn iconify-icon{color:#27ae60!important}.fixed-btn.contact-btn{opacity:1!important;background:#3498db!important}.shortcuts-btn{opacity:1!important;background:#f39c12!important}.info-button{opacity:1!important;background:#3498db!important}.back-to-top{opacity:1!important;background:#27ae60!important}.download-btn{left:calc(50% + -1*clamp(138px,11.7308vw + 93.4231px,199px))!important}.print-friendly-btn{left:calc(50% + -1*clamp(98px,8.26923vw + 66.5769px,141px))!important}.fixed-btn.contact-btn{left:calc(50% + -1*clamp(58px,4.80769vw + 39.7308px,83px))!important}.shortcuts-btn{left:calc(50% + -1*clamp(18px,1.34615vw + 12.8846px,25px))!important}.info-button{left:calc(50% + clamp(62px,5.57692vw + 40.8077px,91px))!important}.back-to-top{display:flex!important;position:fixed!important;bottom:1.5rem!important;left:calc(50% + clamp(102px,9.03846vw + 67.6539px,149px))!important;right:auto!important}.is-mobile-device .download-btn{left:calc(50% + -1*clamp(118px,10vw + 80px,170px))!important}.is-mobile-device .print-friendly-btn{left:calc(50% + -1*clamp(78px,6.53846vw + 53.1538px,112px))!important}.is-mobile-device .fixed-btn.contact-btn{left:calc(50% + -1*clamp(38px,3.07692vw + 26.3077px,54px))!important}.is-mobile-device .info-button{left:calc(50% + clamp(42px,3.84615vw + 27.3846px,62px))!important}.is-mobile-device .back-to-top{left:calc(50% + clamp(82px,7.30769vw + 54.2308px,120px))!important}.back-to-top:hover{opacity:1!important}.download-btn:hover,.download-btn.pdf-hover-sync{background:#cd6060!important;transform:translateY(-3px)!important;box-shadow:0 6px 16px #0006!important}.print-friendly-btn:hover,.print-friendly-btn.print-hover-sync{background:#fff!important;transform:translateY(-3px)!important;box-shadow:0 6px 16px #0006!important}.fixed-btn.contact-btn:hover{background:#3498db!important;transform:translateY(-3px)!important;box-shadow:0 6px 16px #0006!important}.shortcuts-btn:hover{background:#f39c12!important;transform:translateY(-3px)!important;box-shadow:0 6px 16px #0006!important}.info-button:hover{background:#3498db!important;transform:translateY(-3px)!important;box-shadow:0 6px 16px #0006!important}.back-to-top:hover{background:#27ae60!important;transform:translateY(-3px)!important;box-shadow:0 6px 16px #0006!important}.download-btn.at-bottom{opacity:1!important;background:#cd6060!important;transform:none!important}.print-friendly-btn.at-bottom{opacity:1!important;background:#fff!important;transform:none!important}.fixed-btn.contact-btn.at-bottom{opacity:1!important;background:#3498db!important;transform:none!important}.shortcuts-btn.at-bottom{opacity:1!important;background:#f39c12!important;transform:none!important}.info-button.at-bottom{opacity:1!important;background:#3498db!important;transform:none!important}.back-to-top.at-bottom{opacity:1!important;background:#27ae60!important;transform:none!important}.download-btn.footer-hovered,.print-friendly-btn.footer-hovered,.fixed-btn.contact-btn.footer-hovered,.shortcuts-btn.footer-hovered,.info-button.footer-hovered,.back-to-top.footer-hovered,.color-theme-switcher.footer-hovered{opacity:.2!important;pointer-events:none!important}.action-bar.header-hidden,.navigation-menu.header-hidden{transform:translateY(0)!important}footer.no-print{transition:all .3s;position:relative;z-index:1!important;padding-bottom:100px!important}footer.no-print.at-bottom{padding-bottom:110px!important}footer.no-print.at-bottom p,footer.no-print.at-bottom a{transition:all .3s;font-size:1.2em!important;font-weight:500!important}}.zoom-toggle-btn{background:var(--black-bar,#2b2b2b);color:#fff;cursor:pointer;z-index:999;opacity:.6;border:none;border-radius:50%;justify-content:center;align-items:center;width:50px;height:50px;transition:all .3s;display:flex;position:fixed;bottom:10rem;left:2rem;box-shadow:0 4px 12px #0000004d}.zoom-toggle-btn:hover{opacity:1;background:#5c59b6;transform:translateY(-3px);box-shadow:0 6px 16px #0006}.zoom-toggle-btn.at-bottom{opacity:1;background:#5c59b6}.shortcuts-btn{background:var(--black-bar,#2b2b2b);color:#fff;cursor:pointer;z-index:99;opacity:.6;border:none;border-radius:50%;justify-content:center;align-items:center;width:50px;height:50px;transition:all .3s;display:flex;position:fixed;bottom:6rem;left:2rem;box-shadow:0 4px 12px #0000004d}.shortcuts-btn:hover{opacity:1;background:#f39c12;transform:translateY(-3px);box-shadow:0 6px 16px #0006}.shortcuts-btn.at-bottom{opacity:1;background:#f39c12}.shortcuts-btn:active{transform:translateY(-1px)}.print-friendly-btn{background:var(--black-bar,#2b2b2b);color:#fff;cursor:pointer;z-index:999;opacity:.6;border:none;border-radius:50%;justify-content:center;align-items:center;width:50px;height:50px;transition:all .3s;display:flex;position:fixed;bottom:22rem;left:2rem;box-shadow:0 4px 12px #0000004d}.print-friendly-btn iconify-icon{color:#fff}.print-friendly-btn:hover,.print-friendly-btn.print-hover-sync{opacity:1;color:#27ae60;transform:translateY(-3px);box-shadow:0 6px 16px #0006;background:#fff!important}.print-friendly-btn:hover iconify-icon,.print-friendly-btn.print-hover-sync iconify-icon{color:#27ae60}.print-friendly-btn.at-bottom{opacity:1;color:#27ae60;background:#fff!important}.print-friendly-btn.at-bottom iconify-icon{color:#27ae60}.download-btn{background:var(--black-bar,#2b2b2b);color:#fff;cursor:pointer;z-index:999;opacity:.6;background:var(--black-bar,#2b2b2b);opacity:.6;border:none;border-radius:50%;justify-content:center;align-items:center;width:50px;height:50px;transition:all .3s;display:flex;position:fixed;bottom:26rem;left:2rem;box-shadow:0 4px 12px #0000004d}.download-btn:hover,.download-btn.pdf-hover-sync{opacity:1;transform:translateY(-3px);box-shadow:0 6px 16px #0006;background:#cd6060!important}.download-btn iconify-icon{filter:brightness(0)invert();transition:filter .3s}.download-btn:hover iconify-icon{filter:brightness(0)invert()}.download-btn.at-bottom{opacity:1;background:#cd6060!important}ninja-keys{--ninja-font-family:"Quicksand",-apple-system,BlinkMacSystemFont,sans-serif;--ninja-accent-color:#667eea;--ninja-z-index:10000;--ninja-width:640px;--ninja-backdrop-filter:blur(8px);--ninja-modal-background:#fffffff2;--ninja-modal-shadow:0 16px 70px #0003;--ninja-text-color:#1a1a1a;--ninja-secondary-text-color:#666;--ninja-actions-background:#f5f5f5;--ninja-selected-background:#667eea;--ninja-selected-text-color:white;--ninja-key-background:#e0e0e0;--ninja-key-text-color:#333;--ninja-footer-background:#f9f9f9;--ninja-placeholder-color:#999}[data-color-theme=dark] ninja-keys{--ninja-modal-background:#282828f2;--ninja-text-color:#e0e0e0;--ninja-secondary-text-color:#999;--ninja-actions-background:#2a2a2a;--ninja-key-background:#444;--ninja-key-text-color:#e0e0e0;--ninja-footer-background:#2a2a2a;--ninja-placeholder-color:#777}.shortcut-highlight{background:linear-gradient(135deg,#667eea1a 0%,#764ba21a 100%);border-radius:8px;margin:-.5rem;padding:.5rem}.info-modal{background:0 0;border:none;border-radius:24px;width:calc(100% - 2rem);max-width:420px;max-height:fit-content;margin:auto;padding:0;position:fixed;inset:0}.info-modal::backdrop{-webkit-backdrop-filter:blur(10px);background:#000000b3}.info-modal[open]{animation:.3s modalFadeIn}@keyframes modalFadeIn{0%{opacity:0;transform:scale(.9)translateY(20px)}to{opacity:1;transform:scale(1)translateY(0)}}@keyframes modalFadeInMobile{0%{opacity:0;transform:translate(-50%,-50%)scale(.9)}to{opacity:1;transform:translate(-50%,-50%)scale(1)}}.info-modal-content{-webkit-backdrop-filter:blur(20px);background:linear-gradient(135deg,#fffffff2 0%,#ffffffe6 100%);border:1px solid #fffc;border-radius:24px;width:100%;padding:2.5rem;position:relative;box-shadow:0 20px 60px #0000004d,0 0 100px #27ae601a}.info-modal-close{cursor:pointer;width:40px;height:40px;color:var(--text-primary,#1a1a1a);z-index:10;background:#0000000d;border:none;border-radius:50%;justify-content:center;align-items:center;transition:all .2s;display:flex;position:absolute;top:1rem;right:1rem}.info-modal-close:hover{background:#0000001a;transform:rotate(90deg)}.info-modal-header{text-align:center;margin-bottom:2rem}.info-modal-header h2{color:var(--text-primary,#1a1a1a);margin:0 0 1.5rem;font-size:1.5rem;font-weight:600}.info-modal-cv-title{color:#f39c12;letter-spacing:.05em;justify-content:center;align-items:center;gap:.5rem;margin-bottom:0;font-size:1.5rem;font-weight:700;display:flex}#info-modal .info-modal-cv-title{color:#27ae60}.info-modal-photo{object-fit:cover;width:40px;height:53px;box-shadow:none;border:none;border-radius:4px}.photo-bracket-wrapper{align-items:center;padding:0 22px;display:inline-flex;position:relative}.photo-bracket-wrapper:before{content:"{";color:#27ae60;font-size:2rem;font-weight:700;line-height:1;position:absolute;top:8px;left:2px}.photo-bracket-wrapper:after{content:"}";color:#27ae60;font-size:2rem;font-weight:700;line-height:1;position:absolute;top:8px;right:2px}.info-modal-body{color:#333}.info-modal-description{color:#444;margin-bottom:2rem;font-size:1rem;line-height:1.6}.info-modal-description strong{color:#27ae60;font-weight:600}.info-modal-tech{grid-template-columns:repeat(2,1fr);gap:1rem;margin-bottom:2rem;display:grid}.info-tech-item{background:#27ae600d;border:1px solid #27ae601a;border-radius:12px;justify-content:center;align-items:center;gap:.75rem;padding:.75rem;transition:all .3s;display:flex}.info-tech-item:hover{background:#27ae601a;transform:translateY(-2px);box-shadow:0 4px 12px #27ae6033}.info-tech-item iconify-icon{color:#27ae60;flex-shrink:0}.info-tech-item span{color:#333;font-size:.9rem;font-weight:500}.info-modal-github{color:#fff;background:linear-gradient(135deg,#27ae60 0%,#229954 100%);border-radius:12px;justify-content:center;align-items:center;gap:.75rem;padding:1rem 1.5rem;font-size:1rem;font-weight:600;text-decoration:none;transition:all .3s;display:flex;box-shadow:0 4px 15px #27ae604d}.info-modal-github:hover{background:linear-gradient(135deg,#229954 0%,#27ae60 100%);transform:translateY(-2px);box-shadow:0 8px 25px #27ae6066}.info-modal-github:active{transform:translateY(0);box-shadow:0 4px 15px #27ae604d}.info-modal-github-subtext{text-align:center;color:#666;margin-top:1.5rem;margin-bottom:1rem;font-size:.9rem;font-style:italic}@media (width<=768px){.info-modal{width:calc(100vw - 2rem)!important;max-width:calc(100vw - 2rem)!important;max-height:calc(100vh - 2rem)!important;margin:0!important;position:fixed!important;inset:50% auto auto 50%!important;transform:translate(-50%,-50%)!important}.info-modal[open]{animation:.3s modalFadeInMobile}.info-modal-content{max-width:100%;max-height:calc(100vh - 2rem);padding:1.5rem 1rem;overflow-y:auto}.info-modal-close{width:32px;height:32px;top:.5rem;right:.5rem}.info-modal-close iconify-icon{width:20px;height:20px}.info-modal-header{margin-bottom:1.5rem}.info-modal-header h2{margin-bottom:1rem;font-size:1.05rem}.info-modal-cv-title{font-size:.95rem}.info-modal-photo{width:30px;height:40px}.photo-bracket-wrapper{padding:0 18px}.photo-bracket-wrapper:before,.photo-bracket-wrapper:after{font-size:1.5rem;top:5px}.info-modal-description{margin-bottom:1.5rem;font-size:.85rem;line-height:1.5}.info-modal-tech{grid-template-columns:1fr;gap:.75rem;margin-bottom:1.5rem}.info-tech-item{gap:.6rem;padding:.6rem}.info-tech-item iconify-icon{width:24px;height:24px}.info-tech-item span{font-size:.8rem}.info-modal-github-subtext{margin-top:1rem;margin-bottom:.75rem;font-size:.8rem}.info-modal-github{gap:.5rem;padding:.75rem 1.25rem;font-size:.875rem}.info-modal-github iconify-icon{width:20px;height:20px}}#shortcuts-modal{max-width:900px;max-height:80vh}.keyboard-icon-wrapper{align-items:center;padding:0 22px;display:inline-flex;position:relative}.keyboard-icon-wrapper:before{content:"{";color:#575757;font-size:2rem;font-weight:700;line-height:1;position:absolute;top:-3px;left:2px}.keyboard-icon-wrapper:after{content:"}";color:#575757;font-size:2rem;font-weight:700;line-height:1;position:absolute;top:-3px;right:2px}.keyboard-icon-wrapper iconify-icon{color:#f39c12;position:relative;top:1px}#shortcuts-modal .info-modal-cv-title{margin-bottom:.5rem}#shortcuts-modal .info-modal-body{grid-template-columns:1fr 1fr;gap:1.2rem 1.5rem;margin-top:1.5rem;display:grid}.shortcuts-section{background:#f8f9fa;border:1px solid #e1e4e8;border-radius:8px;margin-top:0;padding:1rem;box-shadow:0 1px 3px #0000000d}.shortcuts-section:first-of-type{margin-top:0}.shortcuts-section-title{color:#827a6e;border-bottom:2px solid #827a6e33;align-items:center;gap:.5rem;margin-bottom:.75rem;padding-bottom:.5rem;font-size:1.05rem;font-weight:600;display:flex}.shortcuts-section-title iconify-icon{color:#f39c12}.shortcuts-list{flex-direction:column;gap:.5rem;display:flex}.shortcut-item{justify-content:space-between;align-items:center;gap:1rem;padding:.5rem 0;display:flex}.shortcut-keys{flex-wrap:wrap;align-items:center;gap:.4rem;display:flex}.shortcut-keys kbd{white-space:nowrap;text-align:center;color:#3498db;background:#3498db14;border:1px solid #3498db59;border-radius:6px;justify-content:center;align-items:center;gap:.2rem;min-width:2rem;padding:.3rem .6rem;font-family:Monaco,Courier New,monospace;font-size:.75rem;font-weight:600;transition:all .2s;display:inline-flex;box-shadow:0 2px 4px #3498db1f,inset 0 -1px #3498db40}.shortcut-keys kbd iconify-icon{color:inherit;vertical-align:middle;display:inline-flex}.shortcut-item:hover .shortcut-keys kbd{background:#3498db26;border-color:#3498db80;box-shadow:0 2px 6px #3498db40}.shortcut-desc{color:var(--text-gray,#333);flex:1;font-size:.95rem;line-height:1.4}@media (width<=768px){#shortcuts-modal{width:calc(100vw - 2rem)!important;max-width:calc(100vw - 2rem)!important;max-height:calc(100vh - 2rem)!important}#shortcuts-modal .info-modal-body{grid-template-columns:1fr;gap:1.5rem}}@media (width>=769px) and (width<=1024px){#shortcuts-modal{max-width:700px}#shortcuts-modal .info-modal-body{grid-template-columns:1fr 1fr;gap:1.2rem 1.5rem}.shortcuts-section-title{font-size:1rem}.shortcut-item{flex-direction:column;align-items:flex-start;gap:.35rem}.shortcut-keys kbd{padding:.2rem .4rem;font-size:.7rem}.shortcut-desc{font-size:.9rem}}.pdf-download-modal{width:calc(100% - 2rem);max-width:800px}.pdf-modal-subtitle{color:var(--text-gray,#333);margin-top:.5rem;font-size:.95rem;font-weight:400}.pdf-options-grid{grid-template-columns:repeat(3,1fr);gap:32px;margin:2rem 0 1.5rem;display:grid}.pdf-option-card{cursor:pointer;background:#fff;border:2px solid #0000;border-radius:12px;flex-direction:column;gap:10px;padding:12px;transition:all .25s;display:flex;position:relative}.pdf-option-card:hover{border-color:#e0e0e0;transform:translateY(-2px);box-shadow:0 4px 12px #0000001a}.pdf-option-card:focus{outline-offset:2px;outline:2px solid #0000}.pdf-option-recommended:focus{outline:none}.pdf-option-card.selected:not(.pdf-option-recommended){background:#fff5f5;border-color:#ef4444;box-shadow:0 6px 16px #ef444433}.pdf-thumbnail{background:#fff;border:1px solid #e0e0e0;border-radius:8px;flex-direction:column;gap:10px;height:220px;padding:12px;display:flex;position:relative;overflow:hidden}.pdf-thumbnail .skeleton-block{background:linear-gradient(90deg,#f0f0f0 25%,#e8e8e8 50%,#f0f0f0 75%) 0 0/200% 100%;border-radius:4px;animation:1.8s ease-in-out infinite skeleton-shimmer}.custom-placeholder{color:#999;text-align:center;flex-direction:column;justify-content:center;align-items:center;height:100%;display:flex}.custom-placeholder iconify-icon{opacity:.5;margin-bottom:12px}.custom-placeholder p{color:#666;margin:0;font-size:.9rem;font-weight:500}.thumbnail-badge{color:#fff;letter-spacing:.5px;text-transform:uppercase;background:#000000bf;border-radius:4px;padding:4px 8px;font-size:.7rem;font-weight:600;position:absolute;top:8px;right:8px}.thumbnail-badge.badge-recommended{background:linear-gradient(135deg,#f39c12 0%,#e67e22 100%);box-shadow:0 2px 8px #f39c124d}.recommended-ribbon{color:#fff;letter-spacing:.5px;text-transform:uppercase;z-index:2;background:linear-gradient(135deg,#f39c12 0%,#e67e22 100%);border-radius:0 0 8px 8px;padding:3px 12px;font-size:.65rem;font-weight:700;position:absolute;top:-4px;left:50%;transform:translate(-50%);box-shadow:0 2px 8px #f39c124d}.recommended-badge{margin-left:.25rem;font-size:1rem;display:inline-block}.pdf-option-recommended{z-index:1;position:relative;overflow:visible;transform:scale(1.12);box-shadow:0 2px 8px #f39c1214;border:2px solid #f39c1226!important}.pdf-option-recommended:hover{transform:scale(1.12)translateY(-2px);box-shadow:0 4px 16px #f39c1226}.pdf-option-recommended.selected{background:#fffbf5!important;border:2px solid #f39c12!important;box-shadow:0 6px 16px #f39c124d!important}.pdf-option-info{text-align:center}.pdf-option-info h3{color:var(--text-dark,#1a1a1a);margin:0 0 4px;font-size:1.1rem;font-weight:600}.pdf-option-info p{color:var(--text-gray,#333);margin:0;font-size:.875rem;line-height:1.4}.pdf-option-badge{opacity:0;color:#4caf50;transition:all .25s;position:absolute;top:8px;left:8px;transform:scale(.8)}.pdf-option-card.selected .pdf-option-badge{opacity:1;transform:scale(1)}.pdf-modal-footer{border-top:1px solid #e0e0e0;justify-content:center;margin-top:.5rem;padding-top:1rem;display:flex}.pdf-download-btn{cursor:pointer;border:none;border-radius:8px;align-items:center;gap:8px;padding:12px 32px;font-family:inherit;font-size:1rem;font-weight:600;transition:all .25s;display:inline-flex}.pdf-download-btn iconify-icon{flex-shrink:0}.pdf-download-btn:disabled{color:#999;cursor:not-allowed;opacity:.6;background:#e0e0e0}.pdf-download-btn:not(:disabled){color:#fff;background:#ef4444}.pdf-download-btn:not(:disabled):hover{background:#dc2626;transform:translateY(-1px);box-shadow:0 4px 12px #ef44444d}.pdf-download-btn:not(:disabled):active{transform:translateY(0)}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}@media (width>=480px) and (width<=767px){.pdf-options-grid{grid-template-columns:repeat(2,1fr);gap:16px}.pdf-option-card[data-cv-format=custom]{grid-column:1/-1}.pdf-thumbnail{height:220px}}@media (width<=768px){.pdf-download-modal{width:calc(100vw - 2rem)!important;max-width:calc(100vw - 2rem)!important;max-height:calc(100vh - 2rem)!important}.info-modal-content{padding:1.5rem 1rem}.pdf-modal-subtitle{display:none}.pdf-download-modal .info-modal-header{margin-bottom:1rem}.pdf-download-modal .info-modal-header h2{margin-bottom:0;font-size:1.25rem}.pdf-options-grid{flex-direction:column;gap:10px;margin:1rem 0;display:flex}.pdf-option-recommended{transform:none}.pdf-option-recommended:hover{transform:translateY(-2px)}.pdf-option-card{flex-direction:row;align-items:center;gap:12px;padding:12px}.pdf-thumbnail{display:none}.pdf-option-card:before{content:attr(data-cv-format);color:#666;text-transform:uppercase;background:#f5f5f5;border-radius:8px;flex-shrink:0;justify-content:center;align-items:center;width:50px;height:50px;font-size:.65rem;font-weight:700;display:flex}.pdf-option-card[data-cv-format=short]:before{content:"4\a PAGES";white-space:pre;line-height:1.3}.pdf-option-card[data-cv-format=default]:before{content:"5\a PAGES";white-space:pre;color:#f39c12;background:#fff8e6;line-height:1.3}.pdf-option-card[data-cv-format=long]:before{content:"9\a PAGES";white-space:pre;line-height:1.3}.pdf-option-info{text-align:left;flex:1}.pdf-option-info h3{margin-bottom:2px;font-size:.9rem}.pdf-option-info p{font-size:.75rem}.pdf-option-badge{margin-left:auto;position:static}.pdf-download-btn{justify-content:center;width:100%;padding:10px 20px;font-size:.9rem}.pdf-modal-footer{margin-top:.5rem;padding-top:.75rem}.info-modal-header h2{color:#000;opacity:1}.info-modal-close{color:#000;background:#00000014}.info-modal-close:hover{background:#00000026}}@media (prefers-reduced-motion:reduce){.pdf-thumbnail .skeleton-block{background:#e8e8e8;animation:none}.pdf-option-card,.pdf-option-badge,.pdf-download-btn{transition:none}.pdf-loading-overlay,.pdf-loading-spinner{animation:none}}.pdf-loading-overlay{-webkit-backdrop-filter:blur(8px);z-index:100;background:#fffffff2;border-radius:24px;flex-direction:column;justify-content:center;align-items:center;width:100%;height:100%;animation:.3s overlayFadeIn;display:none;position:absolute;top:0;left:0}.pdf-loading-overlay.active{display:flex}@keyframes overlayFadeIn{0%{opacity:0}to{opacity:1}}.pdf-loading-content{text-align:center;max-width:300px;padding:2rem}.pdf-loading-spinner{border:4px solid #ef444433;border-top-color:#ef4444;border-radius:50%;width:64px;height:64px;margin:0 auto 1.5rem;animation:1s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}.pdf-loading-title{color:var(--text-primary,#1a1a1a);margin:0 0 .5rem;font-size:1.25rem;font-weight:600}.pdf-loading-message{color:var(--text-gray,#333);margin:0 0 .5rem;font-size:.95rem;line-height:1.5}.pdf-loading-estimate{color:#999;margin:1.5rem 0 0;font-size:.85rem;font-style:italic}.info-modal-content.loading-active>:not(.pdf-loading-overlay){filter:blur(3px);pointer-events:none}[data-color-theme=dark] .pdf-download-modal .pdf-modal-subtitle{color:#333!important}[data-color-theme=dark] .pdf-download-modal .pdf-option-info h3{color:#1a1a1a!important}[data-color-theme=dark] .pdf-download-modal .pdf-option-info p{color:#333!important}[data-color-theme=dark] .pdf-download-modal .custom-placeholder p{color:#666!important}[data-color-theme=dark] .pdf-download-modal .pdf-loading-title{color:#1a1a1a!important}[data-color-theme=dark] .pdf-download-modal .pdf-loading-message{color:#333!important}[data-color-theme=dark] .pdf-download-modal .pdf-loading-estimate{color:#999!important}.error-toast,.success-toast,.toast{z-index:10000;-webkit-backdrop-filter:blur(10px);border-radius:12px;align-items:center;gap:.75rem;min-width:320px;max-width:420px;padding:1rem 1.25rem;font-size:.95rem;line-height:1.5;animation:.3s toastSlideIn;display:none;position:fixed;bottom:2rem;right:2rem;box-shadow:0 8px 24px #00000026,0 2px 6px #0000001a}.error-toast.show,.success-toast.show,.toast.show{animation:5s forwards toastLifecycle;display:flex}.error-toast{color:#fff;background:linear-gradient(135deg,#dc3545f2 0%,#c82333f2 100%);border-left:4px solid #fff}.success-toast{color:#fff;background:linear-gradient(135deg,#28a745f2 0%,#198754f2 100%);border-left:4px solid #fff}.info-toast{color:#fff;background:linear-gradient(135deg,#0d6efdf2 0%,#0a58caf2 100%);border-left:4px solid #fff}.warning-toast{color:#333;background:linear-gradient(135deg,#ffc107f2 0%,#ffa726f2 100%);border-left:4px solid #333}.toast-icon,.error-icon,.success-icon,.info-icon{flex-shrink:0;font-size:1.5rem;line-height:1}.toast-content{flex-direction:column;flex:1;gap:.25rem;display:flex}.toast-title{margin:0;font-size:1rem;font-weight:600}.toast-message{opacity:.95;margin:0;font-size:.875rem}.error-close,.toast-close{color:inherit;cursor:pointer;background:#fff3;border:none;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;width:28px;height:28px;font-size:1.5rem;font-weight:300;line-height:1;transition:all .2s;display:flex}.error-close:hover,.toast-close:hover{background:#ffffff4d;transform:rotate(90deg)}.toast-progress{background:#ffffff4d;border-radius:0 0 12px 12px;width:100%;height:3px;position:absolute;bottom:0;left:0;overflow:hidden}.toast-progress-bar{background:#fffc;border-radius:0 0 12px 12px;height:100%;animation:5s linear forwards progressShrink}@keyframes toastSlideIn{0%{opacity:0;transform:translate(100%)translateY(0)}to{opacity:1;transform:translate(0)translateY(0)}}@keyframes toastSlideOut{0%{opacity:1;transform:translate(0)translateY(0)}to{opacity:0;transform:translate(100%)translateY(0)}}@keyframes toastLifecycle{0%{opacity:1;transform:translate(0)translateY(0)}85%{opacity:1;transform:translate(0)translateY(0)}to{opacity:0;transform:translate(100%)translateY(0)}}@keyframes progressShrink{0%{width:100%}to{width:0%}}@media (width<=540px){.error-toast,.success-toast,.toast{min-width:unset;max-width:unset;padding:.875rem 1rem;font-size:.875rem;bottom:1rem;left:1rem;right:1rem}.toast-icon,.error-icon,.success-icon{font-size:1.25rem}.toast-title{font-size:.95rem}.toast-message{font-size:.8rem}}@media (prefers-reduced-motion:reduce){.error-toast,.success-toast,.toast{animation:none}.error-toast.show,.success-toast.show,.toast.show{opacity:1;animation:none}.toast-progress-bar{animation:none}}@media print{.error-toast,.success-toast,.toast{display:none!important}}.zoom-control{z-index:900;-webkit-backdrop-filter:blur(10px);opacity:.7;cursor:move;-webkit-user-select:none;user-select:none;background:#808080b3;border-radius:50px;align-items:center;gap:.75rem;padding:.65rem 1.25rem;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,system-ui,sans-serif;transition:all .3s;display:flex;position:fixed;bottom:100px;left:50%;transform:translate(-50%);box-shadow:0 3px 10px #0003}.zoom-control.zoom-highlight{opacity:1;background:#5b5b5b;box-shadow:0 0 10px 4px #0171bccc}.zoom-hidden{display:none!important}.zoom-close-btn{color:#fffc;cursor:pointer;z-index:1;opacity:.7;background:#80808099;border:2px solid #ffffff4d;border-radius:50%;justify-content:center;align-items:center;width:24px;height:24px;padding:0;transition:all .2s;display:flex;position:absolute;top:-8px;right:-8px}.zoom-close-btn:hover{color:#fff;opacity:1;background:#dc3545e6;transform:scale(1.1);box-shadow:0 2px 8px #dc354566}.zoom-control:hover{opacity:1;background:#5b5b5b;box-shadow:0 4px 15px #0000004d}.zoom-value{color:#fff;text-align:center;min-width:30px;font-size:.95rem;font-weight:500}.zoom-value-current{color:#fff;min-width:35px;font-size:1.05rem;font-weight:600}.zoom-slider{-webkit-appearance:none;appearance:none;cursor:pointer;background:#c8c8c880;border-radius:3px;outline:none;width:180px;height:5px;transition:all .3s}.zoom-control:hover .zoom-slider,.zoom-slider:hover{background:#91beec}.zoom-slider:focus{outline-offset:2px;outline:2px solid #fff9}.zoom-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;cursor:pointer;background:#fff;border:2px solid #b4b4b4cc;border-radius:50%;width:18px;height:18px;transition:all .2s;box-shadow:0 2px 6px #0000004d}.zoom-slider::-webkit-slider-thumb:hover{border-color:#c8c8c8;transform:scale(1.1);box-shadow:0 3px 8px #0006}.zoom-slider::-webkit-slider-thumb:active{transform:scale(1.05)}.zoom-slider::-moz-range-thumb{cursor:pointer;background:#fff;border:2px solid #b4b4b4cc;border-radius:50%;width:18px;height:18px;transition:all .2s;box-shadow:0 2px 6px #0000004d}.zoom-slider::-moz-range-thumb:hover{border-color:#c8c8c8;transform:scale(1.1);box-shadow:0 3px 8px #0006}.zoom-slider::-moz-range-thumb:active{transform:scale(1.05)}.zoom-slider::-moz-range-track{background:#c8c8c880;border-radius:3px;height:5px;transition:all .3s}.zoom-control:hover .zoom-slider::-moz-range-track,.zoom-slider:hover::-moz-range-track{background:#3b82f6}.zoom-reset-btn{color:#fffc;cursor:pointer;background:#c8c8c833;border:2px solid #dcdcdc4d;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;min-width:44px;min-height:44px;margin:0 -5px 0 10px;padding:.5rem;font-size:.85rem;font-weight:700;transition:all .3s;display:flex}.zoom-reset-btn #zoom-value-current{color:inherit;font-size:inherit;font-weight:inherit;min-width:auto}.zoom-reset-btn:hover{color:#fff;background:#dcdcdc66;border-color:#f0f0f099}.zoom-reset-btn.zoom-not-default:hover{color:#fff;background:#74aacd;border-color:#74aacd}.zoom-reset-btn:active{transform:scale(.95)}.zoom-reset-btn:focus{outline-offset:2px;outline:2px solid #fff9}@media (width<=480px){.zoom-control{gap:.35rem;padding:.35rem .7rem;bottom:40px}.zoom-slider{width:100px}.zoom-value-min,.zoom-value-max{display:none}}.cv-page-content-wrapper{position:relative}.hidden{display:none!important}#contact-modal{max-width:520px}#contact-modal .info-modal-cv-title,#contact-modal .info-modal-cv-title iconify-icon{color:#3498db}.contact-modal-description{color:#555;text-align:center;margin-bottom:1.5rem;font-size:.95rem;line-height:1.6}.form-group{margin-bottom:1.25rem}.form-group:last-of-type{margin-bottom:1rem}.form-label{color:#333;margin-bottom:.4rem;font-size:.9rem;font-weight:600;display:block}.required-indicator{color:#ef4444;margin-left:.2rem}.form-input,.form-textarea{color:#333;box-sizing:border-box;background:#fff;border:2px solid #e0e0e0;border-radius:8px;width:100%;padding:.75rem;font-family:inherit;font-size:.95rem;transition:all .2s}.form-input:focus,.form-textarea:focus{border-color:#3498db;outline:none;box-shadow:0 0 0 3px #3498db1a}.form-input::placeholder,.form-textarea::placeholder{color:#999;opacity:1}.form-textarea{resize:vertical;min-height:120px;line-height:1.5}.form-input:invalid:not(:placeholder-shown),.form-textarea:invalid:not(:placeholder-shown){border-color:#ef4444}.form-input:invalid:focus:not(:placeholder-shown),.form-textarea:invalid:focus:not(:placeholder-shown){box-shadow:0 0 0 3px #ef44441a}.contact-response{min-height:0;margin-bottom:1rem}.contact-message{border-radius:8px;align-items:flex-start;gap:.75rem;margin-bottom:1rem;padding:1rem;animation:.3s messageSlideIn;display:flex}@keyframes messageSlideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.contact-message iconify-icon{flex-shrink:0;margin-top:.1rem}.contact-message-content{flex:1}.contact-message-content strong{margin-bottom:.25rem;font-size:.95rem;display:block}.contact-message-content p{margin:0;font-size:.875rem;line-height:1.5}.contact-success{color:#155724;background:linear-gradient(135deg,#28a7451a 0%,#1987540d 100%);border:2px solid #28a7454d}.contact-success iconify-icon{color:#28a745}.contact-error{color:#721c24;background:linear-gradient(135deg,#dc35451a 0%,#c823330d 100%);border:2px solid #dc35454d}.contact-error iconify-icon{color:#dc3545}.form-actions{margin-bottom:.75rem}.contact-submit-btn{cursor:pointer;color:#fff;background:linear-gradient(135deg,#3498db 0%,#2980b9 100%);border:none;border-radius:8px;justify-content:center;align-items:center;gap:.5rem;width:100%;padding:.875rem 1.5rem;font-family:inherit;font-size:1rem;font-weight:600;transition:all .2s;display:flex;box-shadow:0 4px 12px #3498db4d}.contact-submit-btn:hover{background:linear-gradient(135deg,#2980b9 0%,#3498db 100%);transform:translateY(-2px);box-shadow:0 6px 16px #3498db66}.contact-submit-btn:active{transform:translateY(0);box-shadow:0 4px 12px #3498db4d}.contact-submit-btn:disabled{color:#999;cursor:not-allowed;box-shadow:none;background:#e0e0e0;transform:none}.contact-submit-btn .htmx-indicator{display:none}.contact-submit-btn.htmx-request .htmx-indicator{display:inline-flex}.contact-submit-btn.htmx-request>span{opacity:.7}.spinning{animation:1s linear infinite spin}.form-note{color:#666;text-align:center;margin:0;font-size:.8rem;font-style:italic}.fixed-btn.contact-btn{background:var(--black-bar,#2b2b2b);color:#fff;cursor:pointer;z-index:999;opacity:.6;border:none;border-radius:50%;justify-content:center;align-items:center;width:50px;height:50px;transition:all .3s;display:flex;bottom:18rem;left:2rem;box-shadow:0 4px 12px #0000004d;position:fixed!important}.fixed-btn.contact-btn:hover{opacity:1;background:#3498db;transform:translateY(-3px);box-shadow:0 6px 16px #0006}.fixed-btn.contact-btn.at-bottom{opacity:1;background:#3498db!important}@media (width<=768px){#contact-modal{width:calc(100vw - 2rem)!important;max-width:calc(100vw - 2rem)!important;max-height:calc(100vh - 2rem)!important}#contact-modal .info-modal-content{max-height:calc(100vh - 2rem);padding:1.5rem 1rem;overflow-y:auto}#contact-modal .info-modal-header h2{font-size:1.25rem}#contact-modal .info-modal-cv-title{font-size:1rem}.contact-modal-description{margin-bottom:1.25rem;font-size:.875rem}.form-group{margin-bottom:1rem}.form-label{font-size:.85rem}.form-input,.form-textarea{padding:.65rem;font-size:.9rem}.contact-submit-btn{padding:.75rem 1.25rem;font-size:.95rem}.form-note{font-size:.75rem}.fixed-btn.contact-btn{width:45px;height:45px;bottom:13.5rem;left:1.5rem}}@media (prefers-reduced-motion:reduce){.contact-message,.spinning{animation:none}.contact-submit-btn,.form-input,.form-textarea{transition:none}}@media print{.contact-btn,#contact-modal{display:none!important}}.icon-sprite{vertical-align:middle;background-repeat:no-repeat;background-size:auto 50px;width:50px;height:50px;display:inline-block}.icon-company{background-image:url(/static/images/sprites/sprite-companies.png);background-position-x:calc(var(--icon-index,0)*-50px)}.icon-project{background-image:url(/static/images/sprites/sprite-projects.png);background-position-x:calc(var(--icon-index,0)*-50px)}.icon-course{background-image:url(/static/images/sprites/sprite-courses.png);background-position-x:calc(var(--icon-index,0)*-50px)}@media (-webkit-device-pixel-ratio>=2),(resolution>=192dpi){.icon-company{background-image:url(/static/images/sprites/sprite-companies@2x.png);background-size:auto 50px}.icon-project{background-image:url(/static/images/sprites/sprite-projects@2x.png);background-size:auto 50px}.icon-course{background-image:url(/static/images/sprites/sprite-courses@2x.png);background-size:auto 50px}}.icon-sprite.icon-small{background-size:auto 32px;width:32px;height:32px}.icon-sprite.icon-small.icon-company,.icon-sprite.icon-small.icon-project,.icon-sprite.icon-small.icon-course{background-position-x:calc(var(--icon-index,0)*-32px)}.icon-sprite.icon-large{background-size:auto 64px;width:64px;height:64px}.icon-sprite.icon-large.icon-company,.icon-sprite.icon-large.icon-project,.icon-sprite.icon-large.icon-course{background-position-x:calc(var(--icon-index,0)*-64px)}.icon-sprite.icon-section{border:1px solid var(--icon-border,#ddd);box-sizing:border-box;background-color:#0000;background-position:0 0;background-size:auto 60px;background-origin:content-box;background-clip:content-box;border-radius:4px;width:80px;height:80px;padding:10px}.icon-sprite.icon-section.icon-company,.icon-sprite.icon-section.icon-project,.icon-sprite.icon-section.icon-course{background-position-x:calc(var(--icon-index,0)*-60px)}@media (-webkit-device-pixel-ratio>=2),(resolution>=192dpi){.icon-sprite.icon-small{background-size:auto 32px}.icon-sprite.icon-small.icon-company,.icon-sprite.icon-small.icon-project,.icon-sprite.icon-small.icon-course{background-position-x:calc(var(--icon-index,0)*-32px)}.icon-sprite.icon-large{background-size:auto 64px}.icon-sprite.icon-large.icon-company,.icon-sprite.icon-large.icon-project,.icon-sprite.icon-large.icon-course{background-position-x:calc(var(--icon-index,0)*-64px)}.icon-sprite.icon-section{background-position:0 0;background-size:auto 60px;padding:10px}.icon-sprite.icon-section.icon-company,.icon-sprite.icon-section.icon-project,.icon-sprite.icon-section.icon-course{background-size:auto 60px;background-position-x:calc(var(--icon-index,0)*-60px)}}@media (width>=769px) and (width<=1280px){.cv-header-left{padding-right:0}.cv-photo{float:right;shape-outside:margin-box;margin:0 0 15px 20px;position:static}.cv-name,.years-experience{text-align:right}.intro-text{margin-top:15px}.cv-header-left:after{content:"";clear:both;display:table}}@media (width>=901px) and (width<=1023px){html{font-size:14px}.cv-name{font-size:1.8em}.sidebar-title{font-size:.95rem}.sidebar-content{font-size:.9rem}.selector-label{opacity:0;white-space:nowrap;max-width:0;transition:all .3s;overflow:hidden}.selector-group:hover .selector-label{opacity:1;max-width:200px;margin-right:.75rem}.language-selector .selector-btn{justify-content:center;align-items:center;min-width:50px;padding:.4rem 1rem;font-size:0;transition:font-size .3s;display:inline-flex;position:relative;overflow:visible}.language-selector .selector-btn:before{content:attr(data-short);opacity:1;text-align:center;width:100%;font-size:1rem;transition:opacity .3s;display:block}.language-selector .selector-btn:hover{min-width:auto;font-size:1rem}.language-selector .selector-btn:hover:before{content:"";opacity:0}.action-btn{white-space:nowrap;text-indent:0;width:45px;transition:width .3s,padding .3s;position:relative;overflow:hidden}.action-btn iconify-icon{flex-shrink:0}.action-btn{justify-content:center;padding:0 .65rem;font-size:0}.action-btn:hover{gap:.5rem;width:auto;padding:.65rem 1.5rem;font-size:.95rem}}@media (width>=1024px) and (width<=1280px){html{font-size:14px}.cv-name{font-size:1.8em}.sidebar-title{font-size:.95rem}.sidebar-content{font-size:.9rem}.selector-label{opacity:0;white-space:nowrap;max-width:0;transition:all .3s;overflow:hidden}.selector-group:hover .selector-label{opacity:1;max-width:200px;margin-right:.75rem}.language-selector .selector-btn{justify-content:center;align-items:center;min-width:50px;padding:.4rem 1rem;font-size:0;transition:font-size .3s;display:inline-flex;position:relative;overflow:visible}.language-selector .selector-btn:before{content:attr(data-short);opacity:1;text-align:center;width:100%;font-size:1rem;transition:opacity .3s;display:block}.language-selector .selector-btn:hover{min-width:auto;font-size:1rem}.language-selector .selector-btn:hover:before{content:"";opacity:0}.action-btn{white-space:nowrap;text-indent:0;width:45px;transition:width .3s,padding .3s;position:relative;overflow:hidden}.action-btn iconify-icon{flex-shrink:0}.action-btn{justify-content:center;padding:0 .65rem;font-size:0}.action-btn:hover{gap:.5rem;width:auto;padding:.65rem 1.5rem;font-size:.95rem}}@media (width<=768px){.page-1 .page-content,.page-2 .page-content{grid-template-rows:auto auto;grid-template-columns:1fr!important}.page-1 .cv-sidebar-left{order:1;grid-area:1/1}.page-1 .cv-main{order:2;grid-area:2/1}.page-2 .cv-main{order:1;grid-area:1/1}.page-2 .cv-sidebar-right{order:2;grid-area:2/1}.cv-name{text-align:center;font-size:1.6rem}.years-experience{text-align:center;font-size:1.1em}.section-title,.sidebar-title{font-size:1.2em}.experience-period,.experience-separator,.experience-location,.experience-duration,.position{font-size:.95rem}.short-desc,.responsibilities li{font-size:.85rem}.intro-text,.summary-text{text-align:justify;font-size:.85rem;line-height:1.5}.intro-text{width:100%;margin-top:0}.course-desc,.project-desc{line-height:1.5;text-align:left!important;font-size:.85rem!important}.cv-header-content{flex-direction:column;align-items:center;gap:1rem}.cv-header-left{width:100%;padding-right:0;position:static}.cv-photo{text-align:center;width:auto;max-width:250px;height:auto;margin:1.5rem auto;position:static;top:auto;right:auto}.cv-photo img{width:100%;height:auto;max-height:none}.company-logo,.course-icon,.project-icon,.award-logo{flex-shrink:0;width:60px!important;height:60px!important}.company-logo img,.course-icon img,.project-icon img,.award-logo img{object-fit:contain;width:60px!important;height:60px!important}.experience-item,.course-item,.project-item,.award-item{border-bottom:1px solid #0000001a;flex-direction:row;align-items:flex-start;display:flex;gap:1rem!important;margin-bottom:2rem!important;padding-bottom:1.5rem!important}.experience-item{margin-bottom:1.8rem!important}.experience-content,.course-content,.project-content,.award-content{flex:1;min-width:0}.course-title,.project-title,.award-item strong{line-height:1.4;font-size:.95rem!important}.course-item small,.project-item small,.award-item small{font-size:.8rem!important}.course-desc,.project-desc,.award-desc{line-height:1.5;font-size:.85rem!important}.responsibilities li:has(img),.responsibilities li:has(iconify-icon){grid-template-columns:60px 1fr!important;gap:.75rem!important;margin-bottom:.75rem!important}.responsibilities li img,.responsibilities li iconify-icon.default-company-icon{width:60px!important;height:60px!important}.language-item,.reference-item,.other-content{margin-bottom:0!important;margin-left:1rem!important;font-size:.85rem!important;line-height:1.4!important}.cv-sidebar{padding:0!important}.sidebar-accordion summary.sidebar-accordion-header{color:#ccc;cursor:pointer;text-transform:uppercase;-webkit-user-select:none;user-select:none;border-bottom:1px solid #34495e;border-radius:0;justify-content:space-between;align-items:center;gap:.3rem;margin-bottom:0;padding:8px 15px;font-size:.85em;font-weight:400;list-style:none;background:#303030!important;display:flex!important}.sidebar-accordion-content{margin:0;padding:.5rem 1rem;transition:max-height .3s ease-in-out;overflow:hidden}.sidebar-section{margin-bottom:.5rem!important}.sidebar-accordion summary.sidebar-accordion-header::-webkit-details-marker,.sidebar-accordion summary.sidebar-accordion-header::marker{display:none}.sidebar-accordion[open] summary.sidebar-accordion-header .chevron{transition:transform .3s;transform:rotate(180deg)}.sidebar-accordion summary.sidebar-accordion-header .chevron{color:#ccc;transition:transform .3s}.sidebar-accordion:not([open]) .sidebar-accordion-content{opacity:0;max-height:0}.sidebar-accordion[open] .sidebar-accordion-content{opacity:1;max-height:2000px}}@media (width<=540px){.action-bar-content{grid-template-columns:1fr;gap:0;padding:0}.view-controls-center,.action-buttons-right{display:none}.site-title{justify-content:space-between;align-items:center;gap:.5rem;width:100%;padding:0 .5rem;display:flex}.site-title-left{flex:1px;align-items:center;gap:.5rem;min-width:0;display:flex}.site-title-link{flex:auto;min-width:0;overflow:hidden}.site-title-text{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.language-selector{flex:0 0 35%;justify-content:flex-end;gap:.25rem;margin-left:auto;margin-right:0;padding-left:0;display:flex}.site-title-year,.site-logo-link{display:none}.site-icon-mobile{display:inline-flex}.language-selector .selector-btn{justify-content:center;align-items:center;min-width:40px;padding:.4rem .75rem;font-size:0;transition:font-size .3s;display:inline-flex;position:relative;overflow:visible}.language-selector .selector-btn:before{content:attr(data-short);opacity:1;text-align:center;width:100%;font-size:.95rem;transition:opacity .3s;display:block}.language-selector .selector-btn:hover{min-width:40px;font-size:0}.language-selector .selector-btn:hover:before{content:attr(data-short);opacity:1}@supports (backdrop-filter:blur(20px)){.fixed-buttons-backdrop{-webkit-backdrop-filter:blur(20px)saturate(180%);z-index:98;pointer-events:none;background:#fffc;border-top:.5px solid #0000001a;height:90px;position:fixed;bottom:0;left:0;right:0}[data-color-theme=dark] .fixed-buttons-backdrop,[data-color-theme=auto] .fixed-buttons-backdrop{background:#1e1e1ecc;border-top:.5px solid #ffffff1a}}}@media (width<=915px) and (orientation:landscape){@supports (backdrop-filter:blur(20px)){.fixed-buttons-backdrop{-webkit-backdrop-filter:blur(20px)saturate(180%);z-index:98;pointer-events:none;background:#fffc;border-top:.5px solid #0000001a;height:70px;position:fixed;bottom:0;left:0;right:0;display:block!important}[data-color-theme=dark] .fixed-buttons-backdrop,[data-color-theme=auto] .fixed-buttons-backdrop{background:#1e1e1ecc;border-top:.5px solid #ffffff1a}}*{max-width:100vw!important}html,body{width:100vw!important;max-width:100vw!important;overflow-x:hidden!important}.cv-container{width:100%!important;max-width:100%!important;margin:0!important;padding:0!important;overflow-x:hidden!important}.cv-page{width:100%!important;max-width:100%!important;box-shadow:none!important;margin:0!important;transform:scale(1)!important}.page-content{width:100%!important;max-width:100%!important;overflow-x:hidden!important}.action-bar,.cv-header,.cv-sidebar,.cv-main{max-width:100%!important;overflow-x:hidden!important}.cv-page .page-1 .page-content,.cv-page .page-2 .page-content,.page-1 .page-content,.page-2 .page-content{grid-template-rows:auto auto!important;grid-template-columns:1fr!important;max-width:100%!important}.page-1 .cv-sidebar-left{order:1;grid-area:1/1}.page-1 .cv-main{order:2;grid-area:2/1}.page-2 .cv-main{order:1;grid-area:1/1}.page-2 .cv-sidebar-right{order:2;grid-area:2/1}.cv-header{margin-bottom:1rem!important}.cv-name{text-align:left!important;font-size:1.4rem!important}.years-experience{text-align:left!important;font-size:1em!important}.cv-header-left{grid-template-rows:auto auto auto!important;grid-template-columns:1fr auto!important;align-items:start!important;gap:.5rem 1rem!important;display:grid!important}.cv-name{grid-area:1/1}.years-experience{grid-area:2/1;margin:0!important}.intro-text{grid-area:3/1;margin:0!important}.cv-photo{grid-area:1/2/4;align-self:start;width:auto!important;max-width:180px!important;height:auto!important;margin:0!important;position:static!important}.cv-photo img{border-radius:8px;width:100%!important;height:auto!important}.action-bar{padding:.5rem .75rem!important}.action-bar-content{gap:0;padding:0;grid-template-columns:1fr!important}.view-controls-center,.action-buttons-right{display:none!important}.site-title{justify-content:space-between;align-items:center;gap:.5rem;width:100%;padding:0 .5rem;display:flex}.site-title-left{flex:auto;align-items:center;gap:.5rem;min-width:0;display:flex}.site-title-text{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;font-size:.95rem!important}.language-selector{flex:none;gap:.25rem;margin-left:auto;display:flex}.language-selector .selector-btn{min-width:35px!important;padding:.3rem .6rem!important;font-size:.85rem!important}.site-title-year,.site-logo-link{display:none!important}.site-icon-mobile{display:inline-flex!important}.cv-sidebar,.cv-sidebar-left,.cv-sidebar-right{height:auto!important;max-height:none!important;padding:.75rem!important;overflow:visible!important}.cv-sidebar .actual-content,.cv-sidebar-left .actual-content,.cv-sidebar-right .actual-content{height:auto!important;max-height:none!important;overflow:visible!important}.sidebar-accordion,.sidebar-accordion[open],.sidebar-accordion:not([open]){height:auto!important;min-height:0!important;max-height:none!important;display:block!important;overflow:visible!important}.sidebar-accordion>*{display:block!important}.sidebar-accordion summary{pointer-events:none!important;list-style:none!important}.sidebar-accordion summary::-webkit-details-marker{display:none!important}.sidebar-accordion summary.sidebar-accordion-header .chevron{transform:rotate(0)!important}.sidebar-accordion .sidebar-accordion-content,.sidebar-accordion:not([open]) .sidebar-accordion-content{opacity:1!important;visibility:visible!important;max-height:none!important;display:block!important;overflow:visible!important}.sidebar-accordion details>summary:after{transform:rotate(0)!important}.sidebar-accordion details .sidebar-content{opacity:1!important;max-height:none!important;display:block!important}.sidebar-accordion summary.sidebar-accordion-header{padding:6px 12px!important;font-size:.8em!important}.section-title{margin-bottom:.5rem!important;font-size:1rem!important}.experience-item,.project-item,.course-item{margin-bottom:1rem!important;padding-bottom:1rem!important}.experience-period,.experience-location,.position{font-size:.85rem!important}.short-desc,.responsibilities li{font-size:.8rem!important}.hamburger-btn{z-index:1001!important;opacity:1!important;visibility:visible!important;display:flex!important;position:relative!important}.download-btn,.print-friendly-btn,.fixed-btn.contact-btn,.shortcuts-btn,.info-button,.back-to-top,.color-theme-switcher{width:clamp(32px,2.2vw + 19.6px,40px)!important;height:clamp(32px,2.2vw + 19.6px,40px)!important;bottom:1rem!important}.download-btn iconify-icon,.print-friendly-btn iconify-icon,.fixed-btn.contact-btn iconify-icon,.shortcuts-btn iconify-icon,.info-button iconify-icon,.back-to-top iconify-icon,.color-theme-switcher iconify-icon{width:clamp(16px,1.1vw + 9.8px,20px)!important;height:clamp(16px,1.1vw + 9.8px,20px)!important;font-size:clamp(16px,1.1vw + 9.8px,20px)!important}.download-btn{left:calc(50% - 170px)!important}.print-friendly-btn{left:calc(50% - 120px)!important}.fixed-btn.contact-btn{left:calc(50% - 70px)!important}.shortcuts-btn{left:calc(50% - 20px)!important}.color-theme-switcher{left:calc(50% + 30px)!important}.info-button{left:calc(50% + 80px)!important}.back-to-top{left:calc(50% + 130px)!important}.is-mobile-device .download-btn{left:calc(50% - 145px)!important}.is-mobile-device .print-friendly-btn{left:calc(50% - 95px)!important}.is-mobile-device .fixed-btn.contact-btn{left:calc(50% - 45px)!important}.is-mobile-device .color-theme-switcher{left:calc(50% + 5px)!important}.is-mobile-device .info-button{left:calc(50% + 55px)!important}.is-mobile-device .back-to-top{left:calc(50% + 105px)!important}.fixed-buttons-backdrop{height:70px!important}footer.no-print{padding-bottom:90px!important}}.skeleton{will-change:background-position;background:linear-gradient(90deg,#f0f0f0 0%,#e8e8e8 20%,#f0f0f0 40% 100%) 0 0/200% 100%;border-radius:4px;animation:1.8s ease-in-out infinite skeleton-shimmer}@keyframes skeleton-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.component-wrapper{position:relative}.component-wrapper .actual-content{opacity:1;transition:opacity .25s ease-out}.component-wrapper .skeleton-content{opacity:0;pointer-events:none;transition:opacity .25s ease-out;position:absolute;top:0;left:0;right:0}.component-wrapper.loading .actual-content,.loading .component-wrapper .actual-content{opacity:0;pointer-events:none}.component-wrapper.loading .skeleton-content,.loading .component-wrapper .skeleton-content{opacity:1;pointer-events:all}.skeleton-header{min-height:200px;padding-right:185px;position:relative}.skeleton-header-text{z-index:1;position:relative}.skeleton-name{width:75%;height:40px;margin-bottom:12px}.skeleton-experience-years{width:55%;height:24px;margin-bottom:24px}.skeleton-photo{border:3px solid #e8e8e8;border-radius:0;flex-shrink:0;width:150px;height:200px;position:absolute;top:15px;right:15px}.skeleton-intro{width:100%;height:90px;margin-top:12px}.skeleton-section-title{align-items:center;gap:8px;margin-bottom:16px;display:flex}.skeleton-icon{border-radius:4px;flex-shrink:0;width:24px;height:24px}.skeleton-title-text{width:40%;height:24px}.skeleton-skill-category{margin-bottom:20px}.skeleton-skill-title{width:60%;height:20px;margin-bottom:12px}.skeleton-skill-items{flex-direction:column;gap:8px;display:flex}.skeleton-skill-item{width:100%;height:32px}.skeleton-skill-item:nth-child(2){width:85%}.skeleton-skill-item:nth-child(3){width:90%}.skeleton-skill-item:nth-child(4){width:75%}.skeleton-experience-item{gap:16px;margin-bottom:24px;display:flex}.skeleton-company-logo{border-radius:8px;flex-shrink:0;width:60px;height:60px}.skeleton-experience-content{flex-direction:column;flex:1;gap:8px;display:flex}.skeleton-position-line{width:80%;height:20px}.skeleton-date-line{width:50%;height:14px}.skeleton-description-line{width:100%;height:16px;margin-top:4px}.skeleton-responsibility-line{width:100%;height:14px;margin-left:16px}.skeleton-position{width:80%;height:20px}.skeleton-company-info{width:60%;height:16px}.skeleton-description{width:100%;height:40px;margin-top:4px}.skeleton-description.short{width:85%}.skeleton-section{padding:16px 0}.skeleton-section-title{width:35%;height:28px;margin-bottom:20px}.skeleton-education-item{width:100%;height:48px;margin-bottom:12px}.skeleton-education-item:last-child{margin-bottom:0}.skeleton-summary-paragraph{width:100%;height:18px;margin-bottom:10px}.skeleton-summary-paragraph:last-child{margin-bottom:0}.skeleton-award-item{gap:16px;margin-bottom:24px;display:flex}.skeleton-award-logo{border-radius:8px;flex-shrink:0;width:60px;height:60px}.skeleton-award-content{flex-direction:column;flex:1;gap:8px;display:flex}.skeleton-award-title-line{width:70%;height:20px}.skeleton-award-info-line{width:50%;height:14px}.skeleton-award-title{width:70%;height:20px}.skeleton-award-info{width:50%;height:16px}.skeleton-award-description{width:100%;height:40px;margin-top:4px}.skeleton-project-item{gap:16px;margin-bottom:24px;display:flex}.skeleton-project-icon{border-radius:8px;flex-shrink:0;width:80px;height:80px}.skeleton-project-content{flex-direction:column;flex:1;gap:8px;display:flex}.skeleton-project-title-line{width:75%;height:20px}.skeleton-tech-line{width:85%;height:14px;margin-top:4px}.skeleton-footer-line{width:70%;height:16px;margin-top:16px}.skeleton-project-title{width:75%;height:20px}.skeleton-project-info{width:55%;height:16px}.skeleton-project-description{width:100%;height:40px;margin-top:4px}.skeleton-project-description.short{width:80%}.skeleton-course-item{gap:16px;margin-bottom:20px;display:flex}.skeleton-course-icon{border-radius:8px;flex-shrink:0;width:80px;height:80px}.skeleton-course-content{flex-direction:column;flex:1;gap:8px;display:flex}.skeleton-course-title-line{width:70%;height:18px}.skeleton-course-info-line{width:60%;height:14px}.skeleton-course-title{width:70%;height:18px}.skeleton-course-info{width:60%;height:16px}.skeleton-language-item{width:100%;height:20px;margin-bottom:12px}.skeleton-language-item:last-child{margin-bottom:0}.skeleton-reference-item{width:100%;height:22px;margin-bottom:10px}.skeleton-reference-item:last-child{margin-bottom:0}.skeleton-other-item{width:60%;height:20px}.skeleton-sidebar{padding:16px 0}.skeleton-sidebar-header{width:80%;height:28px;margin-bottom:20px}.skeleton-footer{flex-direction:column;gap:12px;padding:16px 0;display:flex}.skeleton-footer-item{width:100%;height:20px}.skeleton-footer-item:nth-child(2){width:90%}.skeleton-footer-item:nth-child(3){width:85%}.skeleton-footer-item:nth-child(4){width:80%}.skeleton-footer-item:nth-child(5){width:75%}.skeleton-text{height:16px;margin-bottom:8px}.skeleton-text.short{width:60%}.skeleton-text.medium{width:80%}.skeleton-text.long{width:95%}@media (width<=768px){.skeleton-header{flex-direction:column;align-items:center}.skeleton-header-text{text-align:center;width:100%}.skeleton-name,.skeleton-experience-years{width:80%;margin-left:auto;margin-right:auto}.skeleton-photo{border-radius:8px;width:100px;height:100px}.skeleton-experience-item{flex-direction:column;gap:12px}.skeleton-company-logo{width:50px;height:50px}}@media (prefers-reduced-motion:reduce){.skeleton{background:#e8e8e8;animation:none}.component-wrapper .actual-content,.component-wrapper .skeleton-content{transition:none}}@media print{.skeleton-content{display:none!important}.component-wrapper .actual-content{opacity:1!important}}.skeleton{backface-visibility:hidden;transform:translateZ(0)}.component-wrapper{contain:layout style}.skeleton-content{contain:layout paint} \ No newline at end of file diff --git a/static/images/sprites/sprite-companies.png b/static/images/sprites/sprite-companies.png new file mode 100644 index 0000000..01e9039 Binary files /dev/null and b/static/images/sprites/sprite-companies.png differ diff --git a/static/images/sprites/sprite-companies@2x.png b/static/images/sprites/sprite-companies@2x.png new file mode 100644 index 0000000..8fb7e86 Binary files /dev/null and b/static/images/sprites/sprite-companies@2x.png differ diff --git a/static/images/sprites/sprite-courses.png b/static/images/sprites/sprite-courses.png new file mode 100644 index 0000000..0208530 Binary files /dev/null and b/static/images/sprites/sprite-courses.png differ diff --git a/static/images/sprites/sprite-courses@2x.png b/static/images/sprites/sprite-courses@2x.png new file mode 100644 index 0000000..108fde6 Binary files /dev/null and b/static/images/sprites/sprite-courses@2x.png differ diff --git a/static/images/sprites/sprite-map.json b/static/images/sprites/sprite-map.json new file mode 100644 index 0000000..cdf339d --- /dev/null +++ b/static/images/sprites/sprite-map.json @@ -0,0 +1,184 @@ +{ + "companies": [ + { + "index": 0, + "name": "accenture.png" + }, + { + "index": 1, + "name": "aena-long.png" + }, + { + "index": 2, + "name": "aena.png" + }, + { + "index": 3, + "name": "clicplan-short.png" + }, + { + "index": 4, + "name": "clicplan.png" + }, + { + "index": 5, + "name": "drolosoft.png" + }, + { + "index": 6, + "name": "drosoloft-plain.png" + }, + { + "index": 7, + "name": "ebantic.png" + }, + { + "index": 8, + "name": "emailing-network.png" + }, + { + "index": 9, + "name": "everis.png" + }, + { + "index": 10, + "name": "gigya.png" + }, + { + "index": 11, + "name": "indra.png" + }, + { + "index": 12, + "name": "insa.png" + }, + { + "index": 13, + "name": "livgolf.png" + }, + { + "index": 14, + "name": "megabanner.png" + }, + { + "index": 15, + "name": "olympic-broadcasting.png" + }, + { + "index": 16, + "name": "pentamsi-long.png" + }, + { + "index": 17, + "name": "pentamsi.png" + }, + { + "index": 18, + "name": "sap.png" + }, + { + "index": 19, + "name": "twentic.png" + }, + { + "index": 20, + "name": "uex.png" + }, + { + "index": 21, + "name": "webratio.png" + }, + { + "index": 22, + "name": "webratioa.png" + } + ], + "projects": [ + { + "index": 0, + "name": "HERRUMBRE_NEGATIVO VER 2@4x.png" + }, + { + "index": 1, + "name": "deliverybikes.png" + }, + { + "index": 2, + "name": "herrumbre-vivo.png" + }, + { + "index": 3, + "name": "jorpack.png" + }, + { + "index": 4, + "name": "laporra-doc.png" + }, + { + "index": 5, + "name": "laporra.png" + }, + { + "index": 6, + "name": "lidering.png" + }, + { + "index": 7, + "name": "ola-logo-squared-blue.png" + }, + { + "index": 8, + "name": "sap.png" + }, + { + "index": 9, + "name": "somosunaola#.png" + }, + { + "index": 10, + "name": "somosunaola.png" + }, + { + "index": 11, + "name": "twentic.png" + } + ], + "courses": [ + { + "index": 0, + "name": "camaracomercio.png" + }, + { + "index": 1, + "name": "codecademy.png" + }, + { + "index": 2, + "name": "forem.png" + }, + { + "index": 3, + "name": "linkedin-blue.png" + }, + { + "index": 4, + "name": "linkedin.png" + }, + { + "index": 5, + "name": "servoy-logo.png" + }, + { + "index": 6, + "name": "servoy.png" + }, + { + "index": 7, + "name": "udemy.png" + }, + { + "index": 8, + "name": "uex.png" + } + ] +} \ No newline at end of file diff --git a/static/images/sprites/sprite-projects.png b/static/images/sprites/sprite-projects.png new file mode 100644 index 0000000..58b3efe Binary files /dev/null and b/static/images/sprites/sprite-projects.png differ diff --git a/static/images/sprites/sprite-projects@2x.png b/static/images/sprites/sprite-projects@2x.png new file mode 100644 index 0000000..43aeb8d Binary files /dev/null and b/static/images/sprites/sprite-projects@2x.png differ diff --git a/static/sprite-showcase.html b/static/sprite-showcase.html new file mode 100644 index 0000000..e43646f --- /dev/null +++ b/static/sprite-showcase.html @@ -0,0 +1,371 @@ + + + + + + CSS Sprite Showcase + + + + +

CSS Sprite Showcase

+ +
+ Summary: + +
+ +
+

Companies (Full Sprite)

+
+ companies sprite +
+ +

Individual Icons

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+

Projects (Full Sprite)

+
+ projects sprite +
+ +

Individual Icons

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+

Courses (Full Sprite)

+
+ courses sprite +
+ +

Individual Icons

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+

Zoom Test

+
+
100%:
+
200%:
+
300%:
+
+
+ +
+

Retina Test

+

On retina displays, the @2x sprite should load automatically for crisp rendering.

+
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+

Network Verification

+

Open DevTools (Network tab, filter by Images) to verify:

+ +
+ + diff --git a/templates/partials/sections/courses.html b/templates/partials/sections/courses.html index 5a4d64a..afc2b56 100644 --- a/templates/partials/sections/courses.html +++ b/templates/partials/sections/courses.html @@ -13,15 +13,15 @@ {{range .CV.Courses}}
- {{if .CourseLogo}}
+ {{if .LogoIndex}} + + {{else if .CourseLogo}} {{.Title}} logo -
- {{else}} -
+ {{else}} + {{end}}
- {{end}}
{{.Title}}
{{.Institution}} - {{.Date}} - ({{.Location}}) diff --git a/templates/partials/sections/experience.html b/templates/partials/sections/experience.html index c6ffae6..814378e 100644 --- a/templates/partials/sections/experience.html +++ b/templates/partials/sections/experience.html @@ -14,7 +14,9 @@ {{range .CV.Experience}}