This commit is contained in:
Nick 2025-07-03 23:04:57 +01:00
parent 74bdb093c9
commit 27cfdd8f6d
133 changed files with 505 additions and 222 deletions

View File

@ -3,11 +3,14 @@ using CatherineLynwood.Models;
using CatherineLynwood.Services; using CatherineLynwood.Services;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Client;
using Newtonsoft.Json; using Newtonsoft.Json;
using SendGrid.Helpers.Mail; using SendGrid.Helpers.Mail;
using System.Text.RegularExpressions;
namespace CatherineLynwood.Controllers namespace CatherineLynwood.Controllers
{ {
[Route("the-alpha-flame")] [Route("the-alpha-flame")]
@ -36,7 +39,6 @@ namespace CatherineLynwood.Controllers
return View(); return View();
} }
[Route("blog")] [Route("blog")]
public async Task<IActionResult> Blog(BlogFilter blogFilter) public async Task<IActionResult> Blog(BlogFilter blogFilter)
{ {
@ -93,6 +95,9 @@ namespace CatherineLynwood.Controllers
blog.ShowThanks = showThanks; blog.ShowThanks = showThanks;
// Generate JSON-LD
blog.SchemaJsonLd = GenerateBlogSchemaJsonLd(blog);
if (blog.Template == "slideshow") if (blog.Template == "slideshow")
{ {
return View("SlideShowTemplate", blog); return View("SlideShowTemplate", blog);
@ -101,8 +106,6 @@ namespace CatherineLynwood.Controllers
return View("DefaultTemplate", blog); return View("DefaultTemplate", blog);
} }
[Route("characters")] [Route("characters")]
public IActionResult Characters() public IActionResult Characters()
{ {
@ -164,6 +167,12 @@ namespace CatherineLynwood.Controllers
return View(); return View();
} }
[Route("giveaways")]
public IActionResult Giveaways()
{
return View();
}
[Route("characters/maggie-grant")] [Route("characters/maggie-grant")]
public IActionResult Maggie() public IActionResult Maggie()
{ {
@ -218,13 +227,74 @@ namespace CatherineLynwood.Controllers
return View(); return View();
} }
#endregion Public Methods #endregion Public Methods
#region Private Methods #region Private Methods
private string GenerateBlogSchemaJsonLd(Blog blog)
{
// Strip HTML from content
string StripHtml(string input)
=> string.IsNullOrWhiteSpace(input) ? string.Empty : Regex.Replace(input, "<.*?>", string.Empty);
string contentTopPlainText = StripHtml(blog.ContentTop);
string contentBottomPlainText = StripHtml(blog.ContentBottom);
string blogUrl = $"https://www.catherinelynwood.com/{blog.BlogUrl}";
// Build the schema object
var schema = new Dictionary<string, object>
{
["@context"] = "https://schema.org",
["@type"] = "BlogPosting",
["headline"] = blog.Title,
["datePublished"] = blog.PublishDate.ToString("yyyy-MM-dd"),
["author"] = new Dictionary<string, object>
{
["@type"] = "Person",
["name"] = "Catherine Lynwood"
},
["description"] = blog.IndexText,
["articleBody"] = $"{contentTopPlainText} {contentBottomPlainText}",
["url"] = blogUrl,
["interactionStatistic"] = new Dictionary<string, object>
{
["@type"] = "InteractionCounter",
["interactionType"] = new Dictionary<string, object>
{
["@type"] = "http://schema.org/LikeAction"
},
["userInteractionCount"] = blog.Likes
}
};
if (!string.IsNullOrWhiteSpace(blog.ImageUrl))
{
schema["image"] = new Dictionary<string, object>
{
["@type"] = "ImageObject",
["url"] = $"https://www.catherinelynwood.com/images/webp/{blog.DefaultWebpImage}",
["description"] = blog.ImageDescription
};
}
if (!string.IsNullOrWhiteSpace(blog.VideoUrl))
{
schema["video"] = new Dictionary<string, object>
{
["@type"] = "VideoObject",
["name"] = $"{blog.Title} Video Teaser",
["description"] = $"Atmospheric teaser video for {blog.Title}.",
["thumbnailUrl"] = $"https://www.catherinelynwood.com/images/webp/{blog.DefaultWebpImage}",
["uploadDate"] = blog.PublishDate.ToString("yyyy-MM-dd"),
["contentUrl"] = blog.VideoUrl,
["embedUrl"] = blog.VideoUrl
};
}
return JsonConvert.SerializeObject(schema, Formatting.Indented);
}
private bool IsMobile(HttpRequest request) private bool IsMobile(HttpRequest request)
{ {
string userAgent = request.Headers["User-Agent"].ToString().ToLower(); string userAgent = request.Headers["User-Agent"].ToString().ToLower();

View File

@ -11,10 +11,10 @@ namespace CatherineLynwood.Middleware
private static readonly string[] ProtectedPaths = new[] private static readonly string[] ProtectedPaths = new[]
{ {
"/ask-a-question", "/ask-a-question",
"/contact-catherine", "/contact-catherine",
"/the-alpha-flame/blog/" "/the-alpha-flame/blog/"
}; };
public IpqsBlockMiddleware(RequestDelegate next, IHttpClientFactory httpClientFactory, ILogger<IpqsBlockMiddleware> logger) public IpqsBlockMiddleware(RequestDelegate next, IHttpClientFactory httpClientFactory, ILogger<IpqsBlockMiddleware> logger)
{ {

View File

@ -0,0 +1,29 @@
using CatherineLynwood.Services;
namespace CatherineLynwood.Middleware
{
public class RedirectMiddleware
{
private readonly RequestDelegate _next;
public RedirectMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context, RedirectsStore store)
{
var path = context.Request.Path.Value ?? "";
if (store.TryGetRedirect(path, out var newLocation))
{
context.Response.StatusCode = StatusCodes.Status301MovedPermanently;
context.Response.Headers.Location = newLocation;
return;
}
await _next(context);
}
}
}

View File

@ -50,6 +50,8 @@
public DateTime PublishDate { get; set; } public DateTime PublishDate { get; set; }
public string SchemaJsonLd { get; set; }
public bool ShowThanks { get; set; } public bool ShowThanks { get; set; }
public string SubTitle { get; set; } public string SubTitle { get; set; }

View File

@ -35,6 +35,13 @@ namespace CatherineLynwood
options.Cookie.IsEssential = true; options.Cookie.IsEssential = true;
}); });
// Add RedirectsStore as singleton
builder.Services.AddSingleton<RedirectsStore>(sp =>
{
var logger = sp.GetRequiredService<ILogger<RedirectsStore>>();
return new RedirectsStore("redirects.json", logger);
});
// ✅ Register the book access code service // ✅ Register the book access code service
builder.Services.AddScoped<IAccessCodeService, AccessCodeService>(); builder.Services.AddScoped<IAccessCodeService, AccessCodeService>();
@ -89,6 +96,9 @@ namespace CatherineLynwood
app.UseMiddleware<HoneypotLoggingMiddleware>(); app.UseMiddleware<HoneypotLoggingMiddleware>();
app.UseMiddleware<IpqsBlockMiddleware>(); app.UseMiddleware<IpqsBlockMiddleware>();
app.UseMiddleware<RedirectMiddleware>();
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseResponseCompression(); app.UseResponseCompression();
app.UseStaticFiles(); app.UseStaticFiles();

View File

@ -0,0 +1,86 @@
using System.Collections.Concurrent;
using System.Text.Json;
using System.Collections.Concurrent;
namespace CatherineLynwood.Services
{
public class RedirectsStore : IDisposable
{
private readonly string _filePath;
private readonly ILogger<RedirectsStore> _logger;
private readonly FileSystemWatcher _watcher;
private readonly ConcurrentDictionary<string, string> _redirects = new();
public RedirectsStore(string filePath, ILogger<RedirectsStore> logger)
{
_logger = logger;
// Ensure absolute path
_filePath = Path.GetFullPath(filePath);
// Split into directory and filename
var directory = Path.GetDirectoryName(_filePath);
var filename = Path.GetFileName(_filePath);
if (string.IsNullOrEmpty(directory) || string.IsNullOrEmpty(filename))
{
throw new ArgumentException($"Invalid redirects.json path: {filePath}");
}
_logger.LogInformation("Watching redirects file in directory: {Directory}, file: {File}", directory, filename);
// Load initial data
LoadRedirects();
// Watch for changes
_watcher = new FileSystemWatcher(directory, filename)
{
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size
};
_watcher.Changed += (_, _) =>
{
_logger.LogInformation("Redirects file changed, reloading...");
LoadRedirects();
};
_watcher.EnableRaisingEvents = true;
}
private void LoadRedirects()
{
try
{
var text = File.ReadAllText(_filePath);
var data = JsonSerializer.Deserialize<Dictionary<string, string>>(text) ?? new();
_redirects.Clear();
foreach (var pair in data)
{
var key = pair.Key.TrimEnd('/').ToLowerInvariant();
var value = pair.Value;
_redirects[key] = value;
}
_logger.LogInformation("Loaded {Count} redirect rules", _redirects.Count);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to load redirects from {File}", _filePath);
}
}
public bool TryGetRedirect(string path, out string newPath)
{
var normalised = path.TrimEnd('/').ToLowerInvariant();
return _redirects.TryGetValue(normalised, out newPath);
}
public void Dispose()
{
_watcher.Dispose();
}
}
}

View File

@ -1,5 +1,5 @@
@{ @{
ViewData["Title"] = "The Alpha Flame: Discovery"; ViewData["Title"] = "The Alpha Flame: A Gritty 1980s Birmingham Crime Novel about Twin Sisters";
} }
<div class="row"> <div class="row">
@ -20,7 +20,7 @@
<div class="col-md-4"> <div class="col-md-4">
<section id="book-cover"> <section id="book-cover">
<div class="card character-card" id="cover-card"> <div class="card character-card" id="cover-card">
<responsive-image src="the-alpha-flame-11.png" class="card-img-top" alt="The Alpha Flame Cover" display-width-percentage="50"></responsive-image> <responsive-image src="the-alpha-flame-11.png" class="card-img-top" 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>
<div class="card-body border-top border-3 border-dark"> <div class="card-body border-top border-3 border-dark">
<h3 class="card-title">The Front Cover</h3> <h3 class="card-title">The Front Cover</h3>
<p class="card-text">This is the final front cover of The Alpha Flame: Discovery. It features Maggie stood outside the derelict Rubery Hill Hospital.</p> <p class="card-text">This is the final front cover of The Alpha Flame: Discovery. It features Maggie stood outside the derelict Rubery Hill Hospital.</p>
@ -34,12 +34,13 @@
<section id="synopsis"> <section id="synopsis">
<div class="card character-card" id="synopsis-card"> <div class="card character-card" id="synopsis-card">
<div class="card-header"> <div class="card-header">
<h1>The Alpha Flame: <span class="fw-light">Discovery</span></h1> <h1>The Alpha Flame: <span class="fw-light">Discovery</span><br /><span class="h2">A Gritty 1980s Birmingham Crime Novel</span></h1>
<h2 class="h3">Survival, secrets, and sisters in 1980s Birmingham.</h2>
</div> </div>
<div class="card-body" id="synopsis-body"> <div class="card-body" id="synopsis-body">
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-2"> <div class="col-2">
<responsive-image src="catherine-lynwood-16.png" class="img-fluid rounded-circle" alt="Catherine Lynwood" display-width-percentage="50"></responsive-image> <responsive-image src="catherine-lynwood-16.png" class="img-fluid rounded-circle border border-2 border-dark" alt="Catherine Lynwood" display-width-percentage="100"></responsive-image>
</div> </div>
<div class="col-10"> <div class="col-10">
<!-- Audio Section --> <!-- Audio Section -->
@ -85,7 +86,7 @@
<!-- Synopsis Content --> <!-- Synopsis Content -->
<h3 class="card-title">Synopsis</h3> <h3 class="card-title">Synopsis</h3>
<p class="card-text">The Alpha Flame is an unflinching and deeply emotional story of resilience, love, and survival, set against the vibrant yet tumultuous backdrop of 1983. Through the intersecting lives of two heroines, it delves into the extremes of human suffering and the boundless power of the human spirit to endure, adapt, and rise.</p> <p class="card-text">Set in 1983 Birmingham, The Alpha Flame: Discovery is a gritty crime novel following twin sisters Beth and Maggie as they uncover dark family secrets and fight to survive abuse in a harsh, realistic world. With unflinching honesty, it explores the bonds of family, the scars of the past, and the resilience needed to endure. This powerful first instalment in the trilogy immerses readers in the grim realities of 1980s Britain while celebrating hope in the face of darkness.</p>
<p class="card-text">For Beth, the world is a cold and unforgiving place. Devastation strikes in a single moment, leaving her isolated, shattered, and vulnerable. Alone in the bleak shadows of a city that offers neither refuge nor redemption, she is forced to navigate a relentless cycle of desperation and despair. Every step of her journey tests the limits of her endurance, pushing her into harrowing situations where survival feels like a hollow victory. Beths existence is marked by loss, betrayal, and an almost suffocating loneliness that threatens to consume her entirely. Yet, even in the darkest corners of her ordeal, a fragile ember of defiance smoulders within her, a quiet, stubborn refusal to let the world destroy her completely.</p> <p class="card-text">For Beth, the world is a cold and unforgiving place. Devastation strikes in a single moment, leaving her isolated, shattered, and vulnerable. Alone in the bleak shadows of a city that offers neither refuge nor redemption, she is forced to navigate a relentless cycle of desperation and despair. Every step of her journey tests the limits of her endurance, pushing her into harrowing situations where survival feels like a hollow victory. Beths existence is marked by loss, betrayal, and an almost suffocating loneliness that threatens to consume her entirely. Yet, even in the darkest corners of her ordeal, a fragile ember of defiance smoulders within her, a quiet, stubborn refusal to let the world destroy her completely.</p>
<p class="card-text">Maggie, by contrast, is a force of nature, a woman who thrives on her unshakable drive and an unrelenting belief in her own power. Behind her fiery red hair and disarming charm lies a storm of determination and ferocity. Maggie doesnt just live; she races through life, fuelled by a need for speed and the thrill of freedom. Her Triumph TR6 isnt just a car; its an extension of her spirit, sleek, powerful, and unapologetically bold. On the open road, with the engine roaring and the world blurring past her, she feels invincible. But Maggies intensity doesnt stop at the wheel. Her relationships burn just as brightly. As a lover, she is dominant, passionate, and unafraid to embrace her darker desires. While fiercely loving and loyal, Maggie is also formidable; crossing her isnt a mistake anyone makes twice.</p> <p class="card-text">Maggie, by contrast, is a force of nature, a woman who thrives on her unshakable drive and an unrelenting belief in her own power. Behind her fiery red hair and disarming charm lies a storm of determination and ferocity. Maggie doesnt just live; she races through life, fuelled by a need for speed and the thrill of freedom. Her Triumph TR6 isnt just a car; its an extension of her spirit, sleek, powerful, and unapologetically bold. On the open road, with the engine roaring and the world blurring past her, she feels invincible. But Maggies intensity doesnt stop at the wheel. Her relationships burn just as brightly. As a lover, she is dominant, passionate, and unafraid to embrace her darker desires. While fiercely loving and loyal, Maggie is also formidable; crossing her isnt a mistake anyone makes twice.</p>
<p class="card-text">When fate brings Beth and Maggie together, their connection is explosive, a union of two polar opposites that burns with both tenderness and raw power. For Beth, Maggie represents a lifeline, a reminder that love and trust still exist, even in a world that has betrayed her at every turn. For Maggie, Beth awakens a fierce protectiveness and vulnerability shes rarely allowed herself to feel. Together, they ignite a flame that challenges them to confront their own fears, desires, and limitations.</p> <p class="card-text">When fate brings Beth and Maggie together, their connection is explosive, a union of two polar opposites that burns with both tenderness and raw power. For Beth, Maggie represents a lifeline, a reminder that love and trust still exist, even in a world that has betrayed her at every turn. For Maggie, Beth awakens a fierce protectiveness and vulnerability shes rarely allowed herself to feel. Together, they ignite a flame that challenges them to confront their own fears, desires, and limitations.</p>
@ -211,11 +212,11 @@
@section Meta{ @section Meta{
<MetaTag meta-title="The Alpha Flame: Discovery by Catherine Lynwood" <MetaTag meta-title="The Alpha Flame: Discovery by Catherine Lynwood"
meta-description="Start the journey with 'The Alpha Flame: Discovery' the gripping first novel in Catherine Lynwoods trilogy set in 1983 Birmingham. Family secrets, resilience, and a powerful bond between sisters await." meta-description="A gritty 1980s Birmingham crime novel about twin sisters uncovering dark family secrets and surviving abuse. Realistic, powerful, and unflinching — discover The Alpha Flame today."
meta-keywords="The Alpha Flame Discovery, Catherine Lynwood, 1983 novel, twin sisters, suspense fiction, Rubery, Birmingham fiction, historical drama, family secrets" meta-keywords="The Alpha Flame Discovery, Catherine Lynwood, 1983 novel, twin sisters, suspense fiction, Rubery, Birmingham fiction, historical drama, family secrets"
meta-author="Catherine Lynwood" meta-author="Catherine Lynwood"
meta-url="https://www.catherinelynwood.com/the-alpha-flame/discovery" meta-url="https://www.catherinelynwood.com/the-alpha-flame/discovery"
meta-image="https://www.catherinelynwood.com/images/webp/the-alpha-flame-11-600.webp" meta-image="https://www.catherinelynwood.com/images/webp/the-alpha-flame-11-1200.webp"
meta-image-alt="Maggie from 'The Alpha Flame: Discovery' by Catherine Lynwood" meta-image-alt="Maggie from 'The Alpha Flame: Discovery' by Catherine Lynwood"
og-site-name="Catherine Lynwood - The Alpha Flame: Discovery" og-site-name="Catherine Lynwood - The Alpha Flame: Discovery"
article-published-time="@new DateTime(2024, 11, 20)" article-published-time="@new DateTime(2024, 11, 20)"
@ -226,53 +227,84 @@
<script type="application/ld+json"> <script type="application/ld+json">
{
"@@context": "https://schema.org",
"@@type": "Book",
"name": "The Alpha Flame: Discovery",
"alternateName": "The Alpha Flame Book 1",
"author": {
"@@type": "Person",
"name": "Catherine Lynwood",
"url": "https://www.catherinelynwood.com"
},
"publisher": {
"@@type": "Organization",
"name": "Catherine Lynwood"
},
"datePublished": "2025-08-21",
"description": "The Alpha Flame: Discovery is a powerful, character-driven novel set in 1983 Birmingham, following Maggie Grant and Beth—two young women separated by fate, reunited by truth, and bound by secrets. As past traumas resurface and danger closes in, their journey through survival, sisterhood, and redemption begins.",
"genre": "Women's Fiction, Mystery, Contemporary Historical",
"inLanguage": "en-GB",
"url": "https://www.catherinelynwood.com/the-alpha-flame/discovery",
"workExample": [
{ {
"@@context": "https://schema.org",
"@@type": "Book", "@@type": "Book",
"bookFormat": "https://schema.org/Hardcover", "name": "The Alpha Flame: Discovery",
"isbn": "978-1-0682258-0-2", "alternateName": "The Alpha Flame Book 1",
"name": "The Alpha Flame: Discovery Hardback" "image": "https://www.catherinelynwood.com/images/webp/the-alpha-flame-11-1200.webp",
}, "author": {
{ "@@type": "Person",
"@@type": "Book", "name": "Catherine Lynwood",
"bookFormat": "https://schema.org/Paperback", "url": "https://www.catherinelynwood.com"
"isbn": "978-1-0682258-1-9", },
"name": "The Alpha Flame: Discovery Softback" "publisher": {
}, "@@type": "Organization",
{ "name": "Catherine Lynwood Publishing",
"@@type": "Book", "url": "https://www.catherinelynwood.com"
"bookFormat": "https://schema.org/Paperback", },
"isbn": "978-1-0682258-2-6", "datePublished": "2025-08-21",
"name": "The Alpha Flame: Discovery Amazon Edition" "description": "The Alpha Flame: Discovery is a powerful, character-driven novel set in 1983 Birmingham, following Maggie Grant and Beth—two young women separated by fate, reunited by truth, and bound by secrets. As past traumas resurface and danger closes in, their journey through survival, sisterhood, and redemption begins.",
}, "genre": "Women's Fiction, Mystery, Contemporary Historical",
{ "inLanguage": "en-GB",
"@@type": "Book", "url": "https://www.catherinelynwood.com/the-alpha-flame/discovery",
"bookFormat": "https://schema.org/EBook", "workExample": [
"isbn": "978-1-0682258-3-3", {
"name": "The Alpha Flame: Discovery eBook" "@@type": "Book",
"bookFormat": "https://schema.org/Hardcover",
"isbn": "978-1-0682258-0-2",
"name": "The Alpha Flame: Discovery Hardback",
"url": "https://www.catherinelynwood.com/the-alpha-flame/discovery",
"offers": {
"@@type": "Offer",
"price": 23.99,
"priceCurrency": "GBP",
"availability": "https://schema.org/InStock"
}
},
{
"@@type": "Book",
"bookFormat": "https://schema.org/Paperback",
"isbn": "978-1-0682258-1-9",
"name": "The Alpha Flame: Discovery Paperback (Bookshop Edition)",
"url": "https://www.catherinelynwood.com/the-alpha-flame/discovery",
"offers": {
"@@type": "Offer",
"price": 17.99,
"priceCurrency": "GBP",
"availability": "https://schema.org/InStock"
}
},
{
"@@type": "Book",
"bookFormat": "https://schema.org/Paperback",
"isbn": "978-1-0682258-2-6",
"name": "The Alpha Flame: Discovery Amazon Edition",
"url": "https://www.catherinelynwood.com/the-alpha-flame/discovery",
"offers": {
"@@type": "Offer",
"price": 14.99,
"priceCurrency": "GBP",
"availability": "https://schema.org/InStock"
}
},
{
"@@type": "Book",
"bookFormat": "https://schema.org/EBook",
"isbn": "978-1-0682258-3-3",
"name": "The Alpha Flame: Discovery eBook",
"url": "https://www.catherinelynwood.com/the-alpha-flame/discovery",
"offers": {
"@@type": "Offer",
"price": 3.95,
"priceCurrency": "GBP",
"availability": "https://schema.org/InStock"
}
}
]
} }
]
}
</script> </script>
} }

View File

@ -47,6 +47,10 @@
The journey continues in <strong>Reckoning</strong> (Spring 2026) and concludes with <strong>Redemption</strong> (Autumn 2026). The journey continues in <strong>Reckoning</strong> (Spring 2026) and concludes with <strong>Redemption</strong> (Autumn 2026).
Learn more about the full trilogy on the <a asp-controller="TheAlphaFlame" asp-action="Index" class="link-dark fw-semibold">Alpha Flame series page</a>. Learn more about the full trilogy on the <a asp-controller="TheAlphaFlame" asp-action="Index" class="link-dark fw-semibold">Alpha Flame series page</a>.
</p> </p>
<p class="mt-4">
<h3 class="h4">About The Alpha Flame Trilogy</h3>
Catherine Lynwoods <em>The Alpha Flame trilogy</em> is gripping UK historical fiction set in 1980s Birmingham. These novels combine family drama, dark secrets, and emotional suspense as two sisters fight for truth and redemption. Perfect for readers who enjoy tense, character-driven stories and literary suspense in a richly described real-world setting.
</p>
</div> </div>
</div> </div>

View File

@ -107,11 +107,12 @@
"name": "The Alpha Flame: Discovery (Hardback)", "name": "The Alpha Flame: Discovery (Hardback)",
"productID": "ISBN:978-1-0682258-0-2", "productID": "ISBN:978-1-0682258-0-2",
"description": "Hardback edition of Book One in the Alpha Flame Trilogy.", "description": "Hardback edition of Book One in the Alpha Flame Trilogy.",
"url": "https://www.catherinelynwood.com/the-alpha-flame", "url": "https://www.catherinelynwood.com/the-alpha-flame/discovery",
"offers": { "offers": {
"@@type": "Offer", "@@type": "Offer",
"availability": "https://schema.org/InStock", "availability": "https://schema.org/InStock",
"priceCurrency": "GBP" "priceCurrency": "GBP",
"price": 23.99
} }
}, },
{ {
@ -119,11 +120,12 @@
"name": "The Alpha Flame: Discovery (Paperback)", "name": "The Alpha Flame: Discovery (Paperback)",
"productID": "ISBN:978-1-0682258-1-9", "productID": "ISBN:978-1-0682258-1-9",
"description": "Paperback edition of Book One in the Alpha Flame Trilogy.", "description": "Paperback edition of Book One in the Alpha Flame Trilogy.",
"url": "https://www.catherinelynwood.com/the-alpha-flame", "url": "https://www.catherinelynwood.com/the-alpha-flame/discovery",
"offers": { "offers": {
"@@type": "Offer", "@@type": "Offer",
"availability": "https://schema.org/InStock", "availability": "https://schema.org/InStock",
"priceCurrency": "GBP" "priceCurrency": "GBP",
"price": 17.99
} }
}, },
{ {
@ -131,11 +133,12 @@
"name": "The Alpha Flame: Discovery (Amazon Edition)", "name": "The Alpha Flame: Discovery (Amazon Edition)",
"productID": "ISBN:978-1-0682258-2-6", "productID": "ISBN:978-1-0682258-2-6",
"description": "Amazon-exclusive edition of Book One in the Alpha Flame Trilogy.", "description": "Amazon-exclusive edition of Book One in the Alpha Flame Trilogy.",
"url": "https://www.amazon.co.uk/dp/B0D3T5XYTG", // Replace with your actual Amazon product URL "url": "https://www.catherinelynwood.com/the-alpha-flame/discovery",
"offers": { "offers": {
"@@type": "Offer", "@@type": "Offer",
"availability": "https://schema.org/InStock", "availability": "https://schema.org/InStock",
"priceCurrency": "GBP" "priceCurrency": "GBP",
"price": 14.99
} }
}, },
{ {
@ -143,21 +146,21 @@
"name": "The Alpha Flame: Discovery (eBook)", "name": "The Alpha Flame: Discovery (eBook)",
"productID": "ISBN:978-1-0682258-3-3", "productID": "ISBN:978-1-0682258-3-3",
"description": "eBook edition of Book One in the Alpha Flame Trilogy.", "description": "eBook edition of Book One in the Alpha Flame Trilogy.",
"url": "https://www.catherinelynwood.com/the-alpha-flame", "url": "https://www.catherinelynwood.com/the-alpha-flame/discovery",
"offers": { "offers": {
"@@type": "Offer", "@@type": "Offer",
"availability": "https://schema.org/InStock", "availability": "https://schema.org/InStock",
"priceCurrency": "GBP" "priceCurrency": "GBP",
"price": 3.95
} }
} }
] ]
}, },
"sameAs": [ "sameAs": [
"https://www.catherinelynwood.com", "https://www.catherinelynwood.com/the-alpha-flame/discovery",
"https://www.amazon.co.uk/dp/B0D3T5XYTG" // Confirm or add additional relevant links "https://www.amazon.co.uk/dp/B0D3T5XYTG"
] ]
} }
</script> </script>
} }

View File

@ -64,7 +64,7 @@
@if (Model.ImageFirst) @if (Model.ImageFirst)
{ {
<div class="float-md-start w-md-50 p-3"> <div class="float-md-start w-md-50 p-3 text-center">
@if (string.IsNullOrEmpty(Model.VideoUrl)) @if (string.IsNullOrEmpty(Model.VideoUrl))
{ {
<figure> <figure>
@ -79,13 +79,14 @@
<!-- Source will be injected later --> <!-- Source will be injected later -->
</video> </video>
</div> </div>
@Html.Raw(Model.ImageDescription)
} }
</div> </div>
} }
else else
{ {
<div class="float-md-end w-md-50 p-3"> <div class="float-md-end w-md-50 p-3 text-center">
@if (string.IsNullOrEmpty(Model.VideoUrl)) @if (string.IsNullOrEmpty(Model.VideoUrl))
{ {
<figure> <figure>
@ -100,6 +101,7 @@
<!-- Source will be injected later --> <!-- Source will be injected later -->
</video> </video>
</div> </div>
@Html.Raw(Model.ImageDescription)
} }
</div> </div>
@ -204,88 +206,11 @@
twitter-player-height="@(string.IsNullOrWhiteSpace(Model.VideoUrl) ? null : 80)" /> twitter-player-height="@(string.IsNullOrWhiteSpace(Model.VideoUrl) ? null : 80)" />
@functions {
public static string StripHtml(string input)
{
return string.IsNullOrWhiteSpace(input) ? string.Empty : Regex.Replace(input, "<.*?>", string.Empty);
}
}
@{
string blogUrl = $"https://www.catherinelynwood.com/{Model.BlogUrl}";
string imageJson = string.Empty;
string videoJson = string.Empty;
string description;
if (!string.IsNullOrWhiteSpace(Model.AudioTeaserUrl))
{
description = $"Listen to my new blog entry: {Model.Title} by Catherine Lynwood";
}
else
{
description = $"Read my latest blog post: {Model.Title} by Catherine Lynwood";
}
var contentTopPlainText = StripHtml(Model.ContentTop);
var contentBottomPlainText = StripHtml(Model.ContentBottom);
// Image JSON-LD block if ImageUrl is available
if (!string.IsNullOrWhiteSpace(Model.ImageUrl))
{
string imageUrl = $"https://www.catherinelynwood.com/images/webp/{Model.DefaultWebpImage}";
imageJson = $@",
""image"": {{
""@type"": ""ImageObject"",
""url"": ""{imageUrl}"",
""description"": ""{Model.ImageDescription}""
}}";
}
// Video JSON-LD block if VideoUrl is available
if (!string.IsNullOrWhiteSpace(Model.VideoUrl))
{
// You can add extra properties here if your Blog class supports them in future
videoJson = $@",
""video"": {{
""@type"": ""VideoObject"",
""name"": ""{Model.Title} Video Teaser"",
""description"": ""Atmospheric teaser video for {Model.Title}."",
""thumbnailUrl"": ""https://www.catherinelynwood.com/images/webp/{Model.DefaultWebpImage}"",
""uploadDate"": ""{Model.PublishDate:yyyy-MM-dd}"",
""contentUrl"": ""{Model.VideoUrl}"",
""embedUrl"": ""{Model.VideoUrl}""
}}";
}
// Assemble the final JSON-LD with conditional blocks
var blogPostJsonLd = $@"
{{
""@context"": ""https://schema.org"",
""@type"": ""BlogPosting"",
""headline"": ""{Model.Title}"",
""datePublished"": ""{Model.PublishDate:yyyy-MM-dd}"",
""author"": {{
""@type"": ""Person"",
""name"": ""Catherine Lynwood""
}},
""description"": ""{Model.IndexText}"",
""articleBody"": ""{contentTopPlainText} {contentBottomPlainText}"",
""url"": ""{blogUrl}""{imageJson}{videoJson},
""interactionStatistic"": {{
""@type"": ""InteractionCounter"",
""interactionType"": {{
""@type"": ""http://schema.org/LikeAction""
}},
""userInteractionCount"": {Model.Likes}
}}
}}";
}
<script type="application/ld+json"> <script type="application/ld+json">
@Html.Raw(blogPostJsonLd) @Html.Raw(Model.SchemaJsonLd)
</script> </script>
<style> <style>
ul { ul {
list-style-type: none; list-style-type: none;

View File

@ -0,0 +1,131 @@
@{
ViewData["Title"] = "Giveaways - The Alpha Flame";
}
<section class="py-3 text-center">
<div class="container">
<div class="card bg-dark text-white">
<div class="card-body">
<h1 class="display-4 fw-bold">Win a Collectors Edition</h1>
<p class="lead mb-4">A special chance to own <em>The Alpha Flame: Discovery</em> in a limited edition, signed or delivered to you.</p>
<a href="/newsletter/signup" class="btn btn-primary btn-lg">Enter Now</a>
</div>
</div>
</div>
</section>
<section class="py-3">
<div class="container">
<div class="card shadow-sm">
<div class="card-body">
<h2 class="h4 fw-bold mb-3">How to Enter</h2>
<ul class="list-unstyled">
<li class="mb-2">
<i class="fad fa-check-circle text-success me-2"></i>
Sign up for my newsletter
</li>
<li class="mb-2">
<i class="fad fa-check-circle text-success me-2"></i>
Entries close on <strong>31st August 2025</strong>
</li>
<li class="mb-2">
<i class="fad fa-check-circle text-success me-2"></i>
Open to UK, US, Canada, and Australia
</li>
<li class="mb-2">
<i class="fad fa-check-circle text-success me-2"></i>
Winner announced 7th September 2025 via email and on this page
</li>
</ul>
<p class="mt-3">
<strong>What youll win:</strong>
UK winners receive a signed Collectors Edition mailed by me.
Winners in the US, Canada, or Australia receive an unsigned Collectors Edition printed and shipped locally.
</p>
<p class="small text-muted">No purchase necessary. You can unsubscribe at any time.</p>
</div>
</div>
</div>
</section>
<section class="py-3">
<div class="container">
<div class="card bg-dark text-white">
<div class="card-body">
<h2 class="h4 fw-bold text-center mb-4">The Prize</h2>
<div class="row text-center">
<div class="col-md-4 mb-4">
<responsive-image src="the-alpha-flame-11.png" class="img-fluid rounded-5 border border-3 border-dark shadow-lg" alt="Signed Collectors Edition" display-width-percentage="50"></responsive-image>
<p class="mt-2">Signed by the author (UK only)</p>
</div>
<div class="col-md-4 mb-4">
<responsive-image src="the-alpha-flame-11.png" class="img-fluid rounded-5 border border-3 border-dark shadow-lg" alt="Exclusive cover detail" display-width-percentage="50"></responsive-image>
<p class="mt-2">Exclusive cover design</p>
</div>
<div class="col-md-4 mb-4">
<responsive-image src="the-alpha-flame-11.png" class="img-fluid rounded-5 border border-3 border-dark shadow-lg" alt="Interior preview" display-width-percentage="50"></responsive-image>
<p class="mt-2">Premium print quality</p>
</div>
</div>
</div>
</div>
</div>
</section>
<section class="py-5 text-center">
<div class="container">
<h2 class="h4 fw-bold mb-3">Dont Miss Out</h2>
<p class="mb-4">Sign up today for your chance to win. Plus, get exclusive updates, early previews, and behind-the-scenes content.</p>
<a href="/newsletter/signup" class="btn btn-dark btn-lg">Enter the Giveaway</a>
</div>
</section>
@section Meta{
<MetaTag meta-title="Win a Collectors Edition of The Alpha Flame: Discovery Catherine Lynwood"
meta-description="Enter to win a limited Collectors Edition of The Alpha Flame: Discovery. UK winners receive a signed copy; US, Canada, and Australia winners receive an unsigned copy shipped locally. Entries close 31st August 2025."
meta-keywords="The Alpha Flame giveaway, signed book giveaway, Catherine Lynwood, book contest, limited edition book"
meta-author="Catherine Lynwood"
meta-url="https://www.catherinelynwood.com/the-alpha-flame/giveaways"
meta-image="https://www.catherinelynwood.com/images/alpha-flame-collector-edition.webp"
meta-image-alt="The Alpha Flame: Discovery Collectors Edition Giveaway"
og-site-name="Catherine Lynwood The Alpha Flame"
article-published-time="@new DateTime(2025, 07, 01)" />
<script type="application/ld+json">
{
"@@context": "https://schema.org",
"@@type": "Offer",
"name": "The Alpha Flame: Discovery Collectors Edition Giveaway",
"description": "Enter to win a Collectors Edition of The Alpha Flame: Discovery. UK winners receive a signed copy, while US, Canada, and Australia winners receive an unsigned copy shipped locally. Entries close 31st August 2025.",
"url": "https://www.catherinelynwood.com/the-alpha-flame/giveaways",
"availabilityStarts": "2025-07-01",
"availabilityEnds": "2025-08-31",
"eligibleRegion": [
{
"@@type": "Country",
"name": "United Kingdom"
},
{
"@@type": "Country",
"name": "United States"
},
{
"@@type": "Country",
"name": "Canada"
},
{
"@@type": "Country",
"name": "Australia"
}
],
"seller": {
"@@type": "Person",
"name": "Catherine Lynwood",
"url": "https://www.catherinelynwood.com"
}
}
</script>
}

View File

@ -142,85 +142,61 @@
</div> </div>
</article> </article>
@section Scripts{ @section Scripts {
<script> <script>
const player = new Plyr('audio'); const player = new Plyr('audio');
</script> </script>
@if (!string.IsNullOrEmpty(Model.VideoUrl))
{
<script>
window.addEventListener("load", () => {
setTimeout(() => {
const video = document.getElementById("heroVideo");
const source = document.createElement("source");
source.src = "/videos/@Model.VideoUrl";
source.type = "video/mp4";
video.appendChild(source);
video.load(); // Triggers actual file load
video.play().catch(err => {
console.warn("Autoplay failed:", err);
});
}, 2000); // Adjust delay (e.g., 10003000ms) based on your needs
});
</script>
}
} }
@section Meta { @section Meta {
<meta name="description" content="Explore the blog of Catherine Lynwood, author of *The Alpha Flame*, for updates, insights, and behind-the-scenes stories. Follow Catherines journey as a writer, discover inspiration for her novels, and delve into discussions on womens fiction, mystery, and family secrets."> <MetaTag meta-title="@Model.Title"
<meta name="keywords" content="Catherine Lynwood blog, The Alpha Flame updates, womens fiction blog, author Catherine Lynwood insights, mystery novel blog, family secrets stories, strong female protagonists, 1980s fiction blog, book updates and stories, authors journey blog, Catherine Lynwood writing process"> meta-description="@Model.IndexText"
<meta name="author" content="Catherine Lynwood"> meta-keywords="Catherine Lynwood blog, The Alpha Flame, psychological thriller, indie fiction, 1980s fiction, dark secrets, strong female characters"
meta-author="Catherine Lynwood"
meta-url="https://www.catherinelynwood.com/@Model.BlogUrl"
meta-image="@(string.IsNullOrWhiteSpace(Model.ImageUrl) ? null : $"https://www.catherinelynwood.com/images/webp/{Model.DefaultWebpImage}")"
meta-image-alt="@Model.ImageAlt"
og-site-name="Catherine Lynwood - The Alpha Flame"
article-published-time="@Model.PublishDate"
article-modified-time="@DateTime.UtcNow"
twitter-card-type="@(string.IsNullOrWhiteSpace(Model.VideoUrl) ? "summary_large_image" : "player")"
twitter-site-handle="@@CathLynwood"
twitter-creator-handle="@@CathLynwood"
twitter-video-url="@(string.IsNullOrWhiteSpace(Model.VideoUrl) ? null : Model.VideoUrl)"
twitter-player-width="@(string.IsNullOrWhiteSpace(Model.VideoUrl) ? null : 480)"
twitter-player-height="@(string.IsNullOrWhiteSpace(Model.VideoUrl) ? null : 80)" />
@functions {
public static string StripHtml(string input)
{
return string.IsNullOrWhiteSpace(input) ? string.Empty : Regex.Replace(input, "<.*?>", string.Empty);
}
}
@{
string blogUrl = $"https://www.catherinelynwood.com/{Model.BlogUrl}";
string imageUrl = $"https://www.catherinelynwood.com/images/webp/{Model.DefaultWebpImage}";
string description = $"Listen to my new blog entry: {Model.Title} by Catherine Lynwood";
var contentTopPlainText = StripHtml(Model.ContentTop);
var contentBottomPlainText = StripHtml(Model.ContentBottom);
var blogPostJsonLd = $@"
{{
""@context"": ""https://schema.org"",
""@type"": ""BlogPosting"",
""headline"": ""{Model.Title}"",
""datePublished"": ""{Model.PublishDate:yyyy-MM-dd}"",
""author"": {{
""@type"": ""Person"",
""name"": ""Catherine Lynwood""
}},
""description"": ""{Model.IndexText}"",
""articleBody"": ""{contentTopPlainText} {contentBottomPlainText}"",
""url"": ""https://www.catherinelynwood.com/the-alpah-flame/blog/{Model.BlogUrl}"",
""image"": {{
""@type"": ""ImageObject"",
""url"": ""https://www.catherinelynwood.com/images/webp/{Model.DefaultWebpImage}"",
""description"": ""{Model.ImageDescription}""
}},
""interactionStatistic"": {{
""@type"": ""InteractionCounter"",
""interactionType"": {{
""@type"": ""http://schema.org/LikeAction""
}},
""userInteractionCount"": {Model.Likes}
}}
}}";
}
<meta property="og:title" content="The Alpha Flame by Catherine Lynwood" />
<meta property="og:description" content="@Model.IndexText" />
<meta property="og:type" content="article" />
<meta property="og:url" content="@blogUrl" />
<meta property="og:image" content="@imageUrl" />
<meta property="og:image:alt" content="@Model.ImageAlt" />
<meta property="og:site_name" content="Catherine Lynwood - The Alpha Flame" />
<meta property="article:author" content="https://www.catherinelynwood.com" />
<meta property="article:published_time" content="2024-11-19" />
<meta property="article:modified_time" content="2024-11-19" />
<meta name="twitter:card" content="player">
<meta name="twitter:title" content="@Model.Title" />
<meta name="twitter:description" content="@description">
<meta name="twitter:site" content="@@CathLynwood" />
<meta name="twitter:creator" content="@@CathLynwood" />
<meta name="twitter:image" content="@imageUrl" />
<meta name="twitter:image:alt" content="@Model.ImageAlt" />
<meta name="twitter:player" content="@blogUrl">
<meta name="twitter:player:width" content="480">
<meta name="twitter:player:height" content="80">
<script type="application/ld+json"> <script type="application/ld+json">
@Html.Raw(blogPostJsonLd) @Html.Raw(Model.SchemaJsonLd)
</script> </script>
<style>
ul {
list-style-type: none;
padding-left: 0;
margin-left: 0;
}
</style>
} }

View File

@ -0,0 +1,15 @@
{
"/the-alpha-flame/chapters/chapter-2-maggie": "/the-alpha-flame/discovery/chapters/chapter-2-maggie",
"/the-alpha-flame/chapters/chapter-1-beth": "/the-alpha-flame/discovery/chapters/chapter-1-beth",
"/sisterhood-blog-highlight": "/the-alpha-flame/blog/sisterhood-survival-self-discovery-books",
"/the-alpha-flame/front-cover": "/the-alpha-flame/discovery",
"/the-alpah-flame/blog/1983-birmingham-behind-the-alpha-flame": "/the-alpha-flame/blog/1983-birmingham-behind-the-alpha-flame",
"/the-alpah-flame/blog/sisterhood-survival-self-discovery-books": "/the-alpha-flame/blog/sisterhood-survival-self-discovery-books",
"/the-alpah-flame/blog/who-is-maggie-grant-alpha-flame": "/the-alpha-flame/blog/who-is-maggie-grant-alpha-flame",
"/the-alpah-flame/blog/why-i-wrote-the-alpha-flame": "/the-alpha-flame/blog/why-i-wrote-the-alpha-flame",
"/the-alpah-flame/blog/books-like-verity": "/the-alpha-flame/blog/books-like-verity",
"/TheAlphaFlame/Discovery": "/the-alpha-flame/discovery",
"/the-alpha-flame/meet-the-characters": "/the-alpha-flame/characters",
"/the-alpha-flame/maggie-grant": "/the-alpha-flame/characters/maggie-grant",
"/the-alpha-flame/characters/beth": "/the-alpha-flame/characters/beth-fletcher"
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 985 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 694 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 669 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 KiB

Some files were not shown because too many files have changed in this diff Show More