@@ -28,6 +28,62 @@ function escapeHtml(str) {
2828 return d . innerHTML ;
2929}
3030
31+ // ============================================
32+ // INFO MODAL FUNCTIONS
33+ // ============================================
34+
35+ function showInfoModal ( title , steps ) {
36+ var overlay = document . getElementById ( 'infoModalOverlay' ) ;
37+ var titleEl = document . getElementById ( 'infoModalTitle' ) ;
38+ var listEl = document . getElementById ( 'infoModalList' ) ;
39+
40+ if ( ! overlay || ! titleEl || ! listEl ) return ;
41+
42+ titleEl . textContent = title ;
43+ listEl . innerHTML = steps . map ( function ( step ) {
44+ return '<li>' + step + '</li>' ;
45+ } ) . join ( '' ) ;
46+
47+ overlay . classList . add ( 'active' ) ;
48+
49+ function closeModal ( ) {
50+ overlay . classList . remove ( 'active' ) ;
51+ closeBtn . removeEventListener ( 'click' , closeModal ) ;
52+ gotItBtn . removeEventListener ( 'click' , closeModal ) ;
53+ overlay . removeEventListener ( 'click' , overlayClick ) ;
54+ }
55+
56+ function overlayClick ( e ) {
57+ if ( e . target === overlay ) closeModal ( ) ;
58+ }
59+
60+ var closeBtn = document . getElementById ( 'infoModalClose' ) ;
61+ var gotItBtn = document . getElementById ( 'infoModalGotIt' ) ;
62+
63+ closeBtn . addEventListener ( 'click' , closeModal ) ;
64+ gotItBtn . addEventListener ( 'click' , closeModal ) ;
65+ overlay . addEventListener ( 'click' , overlayClick ) ;
66+ }
67+
68+ var currentProjectName = '' ;
69+
70+ function setupModalInfoButton ( projectName ) {
71+ currentProjectName = projectName ;
72+ var infoBtn = document . getElementById ( 'modalInfoBtn' ) ;
73+ if ( ! infoBtn ) return ;
74+
75+ // Remove old listener by cloning
76+ var newBtn = infoBtn . cloneNode ( true ) ;
77+ infoBtn . parentNode . replaceChild ( newBtn , infoBtn ) ;
78+
79+ newBtn . addEventListener ( 'click' , function ( ) {
80+ if ( typeof getProjectInstructions === 'function' ) {
81+ var info = getProjectInstructions ( currentProjectName ) ;
82+ showInfoModal ( info . title , info . steps ) ;
83+ }
84+ } ) ;
85+ }
86+
3187/* ── DOMContentLoaded ──────────────────────────────────────── */
3288document . addEventListener ( 'DOMContentLoaded' , function ( ) {
3389 var html = document . documentElement ;
@@ -797,7 +853,6 @@ document.addEventListener('DOMContentLoaded', function () {
797853 function openProjectSafe ( name , trigger ) {
798854 if ( ! modal || ! modalBody ) return ;
799855 lastFocusedElement = trigger || document . activeElement ;
800- if ( modalTitle ) modalTitle . textContent = name || 'Interactive project' ;
801856 modal . classList . add ( 'active' ) ;
802857 modal . setAttribute ( 'aria-hidden' , 'false' ) ;
803858 var scrollbarWidth = window . innerWidth - document . documentElement . clientWidth ;
@@ -812,6 +867,50 @@ document.addEventListener('DOMContentLoaded', function () {
812867 modalBody . innerHTML = '<div style="padding:1rem;color:var(--text-secondary)">Project content unavailable.</div>' ;
813868 }
814869 if ( typeof initializeProject === 'function' ) initializeProject ( name ) ;
870+ setupModalInfoButton ( name ) ;
871+
872+ // Inject info button next to the title (works for all projects)
873+ var projectContent = modalBody . querySelector ( '.project-content' ) ;
874+ if ( projectContent ) {
875+ // Try to find the title element (could be h2, or other heading)
876+ var firstHeading = projectContent . querySelector ( 'h2, h3, .resume-analyzer-copy h2, .pet-title' ) ;
877+
878+ if ( ! firstHeading ) {
879+ // If no heading found, look for any element with a title-like class
880+ firstHeading = projectContent . querySelector ( '[class*="title"], [class*="header"] h2' ) ;
881+ }
882+
883+ if ( firstHeading && ! projectContent . querySelector ( '.inline-info-btn' ) ) {
884+ // Create info button
885+ var infoBtn = document . createElement ( 'button' ) ;
886+ infoBtn . className = 'inline-info-btn' ;
887+ infoBtn . innerHTML = 'ⓘ' ;
888+ infoBtn . setAttribute ( 'aria-label' , 'How to use this project' ) ;
889+
890+ // Style the button
891+ infoBtn . style . marginLeft = '12px' ;
892+ infoBtn . style . background = 'none' ;
893+ infoBtn . style . border = 'none' ;
894+ infoBtn . style . fontSize = '1.3rem' ;
895+ infoBtn . style . cursor = 'pointer' ;
896+ infoBtn . style . color = 'var(--accent)' ;
897+ infoBtn . style . verticalAlign = 'middle' ;
898+
899+ infoBtn . addEventListener ( 'click' , function ( e ) {
900+ e . stopPropagation ( ) ;
901+ if ( typeof getProjectInstructions === 'function' ) {
902+ var info = getProjectInstructions ( name ) ;
903+ showInfoModal ( info . title , info . steps ) ;
904+ }
905+ } ) ;
906+
907+ // Make heading display inline if it's a block element
908+ if ( firstHeading . style . display !== 'inline-block' ) {
909+ firstHeading . style . display = 'inline-block' ;
910+ }
911+ firstHeading . appendChild ( infoBtn ) ;
912+ }
913+ }
815914 } ) ;
816915
817916 removeTrap = trapFocus ( modal ) ;
@@ -820,7 +919,7 @@ document.addEventListener('DOMContentLoaded', function () {
820919 if ( firstFocusable && typeof firstFocusable . focus === 'function' ) {
821920 firstFocusable . focus ( { preventScroll : true } ) ;
822921 }
823- }
922+ }
824923
825924 function closeProjectSafe ( ) {
826925 if ( ! modal || ! modal . classList . contains ( 'active' ) ) return ;
0 commit comments