@@ -47,6 +47,7 @@ <h1 class="mt-4 text-3xl font-semibold leading-tight sm:text-4xl">
4747 showPartnerEventsModal : false ,
4848 showCsvModal : false ,
4949 showLeadsGuideModal : false ,
50+ showPartnersHelpModal : false ,
5051 activeStep : 0 ,
5152 band : 'band1' ,
5253 activeBand : 'band1' ,
@@ -188,42 +189,42 @@ <h1 class="mt-4 text-3xl font-semibold leading-tight sm:text-4xl">
188189 deliverables : [ "Registration form (company, domain, contacts, use case)" , "Approval notification" ] ,
189190 } ,
190191 Identification : {
191- label : "Identification (10% Pipeline )" ,
192+ label : "Identification (10%)" ,
192193 description : "Initial contact made; meeting confirmed; problem identified." ,
193194 entryCriteria : [ "Approved lead" , "Intro outreach completed" ] ,
194195 exitCriteria : [ "Discovery meeting held" , "Clear problem statement" ] ,
195196 deliverables : [ "Discovery notes" , "Next-step email / agenda" ] ,
196197 } ,
197198 Qualification : {
198- label : "Qualification (25% Pipeline )" ,
199+ label : "Qualification (25%)" ,
199200 description : "Pain and decision process validated; budget signal present; technical feasibility confirmed." ,
200201 entryCriteria : [ "Engaged prospect" , "Business + technical discovery" ] ,
201202 exitCriteria : [ "Budget/timeline/authority captured" , "Technical viability confirmed" ] ,
202203 deliverables : [ "MEDDICC summary" , "Qualified Opportunity in CRM" ] ,
203204 } ,
204205 Development : {
205- label : "Development (50% Best Case )" ,
206+ label : "Development (50%)" ,
206207 description : "Solutioning and validation; business case and compelling event linked to value." ,
207208 entryCriteria : [ "Technical work started" , "Solution fit agreed" ] ,
208209 exitCriteria : [ "Technical Win achieved" , "Budget allocated" ] ,
209210 deliverables : [ "Technical validation checklist" , "Mutual Action Plan (MAP)" , "High-level architecture / scope" ] ,
210211 } ,
211212 Proposal : {
212- label : "Proposal (75% Best Case )" ,
213+ label : "Proposal (75%)" ,
213214 description : "Commercial alignment; quote/SOW delivered; legal/security reviews in progress." ,
214215 entryCriteria : [ "Budget identified" , "Solution design accepted" ] ,
215216 exitCriteria : [ "Budget approval in progress" , "Security/Legal submitted" ] ,
216217 deliverables : [ "Final quote or SOW" , "Security questionnaire / DPA (if required)" ] ,
217218 } ,
218219 Closing : {
219- label : "Closing (90% Commit )" ,
220+ label : "Closing (90%)" ,
220221 description : "Signature or PO routing; go-live plan established." ,
221222 entryCriteria : [ "Commercial terms finalized" , "Redlines near completion" ] ,
222223 exitCriteria : [ "Signature/PO received" , "Launch date confirmed" ] ,
223224 deliverables : [ "Executed order form/PO" , "Launch plan" ] ,
224225 } ,
225226 ClosedWon : {
226- label : "Closed Won (100% Closed) " ,
227+ label : "Closed Won" ,
227228 description : "Provisioning and fulfillment completed; CS intro scheduled." ,
228229 entryCriteria : [ "Executed paperwork" ] ,
229230 exitCriteria : [ "Software/license delivered" , "CS kickoff scheduled" ] ,
@@ -290,7 +291,8 @@ <h1 class="mt-4 text-3xl font-semibold leading-tight sm:text-4xl">
290291 } ,
291292 } ,
292293 rows : [ "PQL" , "Identification" , "Qualification" , "Development" , "Proposal" , "Closing" , "ClosedWon" , "Onboarding" ] ,
293- activeAccordion : null ,
294+ selectedStage : null ,
295+ showStageModal : false ,
294296 } ,
295297 msp : {
296298 overview : "Welcome to the Recast MSP Partner Program\n\nThe Recast MSP Program is built for service providers that want to embed Recast’s solutions into their service offerings to drive recurring revenue, expand client value, and scale with predictable economics.\n\nWe support multiple MSP archetypes—from infra-focused to app-centric—with enablement tailored to each type. Whether you’re a high-touch implementation shop or a lightweight automation-led MSP, this program offers flexible models to fit your business.\n\nProgram Highlights:\n\n* Monthly point-based licensing structure for easy cost forecasting\n\n* Tiered margin bands based on deal ownership and engagement\n\n* Access to white-labeled deployment kits, SOWs, and enablement tools\n\n* Support for both application workspace and endpoint solutions\n\n* Designed to protect partner margin, loyalty, and renewal rights" ,
@@ -655,6 +657,12 @@ <h1 class="mt-4 text-3xl font-semibold leading-tight sm:text-4xl">
655657 if ( STATE . showLeadsGuideModal ) {
656658 html += renderLeadsGuideModal ( ) ;
657659 }
660+ if ( STATE . salesCycle . showStageModal ) {
661+ html += renderStageModal ( ) ;
662+ }
663+ if ( STATE . showPartnersHelpModal ) {
664+ html += renderPartnersHelpModal ( ) ;
665+ }
658666 document . getElementById ( 'app' ) . innerHTML = html ;
659667 lucide . createIcons ( ) ;
660668 addEventListeners ( ) ;
@@ -741,6 +749,9 @@ <h2 class="text-xl font-medium mb-4">Select Partner Types to Show</h2>
741749 function renderOverview ( overview , showButtons ) {
742750 let html = '<h2 class="text-xl font-medium mb-6 flex items-center gap-3"><i data-lucide="book-open" class="h-6 w-6 stroke-[#2563eb]"></i>Overview</h2>' ;
743751 if ( STATE . partnerType === 'Program Overview' ) {
752+ html += '<div class="mt-6 flex flex-col sm:flex-row gap-4">' ;
753+ html += '<button id="partnersHelpButton" class="rounded-full border border-[#E2E8F0] px-6 py-3 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-all duration-300 flex items-center justify-center gap-2 focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:ring-offset-2"><i data-lucide="users" class="h-4 w-4"></i>Why Partner with us?</button>' ;
754+ html += '</div>' ;
744755 html += renderProgramOverviewMatrix ( ) ;
745756 } else {
746757 html += `<p class="text-gray-700 leading-relaxed whitespace-pre-line transition-opacity duration-300">${ overview } </p>` ;
@@ -1069,26 +1080,18 @@ <h2 class="text-xl font-medium mb-4">Select Partner Types to Show</h2>
10691080 let html = '<h2 class="text-xl font-medium mb-6 flex items-center gap-3"><i data-lucide="table" class="h-6 w-6 stroke-[#2563eb]"></i>Sales Motion</h2>' ;
10701081 html += '<div class="mb-6 rounded-2xl bg-white p-6 shadow-md">' ;
10711082 html += '<h3 class="text-lg font-medium mb-4 flex items-center gap-2"><i data-lucide="workflow" class="h-5 w-5 stroke-[#2563eb]"></i>Sales Cycle Stages</h3>' ;
1072- html += '<div class="space-y-4">' ;
1083+ html += '<div class="relative before:content-[\'\'] before:absolute before:top-0 before:left-0 before:w-10 before:h-full before:z-10 before:pointer-events-none before:bg-gradient-to-r before:from-white before:to-transparent after:content-[\'\'] after:absolute after:top-0 after:right-0 after:w-10 after:h-full after:z-10 after:pointer-events-none after:bg-gradient-to-l after:from-white after:to-transparent">' ;
1084+ html += '<div class="flex overflow-x-auto gap-2 pb-4 [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden">' ;
1085+ const colors = [ 'bg-yellow-100' , 'bg-green-100' , 'bg-blue-100' , 'bg-purple-100' , 'bg-pink-100' , 'bg-yellow-100' , 'bg-green-100' , 'bg-blue-100' ] ;
10731086 STATE . salesCycle . rows . forEach ( ( row , index ) => {
1074- const active = STATE . salesCycle . activeAccordion === index ;
10751087 const stage = STATE . salesCycle . stages [ row ] ;
10761088 html += `
1077- <div class="border border-[#E2E8F0] rounded-2xl overflow-hidden">
1078- <button data-accordion="${ index } " class="w-full flex justify-between items-center px-6 py-4 text-left font-medium text-sm bg-gray-50 hover:bg-gray-100 transition-colors">
1079- <span>${ stage . label } </span>
1080- <i data-lucide="${ active ? 'chevron-up' : 'chevron-down' } " class="h-4 w-4"></i>
1081- </button>
1082- <div class="${ active ? '' : 'hidden' } px-6 py-4 text-sm text-gray-700 space-y-2">
1083- <p><strong>Description:</strong> ${ stage . description } </p>
1084- <p><strong>Entry Criteria:</strong> ${ stage . entryCriteria . join ( ', ' ) } </p>
1085- <p><strong>Exit Criteria:</strong> ${ stage . exitCriteria . join ( ', ' ) } </p>
1086- <p><strong>Deliverables:</strong> ${ stage . deliverables . join ( ', ' ) } </p>
1087- </div>
1088- </div>
1089+ <button data-stage="${ row } " class="flex-shrink-0 ${ colors [ index ] } rounded-full px-4 py-2 text-sm font-medium text-gray-800 transition-all duration-200 ease-in-out cursor-pointer hover:shadow-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:ring-offset-2">
1090+ ${ stage . label }
1091+ </button>
10891092 ` ;
10901093 } ) ;
1091- html += '</div></div>' ;
1094+ html += '</div></div></div> ' ;
10921095 html += '<div class="mb-6 rounded-2xl bg-white p-6 shadow-md">' ;
10931096 html += '<h3 class="text-lg font-medium mb-4 flex items-center gap-2"><i data-lucide="layers" class="h-5 w-5 stroke-[#2563eb]"></i>Band Overviews</h3>' ;
10941097 html += '<div class="grid grid-cols-1 md:grid-cols-3 gap-4">' ;
@@ -1527,6 +1530,90 @@ <h3 class="text-xl font-medium mb-4 flex items-center gap-2"><i data-lucide="shi
15271530 </div>
15281531 ` ;
15291532 }
1533+ function renderStageModal ( ) {
1534+ const stage = STATE . salesCycle . stages [ STATE . salesCycle . selectedStage ] ;
1535+ let left = STATE . salesCycle . modalPosition . left ;
1536+ if ( left + 320 > window . innerWidth ) {
1537+ left = STATE . salesCycle . modalPosition . left - ( left + 320 - window . innerWidth ) ;
1538+ }
1539+ const style = `position: absolute; top: ${ STATE . salesCycle . modalPosition . top } px; left: ${ left } px;` ;
1540+ return `
1541+ <div class="popout-panel open bg-white rounded-2xl shadow-lg w-80 transform scale-100 opacity-100 transition-all duration-200 ease-in-out z-50" style="${ style } ">
1542+ <div class="p-6">
1543+ <h2 class="text-xl font-medium mb-4">${ stage . label } </h2>
1544+ <div class="space-y-4 text-sm text-gray-700">
1545+ <p><strong>Description:</strong> ${ stage . description } </p>
1546+ <p><strong>Entry Criteria:</strong> ${ stage . entryCriteria . join ( ', ' ) } </p>
1547+ <p><strong>Exit Criteria:</strong> ${ stage . exitCriteria . join ( ', ' ) } </p>
1548+ <p><strong>Deliverables:</strong> ${ stage . deliverables . join ( ', ' ) } </p>
1549+ </div>
1550+ </div>
1551+ </div>
1552+ ` ;
1553+ }
1554+ function renderPartnersHelpModal ( ) {
1555+ return `
1556+ <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 px-4" role="dialog" aria-modal="true" id="partnersHelpModal">
1557+ <div class="relative max-w-4xl w-full bg-white rounded-2xl shadow-2xl p-8 max-h-[90vh] overflow-y-auto">
1558+ <button class="absolute top-4 right-4 text-gray-600 hover:text-gray-900 focus:outline-none" id="closePartnersHelp">
1559+ <i data-lucide="x" class="h-6 w-6"></i>
1560+ <span class="sr-only">Close</span>
1561+ </button>
1562+ <h2 class="text-xl font-medium mb-6 flex items-center gap-3"><i data-lucide="users" class="h-6 w-6 stroke-[#2563eb]"></i>Partners do we help?</h2>
1563+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
1564+ <div class="rounded-2xl bg-white p-6 shadow-md">
1565+ <h3 class="text-lg font-medium mb-4">Hybrid MSP / SI Partners</h3>
1566+ <h4 class="text-base font-medium mb-2">Challenges</h4>
1567+ <ul class="list-disc pl-5 space-y-1 text-gray-700 mb-4">
1568+ <li>Application layer blocks every initiative</li>
1569+ <li>Too much manual app work</li>
1570+ <li>Customer IT overwhelmed</li>
1571+ <li>Project timelines slip</li>
1572+ </ul>
1573+ <h4 class="text-base font-medium mb-2">Business Value</h4>
1574+ <ul class="list-disc pl-5 space-y-1 text-gray-700 mb-4">
1575+ <li>Scale without adding headcount</li>
1576+ <li>Increase ARR + project output</li>
1577+ <li>Faster, more predictable delivery</li>
1578+ <li>Stronger valuation via automation</li>
1579+ </ul>
1580+ <h4 class="text-base font-medium mb-2">Customer Impact</h4>
1581+ <ul class="list-disc pl-5 space-y-1 text-gray-700">
1582+ <li>Faster modernization + migration</li>
1583+ <li>Fewer app incidents</li>
1584+ <li>Clear path to readiness</li>
1585+ <li>More stable environments</li>
1586+ </ul>
1587+ </div>
1588+ <div class="rounded-2xl bg-white p-6 shadow-md">
1589+ <h3 class="text-lg font-medium mb-4">ISV / OEM Partners</h3>
1590+ <h4 class="text-base font-medium mb-2">Challenges</h4>
1591+ <ul class="list-disc pl-5 space-y-1 text-gray-700 mb-4">
1592+ <li>Application readiness stalls adoption</li>
1593+ <li>Pilots fail due to app gaps</li>
1594+ <li>Customers lack capacity</li>
1595+ <li>Sales motions slow down</li>
1596+ </ul>
1597+ <h4 class="text-base font-medium mb-2">Business Value</h4>
1598+ <ul class="list-disc pl-5 space-y-1 text-gray-700 mb-4">
1599+ <li>Faster pilots + rollouts</li>
1600+ <li>Higher win rates</li>
1601+ <li>Larger, stickier deployments</li>
1602+ <li>Predictable co-sell motions</li>
1603+ </ul>
1604+ <h4 class="text-base font-medium mb-2">Customer Impact</h4>
1605+ <ul class="list-disc pl-5 space-y-1 text-gray-700">
1606+ <li>Smooth onboarding</li>
1607+ <li>Lower deployment risk</li>
1608+ <li>Compatible applications</li>
1609+ <li>Faster value from the platform</li>
1610+ </ul>
1611+ </div>
1612+ </div>
1613+ </div>
1614+ </div>
1615+ ` ;
1616+ }
15301617 function addEventListeners ( ) {
15311618 document . getElementById ( 'partnerType' ) . addEventListener ( 'change' , ( e ) => {
15321619 STATE . partnerType = e . target . value ;
@@ -1753,14 +1840,45 @@ <h3 class="text-xl font-medium mb-4 flex items-center gap-2"><i data-lucide="shi
17531840 render ( ) ;
17541841 } ) ;
17551842 } ) ;
1756- document . querySelectorAll ( '[data-accordion ]' ) . forEach ( btn => {
1843+ document . querySelectorAll ( '[data-stage ]' ) . forEach ( btn => {
17571844 btn . addEventListener ( 'click' , ( ) => {
1758- const index = parseInt ( btn . dataset . accordion ) ;
1759- STATE . salesCycle . activeAccordion = STATE . salesCycle . activeAccordion === index ? null : index ;
1845+ const rect = btn . getBoundingClientRect ( ) ;
1846+ STATE . salesCycle . modalPosition = { top : rect . bottom + window . scrollY , left : rect . left + window . scrollX } ;
1847+ STATE . salesCycle . selectedStage = btn . dataset . stage ;
1848+ STATE . salesCycle . showStageModal = true ;
17601849 render ( ) ;
17611850 } ) ;
17621851 } ) ;
1763- const modals = document . querySelectorAll ( '#businessPlanModal, #partnerEventsModal, #evaluationModal, #csvModal, #leadsGuideModal' ) ;
1852+ document . addEventListener ( 'click' , ( e ) => {
1853+ if ( STATE . salesCycle . showStageModal && ! e . target . closest ( '.popout-panel' ) && ! e . target . closest ( '[data-stage]' ) ) {
1854+ STATE . salesCycle . showStageModal = false ;
1855+ render ( ) ;
1856+ }
1857+ } ) ;
1858+ const partnersHelpButton = document . getElementById ( 'partnersHelpButton' ) ;
1859+ if ( partnersHelpButton ) {
1860+ partnersHelpButton . addEventListener ( 'click' , ( ) => {
1861+ STATE . showPartnersHelpModal = true ;
1862+ render ( ) ;
1863+ } ) ;
1864+ }
1865+ const closePartnersHelp = document . getElementById ( 'closePartnersHelp' ) ;
1866+ if ( closePartnersHelp ) {
1867+ closePartnersHelp . addEventListener ( 'click' , ( ) => {
1868+ STATE . showPartnersHelpModal = false ;
1869+ render ( ) ;
1870+ } ) ;
1871+ }
1872+ const partnersHelpModal = document . getElementById ( 'partnersHelpModal' ) ;
1873+ if ( partnersHelpModal ) {
1874+ partnersHelpModal . addEventListener ( 'click' , ( e ) => {
1875+ if ( e . target === partnersHelpModal ) {
1876+ STATE . showPartnersHelpModal = false ;
1877+ render ( ) ;
1878+ }
1879+ } ) ;
1880+ }
1881+ const modals = document . querySelectorAll ( '#businessPlanModal, #partnerEventsModal, #evaluationModal, #csvModal, #leadsGuideModal, #partnersHelpModal' ) ;
17641882 modals . forEach ( m => {
17651883 m . addEventListener ( 'click' , ( e ) => {
17661884 if ( e . target === m ) {
@@ -1769,18 +1887,21 @@ <h3 class="text-xl font-medium mb-4 flex items-center gap-2"><i data-lucide="shi
17691887 STATE . showEvaluationModal = false ;
17701888 STATE . showCsvModal = false ;
17711889 STATE . showLeadsGuideModal = false ;
1890+ STATE . showPartnersHelpModal = false ;
17721891 render ( ) ;
17731892 }
17741893 } ) ;
17751894 } ) ;
17761895 document . addEventListener ( 'keydown' , ( e ) => {
1777- if ( e . key === 'Escape' && ( STATE . showBusinessPlanModal || STATE . showPartnerEventsModal || STATE . showEvaluationModal || STATE . showCsvModal || STATE . showLeadsGuideModal || STATE . filterModalOpen ) ) {
1896+ if ( e . key === 'Escape' && ( STATE . showBusinessPlanModal || STATE . showPartnerEventsModal || STATE . showEvaluationModal || STATE . showCsvModal || STATE . showLeadsGuideModal || STATE . filterModalOpen || STATE . salesCycle . showStageModal || STATE . showPartnersHelpModal ) ) {
17781897 STATE . showBusinessPlanModal = false ;
17791898 STATE . showPartnerEventsModal = false ;
17801899 STATE . showEvaluationModal = false ;
17811900 STATE . showCsvModal = false ;
17821901 STATE . showLeadsGuideModal = false ;
17831902 STATE . filterModalOpen = false ;
1903+ STATE . salesCycle . showStageModal = false ;
1904+ STATE . showPartnersHelpModal = false ;
17841905 render ( ) ;
17851906 }
17861907 } ) ;
0 commit comments