From 39297d4aa529cd448d09c0087795e1b448fcebe8 Mon Sep 17 00:00:00 2001 From: tcpsyn Date: Mon, 16 Mar 2026 01:23:43 -0600 Subject: [PATCH] Growth features: share buttons, NEW badge, sticky CTA, newsletter cross-promote - Share buttons on episode and clip cards (Web Share API + clipboard fallback) - NEW badge on latest episode card - Sticky call-in CTA bar (appears after hero scrolls out) - Daily AI Briefing newsletter cross-promote in footer - Bump cache versions to v=5 Co-Authored-By: Claude Opus 4.6 (1M context) --- website/404.html | 2 +- website/clips.html | 2 +- website/css/style.css | 189 ++++++++++++++++++++++++++++++++++++++ website/episode.html | 2 +- website/how-it-works.html | 2 +- website/index.html | 12 ++- website/js/app.js | 49 +++++++++- website/js/clips.js | 29 ++++++ website/js/footer.js | 32 +++++++ website/privacy.html | 2 +- website/stats.html | 2 +- website/terms.html | 2 +- 12 files changed, 314 insertions(+), 11 deletions(-) diff --git a/website/404.html b/website/404.html index 231dae1..ef26cbd 100644 --- a/website/404.html +++ b/website/404.html @@ -15,7 +15,7 @@ - + diff --git a/website/clips.html b/website/clips.html index 15ae694..66acbbc 100644 --- a/website/clips.html +++ b/website/clips.html @@ -39,7 +39,7 @@ - + diff --git a/website/css/style.css b/website/css/style.css index 0ab000a..bb26822 100644 --- a/website/css/style.css +++ b/website/css/style.css @@ -460,6 +460,113 @@ a:hover { transform: none; } +/* Share Buttons */ +.episode-share-btn, +.clip-share-btn { + background: transparent; + border: none; + color: var(--text-dim); + cursor: pointer; + padding: 0.35rem; + border-radius: 50%; + transition: color 0.2s, background 0.2s; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.episode-share-btn svg, +.clip-share-btn svg { + width: 16px; + height: 16px; +} + +.episode-share-btn:hover, +.clip-share-btn:hover { + color: var(--accent); + background: rgba(212, 164, 74, 0.1); +} + +.episode-share-btn.share-copied, +.clip-share-btn.share-copied { + color: var(--accent); + animation: share-pulse 0.3s ease-out; +} + +@keyframes share-pulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.3); } + 100% { transform: scale(1); } +} + +.clip-share-btn { + position: absolute; + top: 0.5rem; + right: 0.5rem; + z-index: 2; +} + +/* NEW Badge */ +.episode-new-badge { + display: inline-block; + background: var(--accent); + color: #fff; + font-size: 0.6rem; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 0.15em 0.5em; + border-radius: 50px; + vertical-align: middle; +} + +/* Sticky CTA Bar */ +.sticky-cta { + position: fixed; + bottom: 0; + left: 0; + right: 0; + background: rgba(21, 15, 6, 0.92); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + border-top: 1px solid #2a2015; + padding: 0.6rem 1.5rem; + text-align: center; + z-index: 90; + transform: translateY(100%); + transition: transform 0.3s, bottom 0.3s; +} + +.sticky-cta.visible { + transform: translateY(0); +} + +.sticky-cta.player-active { + bottom: 56px; +} + +.sticky-cta-link { + color: var(--text-muted); + font-size: 0.85rem; + display: inline-flex; + align-items: center; + gap: 0.4rem; + transition: color 0.2s; +} + +.sticky-cta-link:hover { + color: var(--accent); +} + +.sticky-cta-link strong { + color: var(--accent); +} + +.sticky-cta-icon { + width: 16px; + height: 16px; +} + /* Testimonials */ .testimonials-section { max-width: 900px; @@ -778,6 +885,80 @@ a:hover { color: var(--accent); } +.footer-newsletter { + margin: 1.25rem 0; + padding: 1rem 0; + border-top: 1px solid #2a2015; +} + +.footer-newsletter-text { + font-size: 0.85rem; + color: var(--text-dim); + margin-bottom: 0.75rem; +} + +.footer-newsletter-name { + color: var(--accent); +} + +.footer-newsletter-form { + display: flex; + justify-content: center; + gap: 0.5rem; + max-width: 420px; + margin: 0 auto; +} + +.footer-newsletter-input { + flex: 1; + min-width: 0; + padding: 0.5rem 0.75rem; + background: #1a1209; + border: 1px solid #3a2f20; + border-radius: 50px; + color: var(--text-light); + font-size: 0.85rem; + outline: none; + transition: border-color 0.2s; +} + +.footer-newsletter-input:focus { + border-color: var(--accent); +} + +.footer-newsletter-input::placeholder { + color: var(--text-dim); +} + +.footer-newsletter-btn { + background: transparent; + color: var(--accent); + border: 2px solid var(--accent); + padding: 0.5rem 1.25rem; + border-radius: 50px; + font-size: 0.85rem; + font-weight: 700; + cursor: pointer; + white-space: nowrap; + transition: background 0.2s, color 0.2s; +} + +.footer-newsletter-btn:hover { + background: var(--accent); + color: #fff; +} + +.footer-newsletter-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.footer-newsletter-success { + font-size: 0.85rem; + color: var(--accent); + font-weight: 600; +} + .footer-contact { margin-bottom: 0.75rem; } @@ -1796,4 +1977,12 @@ p.about-teaser { flex-wrap: wrap; gap: 0.5rem; } + + .footer-newsletter-form { + flex-direction: column; + } + + .footer-newsletter-btn { + width: 100%; + } } diff --git a/website/episode.html b/website/episode.html index fd05d40..b25b693 100644 --- a/website/episode.html +++ b/website/episode.html @@ -30,7 +30,7 @@ - + - + diff --git a/website/js/app.js b/website/js/app.js index bd644d3..bda4566 100644 --- a/website/js/app.js +++ b/website/js/app.js @@ -51,6 +51,7 @@ function truncate(html, maxLen) { // SVG icons const playSVG = ''; const pauseSVG = ''; +const shareSVG = ''; // Fetch with timeout function fetchWithTimeout(url, ms = 8000) { @@ -59,6 +60,26 @@ function fetchWithTimeout(url, ms = 8000) { return fetch(url, { signal: controller.signal }).finally(() => clearTimeout(timeout)); } +async function shareContent(title, url, btn) { + if (navigator.share) { + try { + await navigator.share({ title, url }); + return; + } catch (e) { + if (e.name === 'AbortError') return; + } + } + try { + await navigator.clipboard.writeText(url); + const orig = btn.innerHTML; + btn.innerHTML = 'Copied!'; + btn.classList.add('share-copied'); + setTimeout(() => { btn.innerHTML = orig; btn.classList.remove('share-copied'); }, 2000); + } catch (e) { + prompt('Copy this link:', url); + } +} + // Fetch and parse RSS feed async function fetchEpisodes() { let xml; @@ -130,19 +151,30 @@ function createEpisodeCard(ep) {
${escapeAttr(ep.title)}
${truncate(ep.description, 150)}
${epSlug ? `Read Transcript` : ''} + `; const btn = card.querySelector('.episode-play-btn'); btn.addEventListener('click', () => playEpisode(ep.audioUrl, ep.title, card, btn)); + const shareBtn = card.querySelector('.episode-share-btn'); + const shareUrl = epSlug + ? `${window.location.origin}/episode.html?slug=${encodeURIComponent(epSlug)}` + : window.location.origin; + shareBtn.addEventListener('click', () => shareContent(ep.title, shareUrl, shareBtn)); + return card; } function showMoreEpisodes() { const batch = allEpisodes.slice(displayedCount, displayedCount + EPISODES_PER_PAGE); - batch.forEach((ep) => { - episodesList.appendChild(createEpisodeCard(ep)); + batch.forEach((ep, i) => { + const card = createEpisodeCard(ep); + if (displayedCount === 0 && i === 0) { + card.querySelector('.episode-meta').insertAdjacentHTML('afterbegin', 'NEW '); + } + episodesList.appendChild(card); }); displayedCount += batch.length; @@ -185,6 +217,7 @@ function playEpisode(url, title, card, btn) { playerTitle.textContent = title; stickyPlayer.classList.add('active'); + if (stickyCta) stickyCta.classList.add('player-active'); } // Episode card icon sync (sticky player icons handled by player.js) @@ -295,6 +328,18 @@ function checkOnAir() { .catch(() => {}); } +// Sticky CTA — show after scrolling past hero +const stickyCta = document.getElementById('sticky-cta'); +const heroSection = document.querySelector('.hero'); +if (stickyCta && heroSection) { + const ctaObserver = new IntersectionObserver(([entry]) => { + const show = !entry.isIntersecting; + stickyCta.classList.toggle('visible', show); + stickyCta.setAttribute('aria-hidden', String(!show)); + }, { threshold: 0 }); + ctaObserver.observe(heroSection); +} + // Init fetchEpisodes(); initTestimonials(); diff --git a/website/js/clips.js b/website/js/clips.js index 5e4b0e6..e2e5e6c 100644 --- a/website/js/clips.js +++ b/website/js/clips.js @@ -1,6 +1,7 @@ const CLIPS_JSON_URL = '/data/clips.json'; const clipPlaySVG = ''; +const clipShareSVG = ''; function escapeHTML(str) { const el = document.createElement('span'); @@ -8,6 +9,26 @@ function escapeHTML(str) { return el.innerHTML; } +async function shareClipContent(title, url, btn) { + if (navigator.share) { + try { + await navigator.share({ title, url }); + return; + } catch (e) { + if (e.name === 'AbortError') return; + } + } + try { + await navigator.clipboard.writeText(url); + const orig = btn.innerHTML; + btn.innerHTML = 'Copied!'; + btn.classList.add('share-copied'); + setTimeout(() => { btn.innerHTML = orig; btn.classList.remove('share-copied'); }, 2000); + } catch (e) { + prompt('Copy this link:', url); + } +} + function renderClipCard(clip, featured) { const card = document.createElement('div'); card.className = 'clip-card' + (featured ? ' clip-card-featured' : ''); @@ -31,6 +52,7 @@ function renderClipCard(clip, featured) {

${title}

${desc}

${hasVideo ? `` : ''} + ${hasVideo ? `` : ''} `; @@ -41,6 +63,13 @@ function renderClipCard(clip, featured) { const inner = card.querySelector('.clip-card-inner'); inner.innerHTML = ``; }); + const shareBtn = card.querySelector('.clip-share-btn'); + if (shareBtn) { + shareBtn.addEventListener('click', (e) => { + e.stopPropagation(); + shareClipContent(clip.title || '', `https://youtube.com/watch?v=${youtubeId}`, shareBtn); + }); + } } return card; diff --git a/website/js/footer.js b/website/js/footer.js index 90a45a9..1682aba 100644 --- a/website/js/footer.js +++ b/website/js/footer.js @@ -41,10 +41,42 @@ function initFooter() { YouTube +

© 2026 Luke at the Roost · Privacy Policy · Terms of Service · System Status

`; + + const form = document.getElementById('footer-newsletter-form'); + const success = document.getElementById('footer-newsletter-success'); + if (form) { + form.addEventListener('submit', async (e) => { + e.preventDefault(); + const btn = form.querySelector('.footer-newsletter-btn'); + const email = form.querySelector('.footer-newsletter-input').value; + btn.disabled = true; + btn.textContent = 'Subscribing...'; + try { + await fetch('https://mmg-form-handler.luke-3b5.workers.dev/api/lead-magnet', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, source: 'lukeattheroost-footer' }) + }); + form.hidden = true; + success.hidden = false; + } catch { + btn.disabled = false; + btn.textContent = 'Subscribe'; + } + }); + } } initFooter(); diff --git a/website/privacy.html b/website/privacy.html index 5f0bea0..7457284 100644 --- a/website/privacy.html +++ b/website/privacy.html @@ -33,7 +33,7 @@ - + diff --git a/website/stats.html b/website/stats.html index 4af0d0a..dbf031d 100644 --- a/website/stats.html +++ b/website/stats.html @@ -39,7 +39,7 @@ - + diff --git a/website/terms.html b/website/terms.html index abca657..6d788f8 100644 --- a/website/terms.html +++ b/website/terms.html @@ -33,7 +33,7 @@ - +