overflow: hidden on .chat-row made min-height resolve to 0 (CSS Flexbox §4.5),
so the flex column container crushed rows instead of scrolling. Also fix all
test selectors (.chat-agent→.chat-row-bot .chat-msg) and add bubble dimension
assertions.
- Replace 100vw with 100% on mobile chat panel (avoids scrollbar width)
- Add overflow-x: hidden on html/body for mobile
- Add box-sizing: border-box on mobile panel
- Add overflow: hidden and max-width: 100% on chat rows
- Add overflow-x: hidden on messages container
Header UX:
- Replace 5 icon buttons with single cog (gear) dropdown menu
- Cog menu shows all layout options with icons + labels
- Help and Close buttons stay visible outside the cog
- Active mode highlighted in green
Mobile fixes:
- Fix viewport overflow (100vw + max-width: 100vw)
- Stronger shadow (0 -4px 20px) for clear CV/chat separation
- Rounded corners (12px) on top for recognizable chat window
- Hide desktop-only modes (side panel, floating, full) from cog on mobile
- max-height: 50vh ensures CV always visible above
Dark mode:
- Cog menu styled for dark backgrounds
- doc/30-CHAT-MODULE-PORTABILITY.md: step-by-step guide to port the
chat agent to other Go apps (files, dependencies, customization)
- Fix wave emoji position on mobile (follows button to bottom:5rem right:1rem)
Mobile (≤480px):
- Hide Side Panel, Floating, Full Screen buttons
- Show Split button (50vh vertical split, CV visible above)
- Compact mode: 55vh max bottom sheet
- Force desktop modes to compact if somehow activated
- Disable tooltips on mobile (overflow prevention)
- Tighter header padding
Desktop (>480px):
- Split button hidden, all modes available as before
Tests: 85-chat-mobile.test.mjs — 79 assertions across
iPhone SE (320x568), iPhone 14 (393x852), iPhone 14 Pro Max (430x932),
plus desktop sanity check
- Support image file fallback when no sprite index exists
(Immich Photo Manager, Cmux Resurrect now show their logos)
- Render external links [text](https://...) as clickable links
(fixes Third Party Contributions raw markdown)
- Smaller inline icons (20px) to fit chat bubble aesthetic
- Separate icon-chat CSS class for chat-specific sizing
- Fix side panel and full screen not covering full viewport
- Fix floating mode initial position (near chat button, not top-right)
- Reset width/height inline styles when switching modes
- Add 84-chat-layout-modes.test.mjs: 38 assertions covering
compact, side panel, full screen, floating, drag, rapid switching,
and user avatar rendering
- Add GLM-4.7-Flash as default Ollama model (replaces Mistral)
- Fix WRITE_TIMEOUT (15s→120s) and HTMX timeout (5s→120s) for local LLM
- Auto-warmup model on startup in development mode
- Add /api/chat/status endpoint for model readiness polling
- Show "Initializing AI model..." indicator while model loads
- Add user avatar (mdi:account) on chat messages
- Inject company/project/course sprite icons inline in chat responses
- Replace cramped header icons with 4 icon buttons + tooltips
(Compact, Side panel, Floating, Full screen)
- Add floating/draggable chat mode with smooth drag support
- Chip questions show user bubble instantly and clear input
- Help modal prefills input instead of auto-sending
- User bubble rendered client-side for immediate feedback
Bubbles:
- Teams-style layout: bot avatar (green circle) on left, message beside it
- User messages right-aligned, no avatar (clean, like Teams)
- Rounded bubbles (border-radius: 16px) instead of square
- Distinct corner radii for conversation flow
Navigation:
- Links no longer close the chat — panel stays open for continued navigation
- Added #certifications anchor (alias to courses section)
- Fixed agent instruction to use #courses for certifications references
Theme:
- All colors use CSS variables from _themes.css
- Automatically adapts to light/dark without explicit .theme-clean overrides
- Panel uses --paper-bg (white in light, dark in dark theme)
Size modes:
- 3 discrete toggle buttons: compact, half-screen, fullscreen
- Active state highlighted, direct selection (no confusing cycling)
- Removed chat-half-left (simplified to compact/half/full)
Intelligence:
- React query now returns results (verified: 4 companies + 2 projects + skills)
Avatars:
- Robot icon (green circle) before each agent message
- Person icon (dark circle) before each user message
- New .chat-bubble wrapper with flex layout for avatar + message
Dark theme fixes:
- Panel background: #1e1e1e (not pure black)
- Agent bubbles: #2d2d2d with light text (not dark/invisible)
- Input area: #2d2d2d (not black)
- Header stays green (--accent-green) in both themes
- Chips, suggestions consistent with panel background
Text overflow:
- overflow-wrap + word-break on messages
- min-width: 0 prevents flex overflow
- User bubble properly right-aligned with avatar
4 size modes cycled via expand button in header:
- Compact: 360px (default, bottom-right corner)
- Half-right: 50vw docked to right edge, full height
- Half-left: 50vw docked to left edge, full height
- Full: 100% viewport overlay
Also fixes text overflow in chat messages (overflow-wrap, word-break).
Messages area expands to fill available height in larger modes.
Size button uses mdi icons: arrow-expand, dock-right, dock-left, arrow-collapse.
CSS transitions for smooth size changes.
Agent instruction now requires markdown links to CV anchors:
- Companies: [Olympic Broadcasting](#exp-olympic-broadcasting)
- Projects: [Immich Photo Manager](#proj-immich-photo-manager)
- Sections: [Skills](#skills), [Experience](#experience)
formatResponse converts [text](#anchor) → clickable green links
that close the chat panel, smooth-scroll to the target, and
pulse a green highlight for 2 seconds.
All existing CV anchor IDs used: exp-{companyID}, proj-{projectID},
course-{courseID}, plus section IDs (experience, projects, skills, etc.)
- Replace flat list with <details>/<summary> accordion (5 categories)
- Questions are clickable: close modal → open chat → send question
- closeChatHelpAndAsk() helper bridges modal and chat panel
- Green accent on category icons and question hover
- Chevron arrows for expand/collapse state
- Dark theme support for all accordion elements
- Compact layout with no wasted space
Position fix:
- Remove _chat.css @import from main.css (was overriding with old
left:2rem cached version). Chat CSS now loaded only via head-styles.
- Button confirmed at right:2rem, bottom:6rem (above back-to-top)
Help modal:
- New chat-help-modal.html using same <dialog> pattern as shortcuts
- 6 organized categories: Experience, Technologies, Projects,
Education, Skills, How it works
- Bilingual EN/ES with example questions per category
- ? button in header opens modal via commandfor/show-modal
- Removed inline help card (modal replaces it)
Intelligence:
- Comprehensive query strategy for 8 question types
- Technology queries always use cross-section search
- Company queries use experience without filter for full listing
- Agent knows CV site is built with Go/HTMX (bonus context)
- Skills report proficiency levels when technology found
CSS:
- Button moved to right: 2rem, above back-to-top (bottom: 6rem)
- Uses CV design tokens: --black-bar, --accent-blue, --paper-bg
- Fonts: Quicksand (header), Source Sans Pro (body)
- Tooltip on the left side (tooltip-left class)
- Dark theme uses CV-consistent grays
Intelligence:
- Agent instruction emphasizes exhaustive reporting of ALL matches
- Cross-section search results must not be truncated
- Mentions CV site itself is built with Go when relevant
Tests:
- Updated positioning assertions (right side, x > viewport/2)
- Added 5 intelligence tests: Go cross-section, company count,
years of experience, React cross-section, Spanish response
- Resilient to API errors (waits for any message, not just user)
- 42 total test assertions
- Mascot identity: robot-happy-outline icon, "CV Assistant" branding
- Help popup: onboarding card explaining what the mascot can do (EN/ES)
- Suggested questions: 5 clickable chips that auto-submit (bilingual)
- Typing indicator: three bouncing dots during agent response
- Icon swap: mascot icon ↔ close icon via Hyperscript class toggle
- Dark theme support for all new elements
- Modular CSS loading in development, chat CSS always loaded separately
Chat button and panel now anchor from left: 2rem to match the
zoom, shortcuts, and other fixed buttons. Panel opens rightward
so content is always visible.
Visitors can ask questions about the CV via a floating chat panel.
The agent uses Gemini to answer questions about experience, projects,
skills, and education by querying the cached CV JSON data.
- internal/chat/agent.go: LLM agent with query_cv tool that searches
CV data by section (experience, projects, skills, etc.) with keyword filtering
- internal/chat/handler.go: POST /api/chat endpoint with session management,
graceful degradation when GOOGLE_API_KEY is not set
- chat-widget.html: HTMX-powered floating chat panel with Hyperscript toggle
- _chat.css: Responsive chat UI with dark theme support
- Wired into existing architecture via dependency injection (CVHandler,
routes, main.go) — zero breaking changes, all existing tests pass
- Add media query for screens ≤400px (iPhone 13 mini = 375px)
- Reduce button size to 34px and icon size to 16px
- Recalculate 6-button positions with 6px gaps (234px total width)
- Ensures buttons stay centered within narrow viewports
Replace simple search button with search bar design in action bar:
- Semi-transparent styling integrated with dark action bar
- Keyboard shortcut indicators (⌘ K) shown as individual kbd elements
- Search icon and "Search" text for better discoverability
- Responsive: kbd keys hidden on mobile (<900px)
- Remove unused cmd-k-button.html partial template
Update test to verify new search bar structure (styling, kbd elements, icon).
Reduces HTTP requests from 44+ individual images to 3 sprite sheets
(~93% reduction). Includes Go sprite generator tool, CSS classes,
template integration, and E2E tests.
- Add cmd/sprites/main.go for sprite generation (60x60px + 120x120px @2x)
- Add _sprites.css with responsive sizing and retina support
- Update templates to use sprites with logoIndex fallback
- Add Makefile targets: sprites, sprites-clean
- Add 9-test E2E suite for sprite functionality
- Add doc/22-SPRITES.md with usage documentation
Remove cmd-k-btn (search) from floating action buttons - search now only
lives in the action bar. Recalculated positions for 7-button (desktop)
and 6-button (mobile without shortcuts) layouts using fluid clamp() sizing.
- Add aria-labels to menu action buttons (PDF, Print, Contact)
- Add aria-labelledby to toggle checkboxes (desktop + mobile)
- Add -webkit-user-select prefix for Safari compatibility
- Add DynamicCacheControl middleware for HTML pages
- Add accessibility test suite (60-accessibility.test.mjs)
- Add comprehensive accessibility documentation (21-ACCESSIBILITY.md)
- Update Modern Web Techniques doc to mark audit complete
Implement a command palette accessible via CMD+K/Ctrl+K using the ninja-keys
web component. Features include:
- New /api/cmd-k endpoint serving dynamic CV entries (experiences, projects, courses)
- Language-aware responses with 1-hour cache headers
- Scroll-to-section functionality for quick navigation
- Enhanced keyboard shortcuts modal with CMD+K documentation
- Comprehensive test coverage for API and UI interactions
Also includes cleanup of deprecated debug test files and various UI polish
improvements to contact form, themes, and action bar components.
Remove hardcoded width/height HTML attributes from iconify-icon elements
that were overriding CSS sizing. The iconify-icon component uses HTML
attributes for SVG rendering, ignoring CSS width/height.
- Remove width="28" height="28" from 8 button templates
- Remove conflicting 768px media query from _buttons.css
- Add default desktop icon sizes (24px) in _scroll-behavior.css
- Icons now scale via clamp() from 18px (380px) to 24px (900px)
Plain text endpoint:
- Add /text route for plain text CV (for curl/AI crawlers)
- Use k3a/html2text library for HTML-to-text conversion
- Add Plain Text button to hamburger menu with UI translations
Contact form feature:
- Add ContactHandler with proper email service integration
- Add CSRF protection middleware
- Add rate limiting (5 submissions/hour per IP)
- Add honeypot and timing-based bot protection
- Add input validation with detailed error messages
- Add security logging middleware
- Add browser-only middleware for API protection
Code quality:
- Fix all golangci-lint errcheck warnings for w.Write calls
- Remove duplicate getClientIP functions
- Wire up ContactHandler in routes.Setup
- Delete orphaned CSS files (color-theme.css, logo-toggle.css,
skeleton.css, main.new.css) - replaced by modular equivalents
- Delete 08-contexts/_print.css - wrongly created during modularization
- Remove 08-contexts folder (now empty)
- Add print.css as standalone with media="print" in HTML
- Update stale comments referencing old file names
- Remove _print.css import from main.css
print.css remains standalone and will NOT be bundled, as it's a
special case loaded only when printing (media="print").
On mobile, the info modal fonts were too large and causing content overflow.
All text elements have been proportionally reduced for better mobile UX.
Changes for mobile (≤768px):
- Modal padding: 2rem → 1.5rem (vertical), 1.5rem → 1rem (horizontal)
- Close button: 40px → 32px, icon 24px → 20px
- Title: 1.5rem → 1.05rem (30% reduction)
- CV title: 0.95rem
- Photo: 50px × 67px → 30px × 40px
- Description: 0.95rem → 0.85rem
- Tech items: 0.9rem → 0.8rem, icons 32px → 24px
- GitHub button: 0.875rem, icon 24px → 20px
- Tech grid: Single column layout
- Reduced spacing throughout
Result:
- All content fits comfortably within mobile viewport
- No text overflow or coverage issues
- Improved readability and visual hierarchy
- Better use of limited mobile screen space
Tests created but have Playwright API compatibility issues (non-blocking)
The menu was showing light gray text in dark theme, creating poor contrast
against the white menu background. Menu text must always be dark since
the menu background is always white regardless of theme.
Changes:
- Added dark theme overrides for .navigation-menu and .submenu-content
- Force --text-dark to #1a1a1a (dark text) in dark theme for menu
- Force --text-gray to #333333 (dark gray) in dark theme for menu icons
- Applied same fix for auto theme when system preference is dark
Result:
- Menu text: Dark/black (rgb(26, 26, 26)) in all themes
- Menu icons: Dark gray (rgb(51, 51, 51)) in all themes
- Menu background: White (rgb(255, 255, 255)) in all themes
- Proper contrast and readability restored
Test created: 68-menu-colors-dark-theme-test.mjs
Screenshots: menu-light-theme.png, menu-dark-theme.png
Changed zoom button color from #9b59b6 to #5c59b6 for better visual appeal.
The new shade is more blue-tinted, creating a vibrant indigo/periwinkle appearance
that remains distinct from the info button blue.
Changes:
- Updated zoom-toggle-btn background: rgba(92, 89, 182, 0.7)
- Updated hover state: #5c59b6
- Updated at-bottom state: #5c59b6
- Updated test output to reflect correct hex code
Test verified: All buttons visible and colors distinct at all viewports.
Issues fixed:
1. Zoom button now uses purple color (rgba(155, 89, 182, 0.7)) instead of blue
2. Info button keeps blue color (rgba(52, 152, 219, 0.7))
3. Both buttons now show distinct colors in default state, not just on hover
4. Device detection now considers viewport width, not just user agent
5. Buttons no longer hide in responsive mode at desktop viewport sizes
Changes:
- Updated zoom-toggle-btn to use purple background color
- Updated info-button to use blue background color (explicit, not var)
- Modified device-detection.js to check viewport width (≤900px) in addition to UA
- Added resize listener to update device class dynamically
- Created test (67-button-colors-and-visibility-test.mjs) to verify fixes
Testing:
- Desktop (1278px): All buttons visible with distinct colors
- Mobile (375px): Zoom/shortcuts hidden, core buttons visible
- Device detection now viewport-aware (prevents hiding at desktop sizes)
Fixes info modal positioning to be perfectly centered on mobile devices
in all orientations.
ISSUE:
- Info modal was not centered on mobile viewports
- User reported "pop-up of information in mobile it is not centered"
- Modal positioning relied on inset:0 + margin:auto which doesn't
work consistently on mobile devices
FIX:
- Added explicit mobile centering using transform translate(-50%, -50%)
- Position: top: 50%, left: 50% with transform centering
- Applied to all modals: info, keyboard shortcuts, and PDF download
- Added mobile-specific fade-in animation preserving centering
- Constrained modal to viewport with calc(100vw - 2rem) width/height
Files modified:
- static/css/04-interactive/_modals.css - Mobile centering for all modals
- tests/mjs/58-modal-centering-test.mjs - Validation test
Test results:
✅ Portrait (375×667): Perfect center - 0px offset
✅ Landscape (667×375): Perfect center - 0px offset
✅ Modal center matches viewport center exactly
Fixes three critical mobile UI issues:
1. Theme Button Transparency (FIXED)
- Changed theme button from 50% to full opacity on mobile
- Updated _themes.css with rgba(x, y, z, 1) for all theme modes
- Added opacity: 1 !important to mobile media query
2. Info Button Color Differentiation (FIXED)
- Changed info button from green (#27ae60) to blue (#3498db)
- Now visually distinct from green back-to-top button
- Updated all states: default, hover, at-bottom
3. Button Layout Reorganization (FIXED)
- Added .is-mobile-device rules for 5-button layout (no shortcuts)
- Buttons centered without gap: Download, Print, Theme, Info, Back-to-top
- Total width: 290px (5 buttons + 4 gaps) vs 350px (6 buttons)
Files modified:
- static/css/01-foundation/_themes.css - Primary theme button fix
- static/css/04-interactive/_scroll-behavior.css - Info color + layout
- static/css/color-theme.css - Mobile device positioning sync
- tests/mjs/53-final-mobile-fixes-test.mjs - Comprehensive validation
Test results:
✅ Shortcuts hidden on real mobile (iPhone user agent)
✅ 5 buttons evenly spaced with no gap (60px spacing)
✅ Info button blue (52, 152, 219) vs back-to-top green (39, 174, 96)
✅ Theme button full opacity (alpha: 1, opacity: 1)
Issue 1: Blur bar compatibility (Android doesn't always show at bottom)
✅ Solution: Wrap blur bar in @supports query for backdrop-filter
- Only shows on devices that support backdrop-filter (primarily iOS)
- Android devices without support won't see the bar
- Prevents layout issues on non-iOS devices
Issue 2: Keyboard shortcuts button on real mobile (no physical keyboard)
✅ Solution: Device detection + conditional hiding
- Added device-detection.js: Detects real mobile vs desktop browser
- Checks user agent (Android, iPhone, iPad, etc.) + touch support
- Adds 'is-mobile-device' or 'is-desktop' class to <html>
- CSS hides shortcuts button only on real mobile devices
- Desktop browser in mobile view: shortcuts button still visible (for testing)
Implementation Details:
1. Device Detection (static/js/device-detection.js):
- User agent detection: /Android|iPhone|iPad|etc./
- Touch support check: ontouchstart + maxTouchPoints
- Class added to <html>: is-mobile-device or is-desktop
2. Blur Bar (@supports query):
- Detects backdrop-filter support before applying
- iOS: Shows blur bar with backdrop-filter
- Android (most): No blur bar (no backdrop-filter support)
- Prevents empty/broken bar on incompatible devices
3. CSS Hiding Rules:
- .is-mobile-device .shortcuts-btn { display: none !important; }
- Also hides zoom-toggle-btn and zoom-control on real mobile
- Desktop mobile view: shortcuts button remains visible
Files Modified:
- static/js/device-detection.js: NEW - Device detection logic
- templates/index.html: Load device-detection.js early
- static/css/05-responsive/_breakpoints.css: @supports wrapper for blur bar
- static/css/04-interactive/_scroll-behavior.css: Hide shortcuts on real mobile
- tests/mjs/52-mobile-device-detection-test.mjs: Comprehensive device detection test
Test Results:
✅ iPhone (real mobile): is-mobile-device class, shortcuts hidden
✅ Desktop browser (mobile view): is-desktop class, shortcuts visible
✅ Blur bar: Only shows on devices with backdrop-filter support
Fixed two critical mobile view issues:
1. Extended CV Sidebar Accordion:
- Updated sidebar.html to use native <details> element (was div with onclick)
- Styled accordion header to match CV title badges dark theme (#303030)
- Applied consistent styling: dark gray background, light text, uppercase, no spacing
- Result: Sidebars now collapse/expand properly with native HTML functionality
2. PDF Download Modal Centering:
- Added JavaScript-based centering for mobile viewports (≤768px)
- Uses inline styles with !important flag to override browser defaults
- Updated download button to call openPdfModal() function
- Result: Modal is perfectly centered on mobile (0px offset)
Technical notes:
- Modal centering required setProperty() with 'important' flag
- Accordion matches cv-title-badges-header style exactly
- All tests passing: accordion toggle, modal centering
Files modified:
- templates/partials/cv/sidebar.html
- static/css/05-responsive/_breakpoints.css
- static/js/main.js
- templates/partials/widgets/download-button.html
Tests added:
- tests/mjs/43-mobile-accordion-and-modal-test.mjs
- tests/mjs/46-visual-accordion-style-test.mjs