...cleanup

This commit is contained in:
2026-06-17 23:51:28 -04:00
parent c6b5f8b205
commit ee204dde46
27 changed files with 65978 additions and 45921 deletions
+14 -8
View File
@@ -53,10 +53,12 @@ foreach (var file in mdFiles)
Set = StripWikiLink(yaml.GetValueOrDefault("set")), Set = StripWikiLink(yaml.GetValueOrDefault("set")),
Speed = StripWikiLink(yaml.GetValueOrDefault("speed")), Speed = StripWikiLink(yaml.GetValueOrDefault("speed")),
Archetypes = ParseList(yaml, "archetypes").Select(s => StripWikiLink(s) ?? "").Where(s => s != "").ToList(), Archetypes = ParseList(yaml, "archetypes").Select(s => StripWikiLink(s) ?? "").Where(s => s != "").ToList(),
ImmortalizeTo = yaml.ContainsKey("immortalizeTo") ? ParseListOrScalar(yaml, "immortalizeTo").Select(s => StripWikiLink(s) ?? "").Where(s => s != "").ToList() : null, ImmortalizeTo = yaml.ContainsKey("immortalizeTo")
? ParseListOrScalar(yaml, "immortalizeTo").Select(s => StripWikiLink(s) ?? "").Where(s => s != "").ToList()
: null,
ImmortalizeFrom = StripWikiLink(yaml.GetValueOrDefault("immortalizeFrom")), ImmortalizeFrom = StripWikiLink(yaml.GetValueOrDefault("immortalizeFrom")),
ImmortalizeWhen = NullIfNa(StripWikiLinks(yaml.GetValueOrDefault("immortalizeWhen"))), ImmortalizeWhen = NullIfNa(StripWikiLinks(yaml.GetValueOrDefault("immortalizeWhen"))),
ImageFile = imageFile, ImageFile = imageFile
}; };
cards.Add(card); cards.Add(card);
@@ -71,7 +73,7 @@ foreach (var card in cards)
var src = Path.Combine(docsDir, card.ImageFile); var src = Path.Combine(docsDir, card.ImageFile);
var dst = Path.Combine(cardsDir, card.ImageFile); var dst = Path.Combine(cardsDir, card.ImageFile);
if (File.Exists(src)) if (File.Exists(src))
File.Copy(src, dst, overwrite: true); File.Copy(src, dst, true);
} }
// Generate C# source file // Generate C# source file
@@ -87,7 +89,7 @@ writer.WriteLine("{");
writer.WriteLine(" public static readonly System.Collections.Generic.List<CardData> Cards ="); writer.WriteLine(" public static readonly System.Collections.Generic.List<CardData> Cards =");
writer.WriteLine(" ["); writer.WriteLine(" [");
for (int i = 0; i < cards.Count; i++) for (var i = 0; i < cards.Count; i++)
{ {
var c = cards[i]; var c = cards[i];
writer.WriteLine(" new()"); writer.WriteLine(" new()");
@@ -138,7 +140,10 @@ static string? StripWikiLinks(string? s)
return Regex.Replace(s.Trim('"'), @"\[\[([^\]]*)\]\]", "$1").Trim(); return Regex.Replace(s.Trim('"'), @"\[\[([^\]]*)\]\]", "$1").Trim();
} }
static string? NullIfNa(string? s) => s is "N/A" or null ? null : s.Trim('"'); static string? NullIfNa(string? s)
{
return s is "N/A" or null ? null : s.Trim('"');
}
static List<string> ParseList(Dictionary<string, string> yaml, string key) static List<string> ParseList(Dictionary<string, string> yaml, string key)
{ {
@@ -149,6 +154,7 @@ static List<string> ParseList(Dictionary<string, string> yaml, string key)
var trimmed = item.TrimStart('-', ' ').Trim(' ', '"'); var trimmed = item.TrimStart('-', ' ').Trim(' ', '"');
if (trimmed.Length > 0) result.Add(trimmed); if (trimmed.Length > 0) result.Add(trimmed);
} }
return result; return result;
} }
@@ -165,6 +171,7 @@ static List<string> ParseListOrScalar(Dictionary<string, string> yaml, string ke
var trimmed = item.TrimStart('-', ' ').Trim(' ', '"'); var trimmed = item.TrimStart('-', ' ').Trim(' ', '"');
if (trimmed.Length > 0) result.Add(trimmed); if (trimmed.Length > 0) result.Add(trimmed);
} }
return result; return result;
} }
@@ -240,8 +247,7 @@ static string ToLiteral(string? s)
if (s == null) return "null"; if (s == null) return "null";
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.Append('"'); sb.Append('"');
foreach (char c in s) foreach (var c in s)
{
switch (c) switch (c)
{ {
case '"': sb.Append("\\\""); break; case '"': sb.Append("\\\""); break;
@@ -252,7 +258,7 @@ static string ToLiteral(string? s)
case '\0': sb.Append("\\0"); break; case '\0': sb.Append("\\0"); break;
default: sb.Append(c); break; default: sb.Append(c); break;
} }
}
sb.Append('"'); sb.Append('"');
return sb.ToString(); return sb.ToString();
} }
+2 -1
View File
@@ -1,4 +1,5 @@
<Router AppAssembly="@typeof(App).Assembly" NotFoundPage="typeof(Pages.NotFound)"> @using Web.Pages
<Router AppAssembly="@typeof(App).Assembly" NotFoundPage="typeof(NotFound)">
<Found Context="routeData"> <Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
<FocusOnNavigate RouteData="@routeData" Selector="h1"/> <FocusOnNavigate RouteData="@routeData" Selector="h1"/>
+3 -2
View File
@@ -1,5 +1,4 @@
@page "/agents" @page "/agents"
@using Chrono.Model
<PageTitle>Agents</PageTitle> <PageTitle>Agents</PageTitle>
@@ -29,7 +28,8 @@
<GridColumn Field="@nameof(CardData.Name)" Title="Card" Width="220px"> <GridColumn Field="@nameof(CardData.Name)" Title="Card" Width="220px">
<Template> <Template>
<div class="agent-name-cell"> <div class="agent-name-cell">
<img src="@(((CardData)context).ImagePath)" alt="@(((CardData)context).Name)" class="agent-thumb"/> <img src="@(((CardData)context).ImagePath)" alt="@(((CardData)context).Name)"
class="agent-thumb"/>
<span>@(((CardData)context).Name)</span> <span>@(((CardData)context).Name)</span>
</div> </div>
</Template> </Template>
@@ -55,4 +55,5 @@
.ThenBy(c => c.Name) .ThenBy(c => c.Name)
.ToList(); .ToList();
} }
} }
+42 -13
View File
@@ -1,5 +1,4 @@
@page "/cards" @page "/cards"
@using Chrono.Model
<PageTitle>Card Gallery</PageTitle> <PageTitle>Card Gallery</PageTitle>
@@ -27,7 +26,8 @@
<div class="filter-bar"> <div class="filter-bar">
<div class="search-wrapper"> <div class="search-wrapper">
<i class="bi bi-search search-icon"></i> <i class="bi bi-search search-icon"></i>
<input @bind="search" @bind:event="oninput" class="form-control search-input" placeholder="Search cards by name or description..." /> <input @bind="search" @bind:event="oninput" class="form-control search-input"
placeholder="Search cards by name or description..."/>
@if (search.Length > 0) @if (search.Length > 0)
{ {
<button class="search-clear" @onclick="ClearSearch"><i class="bi bi-x-lg"></i></button> <button class="search-clear" @onclick="ClearSearch"><i class="bi bi-x-lg"></i></button>
@@ -42,7 +42,7 @@
</select> </select>
<select @bind="costFilter" class="form-select filter-select"> <select @bind="costFilter" class="form-select filter-select">
<option value="">All Costs</option> <option value="">All Costs</option>
@for (int i = 0; i <= 12; i++) @for (var i = 0; i <= 12; i++)
{ {
<option value="@i">@i</option> <option value="@i">@i</option>
} }
@@ -89,7 +89,7 @@
@if (filteredCards.Any()) @if (filteredCards.Any())
{ {
<div class="card-grid"> <div class="card-grid">
@{ int idx = 0; } @{ var idx = 0; }
@foreach (var card in filteredCards) @foreach (var card in filteredCards)
{ {
<div class="card-cell @(selectedCard == card ? "selected" : "")" <div class="card-cell @(selectedCard == card ? "selected" : "")"
@@ -105,7 +105,8 @@
} }
@if (card.HasImmortalize) @if (card.HasImmortalize)
{ {
<div class="card-immortalize-badge" title="Immortalizes"><i class="bi bi-star-fill"></i></div> <div class="card-immortalize-badge" title="Immortalizes"><i class="bi bi-star-fill"></i>
</div>
} }
</div> </div>
<div class="card-label"> <div class="card-label">
@@ -140,7 +141,8 @@
<div class="detail-header"> <div class="detail-header">
<h2>@selectedCard.Name</h2> <h2>@selectedCard.Name</h2>
<div class="detail-meta"> <div class="detail-meta">
<span class="meta-badge category @selectedCard.Category?.ToLowerInvariant()">@selectedCard.Category</span> <span
class="meta-badge category @selectedCard.Category?.ToLowerInvariant()">@selectedCard.Category</span>
@if (selectedCard.Cost.HasValue) @if (selectedCard.Cost.HasValue)
{ {
<span class="meta-badge cost"><i class="bi bi-lightning-fill"></i> @selectedCard.Cost</span> <span class="meta-badge cost"><i class="bi bi-lightning-fill"></i> @selectedCard.Cost</span>
@@ -249,14 +251,40 @@
); );
} }
private void SetCategory(string cat) => categoryFilter = categoryFilter == cat ? "" : cat; private void SetCategory(string cat)
private void ClearCategoryFilter() => categoryFilter = ""; {
private void ClearFactionFilter() => factionFilter = ""; categoryFilter = categoryFilter == cat ? "" : cat;
private void ClearCostFilter() => costFilter = ""; }
private void ClearSearch() => search = "";
private void SelectCard(CardData card) => selectedCard = card; private void ClearCategoryFilter()
private void CloseDetail() => selectedCard = null; {
categoryFilter = "";
}
private void ClearFactionFilter()
{
factionFilter = "";
}
private void ClearCostFilter()
{
costFilter = "";
}
private void ClearSearch()
{
search = "";
}
private void SelectCard(CardData card)
{
selectedCard = card;
}
private void CloseDetail()
{
selectedCard = null;
}
private void ClearFilters() private void ClearFilters()
{ {
@@ -265,4 +293,5 @@
factionFilter = ""; factionFilter = "";
costFilter = ""; costFilter = "";
} }
} }
+14 -5
View File
@@ -269,8 +269,12 @@
} }
@keyframes shimmer { @keyframes shimmer {
0% { background-position: -200% 0; } 0% {
100% { background-position: 200% 0; } background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
} }
.card-image-wrapper img { .card-image-wrapper img {
@@ -404,8 +408,12 @@
} }
@keyframes fade-in { @keyframes fade-in {
from { opacity: 0; } from {
to { opacity: 1; } opacity: 0;
}
to {
opacity: 1;
}
} }
.card-detail { .card-detail {
@@ -511,7 +519,8 @@
border: 1px solid var(--border); border: 1px solid var(--border);
} }
.meta-badge.category { } .meta-badge.category {
}
.meta-badge.category.agent { .meta-badge.category.agent {
background: rgba(79, 195, 247, 0.15); background: rgba(79, 195, 247, 0.15);
-1
View File
@@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Telerik.Blazor;
using Web; using Web;
var builder = WebAssemblyHostBuilder.CreateDefault(args); var builder = WebAssemblyHostBuilder.CreateDefault(args);
+19 -5
View File
@@ -22,10 +22,22 @@
scrollbar-color: #2a2a4a transparent; scrollbar-color: #2a2a4a transparent;
} }
::-webkit-scrollbar { width: 5px; } ::-webkit-scrollbar {
::-webkit-scrollbar-track { background: transparent; } width: 5px;
::-webkit-scrollbar-thumb { background: #2a2a4a; border-radius: 3px; } }
::-webkit-scrollbar-thumb:hover { background: #3a3a5a; }
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: #2a2a4a;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #3a3a5a;
}
html, body { html, body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
@@ -47,7 +59,9 @@ h1 {
background-clip: text; background-clip: text;
} }
a, .btn-link { color: var(--accent); } a, .btn-link {
color: var(--accent);
}
.btn-primary { .btn-primary {
background: var(--accent); background: var(--accent);
+12 -12
View File
@@ -3,18 +3,18 @@
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>Web</title> <title>Web</title>
<base href="/"/> <base href="/"/>
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link href="https://fonts.googleapis.com" rel="preconnect"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link crossorigin href="https://fonts.gstatic.com" rel="preconnect"/>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"/> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" /> <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet"/>
<link rel="preload" id="webassembly" /> <link id="webassembly" rel="preload"/>
<link rel="stylesheet" href="lib/bootstrap/dist/css/bootstrap.min.css" /> <link href="lib/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet"/>
<link rel="stylesheet" href="css/app.css" /> <link href="css/app.css" rel="stylesheet"/>
<link rel="stylesheet" href="_content/Telerik.UI.for.Blazor/css/kendo-theme-default/all.css" /> <link href="_content/Telerik.UI.for.Blazor/css/kendo-theme-default/all.css" rel="stylesheet"/>
<link rel="icon" type="image/png" href="favicon.png" /> <link href="favicon.png" rel="icon" type="image/png"/>
<link href="Web.styles.css" rel="stylesheet"/> <link href="Web.styles.css" rel="stylesheet"/>
<script type="importmap"></script> <script type="importmap"></script>
</head> </head>
@@ -22,15 +22,15 @@
<body> <body>
<div id="app"> <div id="app">
<svg class="loading-progress"> <svg class="loading-progress">
<circle r="40%" cx="50%" cy="50%" /> <circle cx="50%" cy="50%" r="40%"/>
<circle r="40%" cx="50%" cy="50%" /> <circle cx="50%" cy="50%" r="40%"/>
</svg> </svg>
<div class="loading-progress-text"></div> <div class="loading-progress-text"></div>
</div> </div>
<div id="blazor-error-ui"> <div id="blazor-error-ui">
An unhandled error has occurred. An unhandled error has occurred.
<a href="." class="reload">Reload</a> <a class="reload" href=".">Reload</a>
<span class="dismiss">🗙</span> <span class="dismiss">🗙</span>
</div> </div>
<script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script> <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -224,6 +224,7 @@ h6, h5, h4, h3, h2, h1 {
h1 { h1 {
font-size: calc(1.375rem + 1.5vw); font-size: calc(1.375rem + 1.5vw);
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
h1 { h1 {
font-size: 2.5rem; font-size: 2.5rem;
@@ -233,6 +234,7 @@ h1 {
h2 { h2 {
font-size: calc(1.325rem + 0.9vw); font-size: calc(1.325rem + 0.9vw);
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
h2 { h2 {
font-size: 2rem; font-size: 2rem;
@@ -242,6 +244,7 @@ h2 {
h3 { h3 {
font-size: calc(1.3rem + 0.6vw); font-size: calc(1.3rem + 0.6vw);
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
h3 { h3 {
font-size: 1.75rem; font-size: 1.75rem;
@@ -251,6 +254,7 @@ h3 {
h4 { h4 {
font-size: calc(1.275rem + 0.3vw); font-size: calc(1.275rem + 0.3vw);
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
h4 { h4 {
font-size: 1.5rem; font-size: 1.5rem;
@@ -351,6 +355,7 @@ a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
text-decoration: underline; text-decoration: underline;
} }
a:hover { a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb); --bs-link-color-rgb: var(--bs-link-hover-color-rgb);
} }
@@ -375,6 +380,7 @@ pre {
overflow: auto; overflow: auto;
font-size: 0.875em; font-size: 0.875em;
} }
pre code { pre code {
font-size: inherit; font-size: inherit;
color: inherit; color: inherit;
@@ -386,6 +392,7 @@ code {
color: var(--bs-code-color); color: var(--bs-code-color);
word-wrap: break-word; word-wrap: break-word;
} }
a > code { a > code {
color: inherit; color: inherit;
} }
@@ -397,6 +404,7 @@ kbd {
background-color: var(--bs-body-color); background-color: var(--bs-body-color);
border-radius: 0.25rem; border-radius: 0.25rem;
} }
kbd kbd { kbd kbd {
padding: 0; padding: 0;
font-size: 1em; font-size: 1em;
@@ -474,6 +482,7 @@ select {
select { select {
word-wrap: normal; word-wrap: normal;
} }
select:disabled { select:disabled {
opacity: 1; opacity: 1;
} }
@@ -488,6 +497,7 @@ button,
[type=submit] { [type=submit] {
-webkit-appearance: button; -webkit-appearance: button;
} }
button:not(:disabled), button:not(:disabled),
[type=button]:not(:disabled), [type=button]:not(:disabled),
[type=reset]:not(:disabled), [type=reset]:not(:disabled),
@@ -519,11 +529,13 @@ legend {
font-size: calc(1.275rem + 0.3vw); font-size: calc(1.275rem + 0.3vw);
line-height: inherit; line-height: inherit;
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
legend { legend {
font-size: 1.5rem; font-size: 1.5rem;
} }
} }
legend + * { legend + * {
clear: left; clear: left;
} }
@@ -224,6 +224,7 @@ h6, h5, h4, h3, h2, h1 {
h1 { h1 {
font-size: calc(1.375rem + 1.5vw); font-size: calc(1.375rem + 1.5vw);
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
h1 { h1 {
font-size: 2.5rem; font-size: 2.5rem;
@@ -233,6 +234,7 @@ h1 {
h2 { h2 {
font-size: calc(1.325rem + 0.9vw); font-size: calc(1.325rem + 0.9vw);
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
h2 { h2 {
font-size: 2rem; font-size: 2rem;
@@ -242,6 +244,7 @@ h2 {
h3 { h3 {
font-size: calc(1.3rem + 0.6vw); font-size: calc(1.3rem + 0.6vw);
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
h3 { h3 {
font-size: 1.75rem; font-size: 1.75rem;
@@ -251,6 +254,7 @@ h3 {
h4 { h4 {
font-size: calc(1.275rem + 0.3vw); font-size: calc(1.275rem + 0.3vw);
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
h4 { h4 {
font-size: 1.5rem; font-size: 1.5rem;
@@ -351,6 +355,7 @@ a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
text-decoration: underline; text-decoration: underline;
} }
a:hover { a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb); --bs-link-color-rgb: var(--bs-link-hover-color-rgb);
} }
@@ -375,6 +380,7 @@ pre {
overflow: auto; overflow: auto;
font-size: 0.875em; font-size: 0.875em;
} }
pre code { pre code {
font-size: inherit; font-size: inherit;
color: inherit; color: inherit;
@@ -386,6 +392,7 @@ code {
color: var(--bs-code-color); color: var(--bs-code-color);
word-wrap: break-word; word-wrap: break-word;
} }
a > code { a > code {
color: inherit; color: inherit;
} }
@@ -397,6 +404,7 @@ kbd {
background-color: var(--bs-body-color); background-color: var(--bs-body-color);
border-radius: 0.25rem; border-radius: 0.25rem;
} }
kbd kbd { kbd kbd {
padding: 0; padding: 0;
font-size: 1em; font-size: 1em;
@@ -474,6 +482,7 @@ select {
select { select {
word-wrap: normal; word-wrap: normal;
} }
select:disabled { select:disabled {
opacity: 1; opacity: 1;
} }
@@ -488,6 +497,7 @@ button,
[type=submit] { [type=submit] {
-webkit-appearance: button; -webkit-appearance: button;
} }
button:not(:disabled), button:not(:disabled),
[type=button]:not(:disabled), [type=button]:not(:disabled),
[type=reset]:not(:disabled), [type=reset]:not(:disabled),
@@ -519,11 +529,13 @@ legend {
font-size: calc(1.275rem + 0.3vw); font-size: calc(1.275rem + 0.3vw);
line-height: inherit; line-height: inherit;
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
legend { legend {
font-size: 1.5rem; font-size: 1.5rem;
} }
} }
legend + * { legend + * {
clear: right; clear: right;
} }
@@ -553,6 +565,7 @@ legend + * {
[type="number"] { [type="number"] {
direction: ltr; direction: ltr;
} }
::-webkit-search-decoration { ::-webkit-search-decoration {
-webkit-appearance: none; -webkit-appearance: none;
} }
@@ -591,4 +604,5 @@ progress {
[hidden] { [hidden] {
display: none !important; display: none !important;
} }
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */ /*# sourceMappingURL=bootstrap-reboot.rtl.css.map */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+1
View File
@@ -18,6 +18,7 @@ cards:
- "[[Holder of the Instruments]]" - "[[Holder of the Instruments]]"
divers: divers:
--- ---
First game, lost to Sungrace Splintergleam. Had slow lethal, but they had burn. First game, lost to Sungrace Splintergleam. Had slow lethal, but they had burn.
My hand had lots of cards. My hand had lots of cards.
+1
View File
@@ -14,4 +14,5 @@ archetypes:
- N/A - N/A
imageLink: "[[Master of Ceremonies.png]]" imageLink: "[[Master of Ceremonies.png]]"
--- ---
![[Master of Ceremonies.png]] ![[Master of Ceremonies.png]]