Updates skeleton header to exactly match the actual CV header layout with
photo absolutely positioned on the right side.
**Layout Changes:**
- Photo: 150x200px positioned absolutely (top: 15px, right: 15px)
- Name: 40px height (h1 size)
- Subtitle: 24px height (larger)
- Intro text: 90px height (3-4 lines)
- Header has padding-right: 185px to accommodate photo
- Removed flex layout, using absolute positioning like actual
**Visual Improvements:**
- Photo border: 3px solid #e8e8e8 (matches actual white border)
- No border-radius on photo (matches actual)
- Larger text blocks for better visual match
- Proper spacing between elements
**Tests:**
- test-skeleton-verify.mjs: ✅ All 4 language switches pass
- Skeleton displays correctly on every transition
Implements component-level skeleton loaders that display during language
transitions, providing visual feedback while content swaps.
**Implementation:**
- Dual-state structure: each component has actual-content + skeleton-content
- CSS toggles visibility via opacity transitions (250ms)
- Global hyperscript listener on <body> for language button clicks
- Adds .loading class to parent containers that persist across OOB swaps
- Skeleton shapes mimic actual components (header, name, photo, intro)
**Key Technical Solutions:**
- Event delegation using event.target.matches('.selector-btn')
- Parent container targeting to survive HTMX OOB innerHTML swaps
- CSS descendant selector .loading .component-wrapper for triggering
- Shimmer animation with GPU acceleration (1.8s infinite)
**Files Modified:**
- static/css/skeleton.css: Complete skeleton system with shimmer animation
- templates/index.html: Global hyperscript for .loading class management
- templates/partials/sections/header.html: Dual-state component structure
- templates/partials/navigation/language-selector.html: Removed local hyperscript
**Tests:**
- test-skeleton-verify.mjs: Validates skeleton across 4 language switches
- All tests passing: ✅ Consistent activation on every language change
Implemented dual-state skeleton system as specified in prompt:
- Each component has actual-content + skeleton-content structure
- CSS toggles visibility via .loading class on component-wrapper
- Individual skeleton boxes (name, photo, intro, etc.) with shimmer animation
- Hyperscript triggers loading state during language switch
Changes:
- skeleton.css: Complete component-level skeleton CSS system with shimmer
- language-selector.html: Hyperscript to add/remove .loading class
- header.html: Dual-state structure with skeleton placeholders
Behavior:
- Click language button → .loading class added to components
- Actual content fades out (opacity → 0) in 250ms
- Skeleton boxes appear and shimmer
- After HTMX swap + 100ms delay → .loading class removed
- New content fades in (opacity → 1) in 250ms
Test Results:
✅ Component wrapper structure verified
✅ Dual-state toggle working correctly
✅ Skeleton elements present and animated
✅ Shimmer animation active (1.8s infinite loop)
✅ Accessibility: respects prefers-reduced-motion
✅ Print: skeletons hidden, content always visible
Next: Add skeleton structure to remaining components (experience, education, skills, projects)