|
1 | | -import { readFileSync, writeFileSync, mkdirSync, copyFileSync } from 'fs'; |
| 1 | +import { readFileSync, writeFileSync, mkdirSync, copyFileSync, existsSync } from 'fs'; |
2 | 2 | import { join, dirname } from 'path'; |
3 | 3 | import { fileURLToPath } from 'url'; |
4 | 4 | import QRCode from 'qrcode'; |
@@ -28,9 +28,16 @@ copyFileSync( |
28 | 28 | // Copy the brand intro video used as the looping hero on the fractional-cfo page |
29 | 29 | copyFileSync( |
30 | 30 | join(assetsDir, 'animation', 'Make Bold Intro.mp4'), |
31 | | - join(docsDir, content.fractionalCfo.video.src) |
| 31 | + join(docsDir, content.servicePages.find(p => p.slug === 'fractional-cfo').video.src) |
32 | 32 | ); |
33 | 33 |
|
| 34 | +// Copy the project-engagements intro video used as the looping hero on the |
| 35 | +// projects page, once it has been exported and dropped into attached_assets. |
| 36 | +const projectsVideoSrc = join(assetsDir, 'Projects Animation.mp4'); |
| 37 | +if (existsSync(projectsVideoSrc)) { |
| 38 | + copyFileSync(projectsVideoSrc, join(docsDir, content.servicePages.find(p => p.slug === 'projects').video.src)); |
| 39 | +} |
| 40 | + |
34 | 41 | // Copy favicon |
35 | 42 | const staticDir = join(__dirname, 'static'); |
36 | 43 | copyFileSync(join(staticDir, 'favicon.png'), join(docsDir, 'favicon.png')); |
@@ -1062,20 +1069,11 @@ h1, h2, h3, h4, h5, h6 { |
1062 | 1069 | .card-spark-link:hover { color: var(--primary); } |
1063 | 1070 | .card-footer { text-align: center; font-size: 0.75rem; color: var(--muted-fg); opacity: 0.7; padding: 1.5rem 0; } |
1064 | 1071 |
|
1065 | | -/* Fractional CFO — video hero + restated content */ |
1066 | | -.deck-hero { text-align: center; padding: 2.5rem 0 1.5rem; } |
1067 | | -.deck-hero .kicker { color: var(--primary); font-weight: 800; letter-spacing: 0.18em; text-transform: uppercase; font-size: 0.8rem; } |
1068 | | -.deck-hero h1 { font-size: 2.5rem; margin: 0.75rem 0; } |
1069 | | -.deck-hero p { color: var(--muted-fg); max-width: 40rem; margin: 0 auto; } |
1070 | | -.deck-intro { color: var(--muted-fg); max-width: 40rem; margin: 2.5rem auto 0; text-align: center; font-size: 1.0625rem; line-height: 1.7; } |
| 1072 | +/* Service page video hero */ |
1071 | 1073 | .deck { max-width: 64rem; margin: 0 auto 4rem; padding: 0 1rem; } |
1072 | 1074 | .intro-video-frame { position: relative; overflow: hidden; border-radius: 1.25rem; box-shadow: 0 24px 60px rgba(30,30,30,0.18); background: #1E1E1E; aspect-ratio: 16 / 9; } |
1073 | 1075 | .intro-video-frame video { display: block; width: 100%; height: 100%; object-fit: cover; } |
1074 | 1076 | .intro-video-caption { text-align: center; margin-top: 0.875rem; font-size: 0.8125rem; color: var(--muted-fg); font-style: italic; } |
1075 | | -.intro-cta { text-align: center; margin-top: 2rem; } |
1076 | | -.cfo-card-icon { width: 3.5rem; height: 3.5rem; margin-bottom: 1.5rem; } |
1077 | | -.cfo-card-kicker { color: var(--primary); font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; font-size: 0.75rem; display: block; margin-bottom: 0.625rem; } |
1078 | | -@media (max-width: 640px) { .deck-hero h1 { font-size: 1.9rem; } } |
1079 | 1077 | `; |
1080 | 1078 |
|
1081 | 1079 | // Write CSS once as a shared external file so browsers cache it across page |
@@ -1613,6 +1611,15 @@ function buildServicePage(page) { |
1613 | 1611 | <p class="text-xl text-white-80 max-w-2xl">${escapeHtml(page.subtitle)}</p> |
1614 | 1612 | </div> |
1615 | 1613 | </div> |
| 1614 | +${page.video ? ` |
| 1615 | + <div class="deck" style="padding-top:2.5rem"> |
| 1616 | + <div class="intro-video-frame"> |
| 1617 | + <video autoplay muted loop playsinline aria-label="${escapeAttr(page.video.label)}"> |
| 1618 | + <source src="${escapeAttr(page.video.src)}" type="video/mp4"> |
| 1619 | + </video> |
| 1620 | + </div> |
| 1621 | + <p class="intro-video-caption">${escapeHtml(page.video.label)}</p> |
| 1622 | + </div>` : ''} |
1616 | 1623 |
|
1617 | 1624 | <!-- Intro --> |
1618 | 1625 | <div class="container section"> |
@@ -1892,72 +1899,6 @@ function buildContact() { |
1892 | 1899 | </section>` + footer() + closePage(); |
1893 | 1900 | } |
1894 | 1901 |
|
1895 | | -function buildFractionalCfo() { |
1896 | | - const p = content.fractionalCfo; |
1897 | | - const intro = p.slides[0]; |
1898 | | - const closing = p.slides[p.slides.length - 1]; |
1899 | | - const middleSlides = p.slides.slice(1, -1); |
1900 | | - |
1901 | | - const cards = middleSlides.map(s => { |
1902 | | - const bullets = s.bullets ? `<div class="features">${s.bullets.map(b => `<div class="feature">${escapeHtml(b)}</div>`).join('')}</div>` : ''; |
1903 | | - return ` |
1904 | | - <div class="service-card"> |
1905 | | - <div class="icon-box cfo-card-icon">${icon(s.icon, 40)}</div> |
1906 | | - <span class="cfo-card-kicker">${escapeHtml(s.kicker)}</span> |
1907 | | - <h3>${escapeHtml(s.title)}</h3> |
1908 | | - <p>${escapeHtml(s.body)}</p> |
1909 | | - ${bullets} |
1910 | | - </div>`; |
1911 | | - }).join(''); |
1912 | | - |
1913 | | - return htmlHead( |
1914 | | - 'Fractional CFO & Executive Leadership', |
1915 | | - 'Fractional CFO and executive leadership from Make Bold Solutions — data-driven financial strategy that bridges operations and the bottom line for growing businesses.', |
1916 | | - 'fractional-cfo.html' |
1917 | | - ) + navigation('fractional-cfo.html') + ` |
1918 | | -
|
1919 | | - <div class="deck-hero container"> |
1920 | | - <span class="kicker">${escapeHtml(p.kicker)}</span> |
1921 | | - <h1>${escapeHtml(p.title)}</h1> |
1922 | | - <p>${escapeHtml(p.subtitle)}</p> |
1923 | | - </div> |
1924 | | -
|
1925 | | - <div class="deck"> |
1926 | | - <div class="intro-video-frame"> |
1927 | | - <video autoplay muted loop playsinline aria-label="${escapeAttr(p.video.label)}"> |
1928 | | - <source src="${escapeAttr(p.video.src)}" type="video/mp4"> |
1929 | | - </video> |
1930 | | - </div> |
1931 | | - <p class="intro-video-caption">${escapeHtml(p.video.label)}</p> |
1932 | | - <div class="intro-cta"> |
1933 | | - <a href="contact.html" class="btn-primary">Start Your Climb to Value</a> |
1934 | | - </div> |
1935 | | - <p class="deck-intro">${escapeHtml(intro.body)}</p> |
1936 | | - </div> |
1937 | | -
|
1938 | | - <section class="section-muted section"> |
1939 | | - <div class="container"> |
1940 | | - <div class="text-center max-w-2xl mx-auto mb-12"> |
1941 | | - <h2 class="text-3xl font-heading font-bold mb-4">${escapeHtml(p.sectionHeading)}</h2> |
1942 | | - <p class="text-muted">${escapeHtml(p.sectionIntro)}</p> |
1943 | | - </div> |
1944 | | - <div class="grid grid-2-lg" style="gap:1.5rem">${cards} |
1945 | | - </div> |
1946 | | - </div> |
1947 | | - </section> |
1948 | | -
|
1949 | | - <section class="section-primary section"> |
1950 | | - <div class="container text-center"> |
1951 | | - <h2 class="text-3xl font-heading font-bold mb-6">${escapeHtml(closing.title)}</h2> |
1952 | | - <p class="text-white-80 max-w-2xl mx-auto mb-10 text-lg">${escapeHtml(closing.body)}</p> |
1953 | | - <div class="flex flex-col sm-flex-row gap-4 justify-center"> |
1954 | | - <a href="contact.html" class="btn-white btn-large">Start Your Climb to Value</a> |
1955 | | - <a href="services-cfo.html" class="btn-outline-white btn-large">Explore Our Services</a> |
1956 | | - </div> |
1957 | | - </div> |
1958 | | - </section>` + footer() + closePage(); |
1959 | | -} |
1960 | | - |
1961 | 1902 | function buildNotFound() { |
1962 | 1903 | const nf = content.notFound; |
1963 | 1904 | return htmlHead('Page Not Found', nf.description, '404.html', { robots: 'noindex, follow' }) + navigation('') + ` |
@@ -2160,7 +2101,6 @@ const pages = [ |
2160 | 2101 | { file: 'about.html', builder: buildAbout }, |
2161 | 2102 | { file: 'cfo-of-the-year.html', builder: buildCfoOfTheYear }, |
2162 | 2103 | { file: 'contact.html', builder: buildContact }, |
2163 | | - { file: 'fractional-cfo.html', builder: buildFractionalCfo }, |
2164 | 2104 | { file: '404.html', builder: buildNotFound }, |
2165 | 2105 | ...cards.map(person => ({ file: `${person.slug}/card/index.html`, builder: () => buildCard(person) })), |
2166 | 2106 | ]; |
|
0 commit comments