489 lines
22 KiB
Plaintext
489 lines
22 KiB
Plaintext
@model CatherineLynwood.Models.FlagSupportViewModel
|
||
|
||
@{
|
||
ViewData["Title"] = "The Alpha Flame - Coming Soon";
|
||
}
|
||
|
||
<!-- Your existing video container: unchanged -->
|
||
<div class="container">
|
||
<div class="row">
|
||
<div class="col-md-12">
|
||
<!-- H1 for SEO and accessibility -->
|
||
<header class="mb-2 text-center">
|
||
<h1 class="h3 mb-1">The Alpha Flame: <span class="fw-light">Discovery</span></h1>
|
||
<p class="mb-1">A gritty Birmingham crime novel set in 1983</p>
|
||
</header>
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<div class="col-md-12">
|
||
<div class="trailer-wrapper">
|
||
<video id="trailerVideo" playsinline preload="none"></video>
|
||
<button id="trailerPlayBtn" class="trailer-play-btn">
|
||
<i class="fad fa-play"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
@if (DateTime.Now < new DateTime(2025, 8, 21))
|
||
{
|
||
<section>
|
||
<div class="row justify-content-center">
|
||
<div class="col-10 col-md-4 text-center bg-white text-dark border border-dark border-3 rounded-5 mt-3 p-3">
|
||
<h3 class="h4">Released in:</h3>
|
||
<div class="release-countdown mb-2" data-release="2025-08-21T00:00:00+01:00" data-out-text="Out now">
|
||
<span class="rcd d"><span class="num">0</span><span class="label">d</span></span>
|
||
<span class="rcd h"><span class="num">00</span><span class="label">h</span></span>
|
||
<span class="rcd m"><span class="num">00</span><span class="label">m</span></span>
|
||
<span class="rcd s"><span class="num">00</span><span class="label">s</span></span>
|
||
</div>
|
||
<div class="d-grid px-3"><a asp-controller="Discovery" asp-action="Index" class="btn btn-dark btn-pulse">Pre-order Now!</a></div>
|
||
<noscript>Releases on 21 Aug 2025</noscript>
|
||
|
||
</div>
|
||
</div>
|
||
</section>
|
||
}
|
||
|
||
|
||
<!-- Quick interaction: flags -->
|
||
<section class="container py-3">
|
||
<div class="row">
|
||
<div class="col-12 text-center">
|
||
<h2 class="h4 mb-2">Please show your support</h2>
|
||
<p class="mb-3">Tap your flag to show your support.</p>
|
||
</div>
|
||
<div class="col-12">
|
||
<div class="flag-grid" role="group" aria-label="Choose your country">
|
||
@foreach (var kv in Model.FlagCounts)
|
||
{
|
||
string pulse = "";
|
||
var code = kv.Key;
|
||
var count = kv.Value;
|
||
var name = code switch
|
||
{
|
||
"UK" => "UK",
|
||
"US" => "US",
|
||
"CA" => "Canada",
|
||
"AU" => "Australia",
|
||
"IE" => "Ireland",
|
||
"NZ" => "New Zealand",
|
||
_ => code
|
||
};
|
||
var flagFile = code.ToLower() switch
|
||
{
|
||
"uk" => "gb",
|
||
_ => code.ToLower()
|
||
};
|
||
|
||
if (kv.Selected)
|
||
{
|
||
pulse = "btn-pulse";
|
||
}
|
||
<button class="flag-btn @pulse" data-country="@code">
|
||
<img src="/images/flags/@($"{flagFile}.svg")" alt="@name flag">
|
||
<span class="flag-name">@Html.Raw(name)</span>
|
||
<span class="flag-count">(@count)</span>
|
||
</button>
|
||
}
|
||
</div>
|
||
|
||
<!-- Toast/message area -->
|
||
<div id="flagToast" class="flag-toast text-center" role="status" aria-live="polite" style="display:none;">
|
||
<p id="flagMessage" class="mb-2"></p>
|
||
|
||
<!-- Hidden release notification form -->
|
||
<div id="releaseForm" style="display:none;">
|
||
<p>Want me to let you know when the book is released?</p>
|
||
<form id="notifyForm" class="mt-2">
|
||
<input type="text" id="notifyName" name="name" placeholder="Your name (optional)" class="form-control form-control-sm mb-2">
|
||
<input type="email" id="notifyEmail" name="email" placeholder="Enter your email" class="form-control form-control-sm mb-2" required>
|
||
<button type="submit" class="btn btn-sm btn-primary">Notify Me</button>
|
||
</form>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="container py-3">
|
||
<div class="row justify-content-center">
|
||
<div class="col-md-6 text-dark border border-dark border-3 rounded-5 p-4" style="background-image:url('/images/sheet-music.png'); background-position: center; background-size: cover;">
|
||
<h3 class="h5 text-center text-dark pb-3">Listen to The Alpha Flame theme tune<br />The Flame We Found</h3>
|
||
<audio controls="controls">
|
||
<source src="~/audio/the-flame-we-found-original-song-inspired-by-alpha-flame_teaser.mp3" type="audio/mpeg" />
|
||
</audio>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
|
||
<!-- Teaser reel -->
|
||
<section class="container py-3">
|
||
<div class="row">
|
||
<div class="col-12">
|
||
<h2 class="h4 mb-3 text-center">A glimpse inside</h2>
|
||
</div>
|
||
|
||
<div class="col-md-4">
|
||
<article class="teaser-card border border-3 border-dark mb-3">
|
||
<div class="teaser-bg" style="background-image:url('/images/webp/the-alpha-flame-discovery-back-cover-400.webp');"></div>
|
||
<div class="teaser-copy">
|
||
<div>
|
||
<p class="h1 text-warning">
|
||
The Alpha Flame: Discovery
|
||
</p>
|
||
<p>
|
||
Some girls survive. Others set the world on fire.
|
||
</p>
|
||
<p>
|
||
She didn’t go looking for trouble. But when she found Beth, bruised, broken, and terrified, Maggie couldn’t walk away.
|
||
</p>
|
||
<p>
|
||
But nothing prepares her for Beth.
|
||
</p>
|
||
<p>
|
||
As she digs deeper into Beth’s world, Maggie finds herself pulled into the shadows, a seedy underworld of secrets, survival, and control, where loyalty is rare and nothing is guaranteed. The more she uncovers, the more she realises this isn’t someone else’s nightmare. It’s her own.
|
||
</p>
|
||
<p>
|
||
The Alpha Flame: Discovery is a gritty, emotionally charged thriller that pulls no punches. Raw, real, and anything but a fairy tale, it’s a story of survival, sisterhood, and fire.
|
||
</p>
|
||
<div class="teaser-actions">
|
||
<button class="btn btn-sm btn-light" data-audio="#aud4"><i class="fad fa-play"></i> Listen 58s</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<audio id="aud4" preload="none" src="/audio/book-synopsis.mp3"></audio>
|
||
</article>
|
||
</div>
|
||
|
||
<div class="col-md-4">
|
||
<article class="teaser-card border border-3 border-dark mb-3">
|
||
<div class="teaser-bg" style="background-image:url('/images/webp/teaser-city-400.webp');"></div>
|
||
<div class="teaser-copy">
|
||
<div>
|
||
<p>
|
||
I eased the TR6 down a side street, the headlights sweeping over a figure shifting in the shadows. A movement to my left. A woman, young, her face pale beneath the heavy makeup, stepped forward as I slowed at the junction. She leaned down to my passenger window, so close I could see the faint smudge of lipstick at the corner of her mouth.
|
||
</p>
|
||
<p>
|
||
A loud knock on the glass made me jump.
|
||
</p>
|
||
<p>
|
||
“You looking for something, love?” she asked, her voice soft but direct. Her lips were parted just slightly, her breath misting against the cold window.
|
||
</p>
|
||
<p>
|
||
My stomach tightened.
|
||
</p>
|
||
<p>
|
||
I wasn’t looking for anything. Not really. But I didn’t drive away either.
|
||
</p>
|
||
<p>
|
||
She was close now, close enough that I could see the dark liner smudged beneath her eyes, the glint of something unreadable in her gaze. Not quite curiosity. Not quite suspicion. Just a quiet knowing.
|
||
</p>
|
||
<div class="teaser-actions">
|
||
<button class="btn btn-sm btn-light" data-audio="#aud1"><i class="fad fa-play"></i> Listen 50s</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<audio id="aud1" preload="none" src="/audio/snippets/clip-1.mp3"></audio>
|
||
</article>
|
||
</div>
|
||
<div class="col-md-4">
|
||
<article class="teaser-card border border-3 border-dark mb-3">
|
||
<div class="teaser-bg" style="background-image:url('/images/webp/teaser-hospital-400.webp');"></div>
|
||
<div class="teaser-copy">
|
||
<div>
|
||
<p>
|
||
“Maggie… wait.”
|
||
</p>
|
||
<p>
|
||
She turned as I crouched down. My stomach dropped.
|
||
</p>
|
||
<p>
|
||
It was a sweatshirt. Pink. Faded. Cartoon print on the front, cracked with age and wear. Garfield, grinning.
|
||
</p>
|
||
<p>
|
||
I reached out slowly, fingertips brushing the fabric. The left sleeve was soaked, stiff with something dark.
|
||
</p>
|
||
<p>
|
||
Blood.
|
||
</p>
|
||
<p>
|
||
“Maggie…” My voice broke. “It’s hers. She used to wear this all the time. She was wearing it the last time I saw her.”
|
||
</p>
|
||
<p>
|
||
Maggie dropped to her knees beside me, torch trembling in her grip. “Bloody hell. You’re right.”
|
||
</p>
|
||
<p>
|
||
For a second neither of us moved. The building suddenly felt tighter, like it was watching us.
|
||
</p>
|
||
<div class="teaser-actions">
|
||
<button class="btn btn-sm btn-light" data-audio="#aud2"><i class="fad fa-play"></i> Listen 28s</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<audio id="aud2" preload="none" src="/audio/snippets/clip-2.mp3"></audio>
|
||
</article>
|
||
</div>
|
||
|
||
<div class="col-md-4 d-md-none">
|
||
<article class="teaser-card border border-3 border-dark mb-2">
|
||
<div class="teaser-bg" style="background-image:url('/images/webp/teaser-beach-400.webp');"></div>
|
||
<div class="teaser-copy">
|
||
<div>
|
||
<p>
|
||
She turned in the water, soaked to the waist, flinging droplets everywhere.
|
||
</p>
|
||
<p>
|
||
“Maggie! Come on!” she shouted, laughing. “You’ve got to feel this!”
|
||
</p>
|
||
<p>
|
||
I didn’t hesitate.
|
||
</p>
|
||
<p>
|
||
I peeled off my hoody and shorts, left them in a heap on the rocks, and sprinted after her, my bikini clinging tight to my skin in the salty breeze. The sand stung slightly as I ran, then came the cold slap of the sea, wrapping around my legs and dragging a breathless laugh out of me.
|
||
</p>
|
||
<p>
|
||
Beth was already dancing through the waves like a lunatic.
|
||
</p>
|
||
<p>
|
||
We collided mid-splash, both of us soaked, screaming and laughing like we were eight years old again, like we’d somehow got all those childhood summers back in one moment.
|
||
The sea was freezing, but we didn’t care.
|
||
</p>
|
||
<div class="teaser-actions">
|
||
<button class="btn btn-sm btn-light" data-audio="#aud3"><i class="fad fa-play"></i> Listen 37s</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<audio id="aud3" preload="none" src="/audio/snippets/clip-3.mp3"></audio>
|
||
</article>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Footer note -->
|
||
<section class="container pb-4">
|
||
<div class="row justify-content-center">
|
||
<div class="col-md-6 text-center">
|
||
<h3 class="h4 mb-3"><strong>Coming 21st August 2025</strong> to major retailers.</h3>
|
||
<div class="d-grid"><a asp-controller="Discovery" asp-action="Index" class="btn btn-dark btn-pulse">Find Out More</a></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
|
||
|
||
@*
|
||
<responsive-image src="the-alpha-flame-discovery-trailer-landscape.png" class="img-fluid" alt="The Alpha Flame book cover — gritty 1980s Birmingham crime novel about twin sisters uncovering secrets and surviving abuse" display-width-percentage="100"></responsive-image>
|
||
<responsive-image src="the-alpha-flame-discovery-trailer-portrait.png" class="img-fluid" alt="The Alpha Flame book cover — gritty 1980s Birmingham crime novel about twin sisters uncovering secrets and surviving abuse" display-width-percentage="50"></responsive-image>
|
||
<responsive-image src="the-alpha-flame-discovery-back-cover.png" class="img-fluid" alt="The Alpha Flame book cover — gritty 1980s Birmingham crime novel about twin sisters uncovering secrets and surviving abuse" display-width-percentage="50"></responsive-image>
|
||
*@
|
||
@section Scripts {
|
||
<script>
|
||
const player = new Plyr('audio');
|
||
</script>
|
||
|
||
<script>
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
const video = document.getElementById("trailerVideo");
|
||
const playBtn = document.getElementById("trailerPlayBtn");
|
||
|
||
// Pick correct source and poster before loading
|
||
const isDesktop = window.matchMedia("(min-width: 400px)").matches;
|
||
video.poster = isDesktop
|
||
? "/images/webp/the-alpha-flame-discovery-trailer-landscape-1400.webp"
|
||
: "/images/webp/the-alpha-flame-discovery-trailer-portrait-400.webp";
|
||
const src = isDesktop
|
||
? "/videos/the-alpha-flame-discovery-trailer-landscape.mp4"
|
||
: "/videos/the-alpha-flame-discovery-trailer-portrait.mp4";
|
||
const sourceEl = document.createElement("source");
|
||
sourceEl.src = src;
|
||
sourceEl.type = "video/mp4";
|
||
video.appendChild(sourceEl);
|
||
|
||
// Play button click handler
|
||
playBtn.addEventListener("click", () => {
|
||
video.muted = false;
|
||
video.volume = 1.0;
|
||
video.play().then(() => {
|
||
playBtn.style.display = "none"; // hide button once playing
|
||
}).catch(err => {
|
||
console.warn("Video play failed:", err);
|
||
});
|
||
});
|
||
|
||
// --- Teaser audio logic ---
|
||
const btnForAudio = new Map();
|
||
function anyTeaserPlaying() {
|
||
return Array.from(btnForAudio.keys()).some(a => !a.paused && !a.ended);
|
||
}
|
||
|
||
document.querySelectorAll("[data-audio]").forEach(btn => {
|
||
const aud = document.querySelector(btn.getAttribute("data-audio"));
|
||
if (!aud) return;
|
||
btn.dataset.orig = btn.innerHTML;
|
||
btnForAudio.set(aud, btn);
|
||
|
||
aud.addEventListener("ended", () => {
|
||
btn.innerHTML = btn.dataset.orig;
|
||
if (!anyTeaserPlaying()) {
|
||
video.play().catch(() => {});
|
||
}
|
||
});
|
||
});
|
||
|
||
document.addEventListener("click", e => {
|
||
const btn = e.target.closest("[data-audio]");
|
||
if (!btn) return;
|
||
const aud = document.querySelector(btn.getAttribute("data-audio"));
|
||
if (!aud) return;
|
||
|
||
// Stop others
|
||
btnForAudio.forEach((b, a) => {
|
||
if (a !== aud) {
|
||
a.pause();
|
||
a.currentTime = 0;
|
||
b.innerHTML = b.dataset.orig;
|
||
}
|
||
});
|
||
|
||
if (aud.paused) {
|
||
video.pause();
|
||
aud.currentTime = 0;
|
||
aud.play().then(() => {
|
||
btn.innerHTML = '<i class="fad fa-pause"></i> Pause';
|
||
});
|
||
} else {
|
||
aud.pause();
|
||
btn.innerHTML = btn.dataset.orig;
|
||
if (!anyTeaserPlaying()) {
|
||
video.play().catch(() => {});
|
||
}
|
||
}
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<script>
|
||
let selectedCountry = null;
|
||
|
||
window.addEventListener("click", function (e) {
|
||
const flag = e.target.closest('.flag-btn');
|
||
if (!flag) return;
|
||
|
||
selectedCountry = flag.getAttribute("data-country") || "Your country";
|
||
const key = "taf_support_" + selectedCountry;
|
||
|
||
if (!localStorage.getItem(key)) {
|
||
localStorage.setItem(key, "1");
|
||
}
|
||
|
||
// Send click to server and update count
|
||
fetch("/api/support/flag", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ country: selectedCountry })
|
||
})
|
||
.then(res => res.json())
|
||
.then(data => {
|
||
if (data.ok) {
|
||
// Update the flag's count
|
||
const countEl = flag.querySelector(".flag-count");
|
||
if (countEl) countEl.textContent = `(${data.total})`;
|
||
}
|
||
});
|
||
|
||
// Show thank-you + form
|
||
document.getElementById("flagMessage").textContent = `Thanks for the love, ${selectedCountry}!`;
|
||
document.getElementById("flagToast").style.display = "block";
|
||
document.getElementById("releaseForm").style.display = "block";
|
||
|
||
// Tap animation
|
||
if (flag.animate) {
|
||
flag.animate(
|
||
[{ transform: 'scale(1)' }, { transform: 'scale(1.06)' }, { transform: 'scale(1)' }],
|
||
{ duration: 260, easing: 'ease-out' }
|
||
);
|
||
}
|
||
});
|
||
|
||
document.addEventListener("submit", function (e) {
|
||
if (e.target && e.target.id === "notifyForm") {
|
||
e.preventDefault();
|
||
const emailInput = document.getElementById("notifyEmail");
|
||
if (!emailInput.value) return;
|
||
|
||
fetch("/api/support/subscribe", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({
|
||
country: selectedCountry,
|
||
name: document.getElementById("notifyName").value || null,
|
||
email: document.getElementById("notifyEmail").value
|
||
})
|
||
});
|
||
|
||
|
||
document.getElementById("releaseForm").innerHTML = "<p>Thanks! We'll email you when the book is released.</p>";
|
||
}
|
||
});
|
||
</script>
|
||
|
||
@if (DateTime.Now < new DateTime(2025, 8, 21))
|
||
{
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
function initCountdown(el) {
|
||
if (!el) return;
|
||
var iso = el.getAttribute('data-release');
|
||
var ts = Date.parse(iso);
|
||
if (isNaN(ts)) return;
|
||
|
||
var dEl = el.querySelector('.rcd.d .num');
|
||
var hEl = el.querySelector('.rcd.h .num');
|
||
var mEl = el.querySelector('.rcd.m .num');
|
||
var sEl = el.querySelector('.rcd.s .num');
|
||
var outText = el.getAttribute('data-out-text') || 'Out now';
|
||
|
||
function pad(n) { return n < 10 ? '0' + n : '' + n; }
|
||
|
||
function tick() {
|
||
var now = Date.now();
|
||
var diff = ts - now;
|
||
|
||
if (diff <= 0) {
|
||
el.textContent = outText;
|
||
clearInterval(timer);
|
||
return;
|
||
}
|
||
|
||
var secs = Math.floor(diff / 1000);
|
||
var days = Math.floor(secs / 86400); secs -= days * 86400;
|
||
var hrs = Math.floor(secs / 3600); secs -= hrs * 3600;
|
||
var mins = Math.floor(secs / 60); secs -= mins * 60;
|
||
|
||
if (dEl) dEl.textContent = days;
|
||
if (hEl) hEl.textContent = pad(hrs);
|
||
if (mEl) mEl.textContent = pad(mins);
|
||
if (sEl) sEl.textContent = pad(secs);
|
||
}
|
||
|
||
tick();
|
||
var timer = setInterval(tick, 1000);
|
||
}
|
||
|
||
// Support multiple countdowns on a page
|
||
var timers = document.querySelectorAll('.release-countdown');
|
||
for (var i = 0; i < timers.length; i++) {
|
||
initCountdown(timers[i]);
|
||
}
|
||
});
|
||
</script>
|
||
}
|
||
|
||
|
||
}
|