/* global React, Icon, AnimatedCounter */ // Shared building blocks for product landing pages. const PStat = ({ n, l }) => (
{l}
); const PFeat = ({ num, title, body }) => (
{num}

{title}

{body}

); const PStep = ({ n, title, body }) => (
{n}

{title}

{body}

); // Generic product page renderer const ProductLanding = ({ go, accent, iconName, eyebrow, version, h1, lead, mock, stats, featTitle, feats, installSteps, ctaTitle, ctaBody }) => (
{eyebrow}
{version} · D365 9.2+ · Power Apps

{h1}

{lead}

{mock}
{stats.map((s, i) => )}
What it does

{featTitle}

{feats.map((f, i) => )}
Install

Three steps. About one minute.

Sandbox first. Production when you're ready.

{installSteps.map((s, i) => )}

{ctaTitle}

{ctaBody}

{ e.preventDefault(); go('contact'); }}>Get the trial link
); // ====== KANBAN BOARD MOCK ====== const KanbanMock = () => (
org.crm.dynamics.com / opportunities / kanban
{[ { title: 'Qualify', count: 4, color: '#0F6CBD', cards: ['Contoso · $48k', 'Fabrikam · $12k', 'Adventure · $89k', 'Northwind · $5k'] }, { title: 'Propose', count: 2, color: '#A16207', cards: ['Tailwind · $120k', 'Wide World · $34k'] }, { title: 'Close', count: 1, color: '#15803D', cards: ['Litware · $210k'] }, ].map((col, i) => (
{col.title} {col.count}
{col.cards.map((c, j) => (
{c.split(' · ')[0]}
{c.split(' · ')[1]}
))}
))}
); const KanbanPage = ({ go }) => ( } stats={[ { n: '60s', l: 'Average install time' }, { n: 'Any', l: 'Choice column as columns' }, { n: '<100ms', l: 'Drag-to-save latency' }, { n: '14+', l: 'Languages supported' }, ]} featTitle="Six things that make it feel native." feats={[ { title: 'Bind to any choice column', body: 'Status, stage, priority, custom — pick a choice column and the board renders columns from it. No metadata config.' }, { title: 'Swimlanes by owner or team', body: 'Toggle a horizontal split by owner, business unit, or any lookup. Useful for stand-ups and load balancing.' }, { title: 'Drag = write back', body: 'Every drag triggers a Web API update against Dataverse. Honors business rules, security roles, and audit logging.' }, { title: 'WIP limits', body: 'Cap any column at N records. Once exceeded, the column shows a warning chip and the next drag is rejected with an explainer.' }, { title: 'Inline filters', body: 'Filter by owner, date range, or any custom view filter. Persists per user — your settings survive a reload.' }, { title: 'Keyboard friendly', body: 'Tab between cards, space to lift, arrow keys to move, enter to drop. Full accessibility out of the box.' }, ]} installSteps={[ { title: 'Download solution', body: <>Grab KanbanBoard_managed.zip from your dashboard. }, { title: 'Import to your environment', body: <>Power Platform admin center → Solutions → Import. Pick the .zip. }, { title: 'Add to a view', body: <>Open Form Designer or App Designer, drop KanbanBoard_Control on a section, save & publish. }, ]} ctaTitle="Free in your sandbox forever." ctaBody="Bundled with Gantt and Video Playlist on paid plans. Try the bundle free in any sandbox environment." /> ); // ====== GANTT CHART MOCK ====== const GanttMock = () => (
org.crm.dynamics.com / projects / Q2-launch
{['W1', 'W2', 'W3', 'W4', 'W5', 'W6', 'W7', 'W8', 'W9', 'W10'].map((w) =>
{w}
)}
{[ { task: 'Discovery', start: 0, len: 2, color: '#15803D' }, { task: 'Design', start: 1, len: 3, color: '#15803D' }, { task: 'Build · API', start: 3, len: 4, color: '#0F6CBD' }, { task: 'Build · UI', start: 4, len: 3, color: '#0F6CBD' }, { task: 'QA', start: 6, len: 2, color: '#A16207' }, { task: 'Launch', start: 8, len: 1, color: '#4F46E5' }, ].map((bar, i) => (
{bar.task}
{Array.from({ length: 10 }).map((_, c) => { const inBar = c >= bar.start && c < bar.start + bar.len; return (
{inBar &&
}
); })}
))}
Discovery Build QA
); const GanttPage = ({ go }) => ( } stats={[ { n: 'Any', l: 'Entity with two dates' }, { n: '6', l: 'Dependency types' }, { n: '∞', l: 'Tasks per chart' }, { n: '✓', l: 'Print-clean export' }, ]} featTitle="Project planning, the way Microsoft would build it." feats={[ { title: 'Drag-resize bars', body: 'Grab either edge to change start or end. Grab the middle to slide the whole task. Every change writes back via Web API.' }, { title: 'Smart dependencies', body: 'FS, SS, FF, SF — all four dependency types, with auto-shift on the dependent when you move a predecessor.' }, { title: 'Weekend awareness', body: 'Configurable working days and company holidays. Bars skip non-working days; durations reflect actual work time.' }, { title: 'Critical path', body: 'One toggle highlights the longest dependency chain. Useful for triage when a deadline starts slipping.' }, { title: 'Zoom levels', body: 'Day, week, month, quarter, year. Smooth zoom transitions; the bar widths recalc, the label density adapts.' }, { title: 'Print-clean PDF', body: 'A landscape A3 export that does not look like 2008. Page-break aware, header repeats per page, fits any modern printer.' }, ]} installSteps={[ { title: 'Download solution', body: <>Grab GanttChart_managed.zip from your dashboard. }, { title: 'Import to your environment', body: <>Power Platform admin center → Solutions → Import. Pick the .zip. }, { title: 'Bind to your entity', body: <>Open Form Designer, drop GanttChart_Control on a section, point it at your start/end date columns. }, ]} ctaTitle="Beta is open. Sign up to break it." ctaBody="Free during beta. Help us shake out the edges and we'll thank you in the v1 release notes." /> ); // ====== VIDEO PLAYLIST MOCK ====== const VideoMock = () => (
org.crm.dynamics.com / training / new-hire
Module 2 · Logging cases in D365
2:146:32
Playlist · 5 of 8
{[ { t: 'Welcome', d: '1:42', done: true, active: false }, { t: 'D365 navigation', d: '4:18', done: true, active: false }, { t: 'Logging cases', d: '6:32', done: false, active: true }, { t: 'Resolving cases', d: '5:21', done: false, active: false }, { t: 'Reporting', d: '7:55', done: false, active: false }, ].map((it, i) => (
{it.done ? '✓' : i + 1}
{it.t}
{it.d}
))}
); const VideoPage = ({ go }) => ( } stats={[ { n: '2.4×', l: 'Faster first-ticket close' }, { n: 'Per-user', l: 'Watch progress saved' }, { n: 'YouTube,', l: 'Vimeo, MP4, Stream' }, { n: '0', l: 'Tabs to switch' }, ]} featTitle="The training video, where the work actually happens." feats={[ { title: 'Bind to any entity', body: 'Attach a playlist to a record, a record type, or a user role. Different playlists for different teams, no code.' }, { title: 'Per-user progress', body: 'Each user\'s watch progress is saved per video and per playlist. Resume right where they left off, on any device.' }, { title: 'Completion gating', body: 'Optionally lock a form action behind playlist completion. New hires cannot resolve cases until they have watched the SOP video.' }, { title: 'Multi-source', body: 'YouTube, Vimeo, Microsoft Stream, raw MP4, and SharePoint links all play in the same shell. Mix and match in one playlist.' }, { title: 'Captions and transcripts', body: 'WebVTT captions render natively. Transcripts surface as a side panel and are searchable across the playlist.' }, { title: 'Reporting', body: 'A built-in dashboard entity tracks watch time per user, per video, per role. Drop into any Power BI report.' }, ]} installSteps={[ { title: 'Download solution', body: <>Grab VideoPlaylist_managed.zip from your dashboard. }, { title: 'Import to your environment', body: <>Power Platform admin center → Solutions → Import. Pick the .zip. }, { title: 'Add to a form', body: <>Open Form Designer, drop VideoPlaylist_Control on a section, point it at your video list table. }, ]} ctaTitle="Onboard new hires faster." ctaBody="Customers report a 2.4× reduction in time-to-first-resolved-case once training lives inside the form, not in a separate LMS." /> ); window.KanbanPage = KanbanPage; window.GanttPage = GanttPage; window.VideoPage = VideoPage;