diff --git a/assets/styles/app.css b/assets/styles/app.css index de3c520..ded7a3f 100644 --- a/assets/styles/app.css +++ b/assets/styles/app.css @@ -133,3 +133,298 @@ table.dataTable { border-color: var(--bs-primary); transform: translateY(-2px); } + +/* --- Storyline: chapter index ----------------------------------------- */ +.storyline-chapter-list { + display: grid; + gap: 0.75rem; + max-width: 60rem; +} +.storyline-chapter-card { + display: flex; + align-items: flex-start; + gap: 1rem; + padding: 1rem 1.25rem; + border: 1px solid var(--bs-border-color); + border-radius: 0.5rem; + background: var(--bs-body-bg); + text-decoration: none; + color: inherit; + transition: border-color 0.12s ease, transform 0.12s ease; +} +.storyline-chapter-card:hover { + border-color: var(--bs-primary); + transform: translateY(-2px); +} +.storyline-chapter-num { + flex: 0 0 auto; + width: 2rem; + height: 2rem; + display: grid; + place-items: center; + border-radius: 50%; + background: var(--bs-tertiary-bg); + border: 1px solid var(--bs-border-color); + font-variant-numeric: tabular-nums; + font-weight: 600; +} +.storyline-chapter-body { + flex: 1 1 auto; + min-width: 0; +} +.storyline-chapter-title { + display: block; + font-size: 1.125rem; + font-weight: 600; +} +.storyline-chapter-summary { + display: block; + color: var(--bs-secondary-color); + font-size: 0.9375rem; + margin-top: 0.15rem; +} +.storyline-chapter-go { + align-self: center; + color: var(--bs-secondary-color); + font-size: 1.25rem; +} + +/* --- Storyline: chapter layout + table of contents ------------------- */ +.storyline-toc { + position: sticky; + top: 1.5rem; + font-size: 0.9375rem; +} +.storyline-toc-link { + display: flex; + gap: 0.5rem; + padding: 0.3rem 0.5rem; + border-radius: 0.375rem; + border-left: 2px solid transparent; + text-decoration: none; + color: var(--bs-secondary-color); +} +.storyline-toc-link:hover { + color: var(--bs-body-color); + background: var(--bs-tertiary-bg); +} +.storyline-toc-link.is-active { + color: var(--bs-body-color); + border-left-color: var(--bs-primary); + background: var(--bs-tertiary-bg); + font-weight: 600; +} +.storyline-toc-num { + font-variant-numeric: tabular-nums; + color: var(--bs-secondary-color); +} + +/* --- Storyline: the document itself --------------------------------- */ +.storyline-doc { + max-width: 46rem; + font-size: 1.05rem; + line-height: 1.7; +} +.storyline-doc-head { + margin-bottom: 2.5rem; + padding-bottom: 1.25rem; + border-bottom: 1px solid var(--bs-border-color); +} +.storyline-eyebrow { + text-transform: uppercase; + letter-spacing: 0.09em; + font-size: 0.8rem; + font-weight: 700; + color: var(--bs-primary); +} +.storyline-doc-head h1 { + margin: 0.35rem 0 0.5rem; + font-size: clamp(1.9rem, 4vw, 2.6rem); + line-height: 1.15; +} +.storyline-standfirst { + font-size: 1.15rem; + color: var(--bs-secondary-color); + font-style: italic; + margin: 0; +} +.storyline-doc p { + margin: 0 0 1.1rem; +} +.storyline-doc u { + text-underline-offset: 0.18em; + text-decoration-thickness: 1px; +} +.storyline-section { + display: flex; + align-items: baseline; + gap: 0.75rem; + margin: 2.75rem 0 1rem; + padding-top: 1.25rem; + border-top: 1px solid var(--bs-border-color); + font-size: 1.45rem; + line-height: 1.25; +} +.storyline-section span { + flex: 0 0 auto; + font-variant-numeric: tabular-nums; + font-size: 0.9rem; + font-weight: 700; + color: var(--bs-primary); + padding-top: 0.35rem; +} +.storyline-lead { + font-size: 1.15rem; +} +.storyline-lead::first-letter { + /* subtle emphasis on the opening line of a section */ + font-weight: 600; +} +.storyline-turn { + font-size: 1.2rem; + font-weight: 600; + text-align: center; + margin: 1.75rem 0 !important; +} + +/* Pull-quotes for in-world spoken lines */ +.storyline-quote { + margin: 1.5rem 0; + padding: 0.6rem 0 0.6rem 1.25rem; + border-left: 3px solid var(--bs-primary); + font-size: 1.2rem; + font-style: italic; + color: var(--bs-body-color); +} +.storyline-quote::before { content: "\201C"; } +.storyline-quote::after { content: "\201D"; } +.storyline-quote--close { + border-left: 0; + text-align: center; + font-size: 1.3rem; + padding: 1.5rem 1rem; + margin-top: 2.5rem; + border-top: 1px solid var(--bs-border-color); +} + +/* GM callouts */ +.storyline-callout { + position: relative; + margin: 1.75rem 0; + padding: 1.1rem 1.25rem 1.1rem 1.25rem; + background: var(--bs-tertiary-bg); + border: 1px solid var(--bs-border-color); + border-left: 3px solid var(--bs-info, #6ea8fe); + border-radius: 0.4rem; +} +.storyline-callout > :last-child { margin-bottom: 0; } +.storyline-callout::before { + content: attr(data-label); + display: block; + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 0.72rem; + font-weight: 700; + color: var(--bs-secondary-color); + margin-bottom: 0.4rem; +} +.storyline-callout--warn { + border-left-color: var(--bs-warning, #ffc107); +} + +/* The escalation ladder in the Final Crisis */ +.storyline-ladder { + margin: 1.25rem 0; + padding-left: 0; + list-style: none; + counter-reset: rung; +} +.storyline-ladder li { + counter-increment: rung; + position: relative; + padding: 0.55rem 0.9rem 0.55rem 2.75rem; + margin-bottom: 0.4rem; + background: var(--bs-tertiary-bg); + border: 1px solid var(--bs-border-color); + border-radius: 0.375rem; + font-style: italic; +} +.storyline-ladder li::before { + content: counter(rung); + position: absolute; + left: 0.9rem; + font-style: normal; + font-weight: 700; + color: var(--bs-primary); +} + +/* Quick-reference panels (numbers + cast) */ +.storyline-ref { + display: grid; + gap: 1rem; + grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); + margin-bottom: 2.5rem; +} +.storyline-ref-panel { + padding: 1.1rem 1.25rem; + background: var(--bs-tertiary-bg); + border: 1px solid var(--bs-border-color); + border-radius: 0.5rem; + font-size: 0.95rem; + line-height: 1.5; +} +.storyline-ref-panel h3 { + font-size: 0.78rem; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--bs-secondary-color); + margin: 0 0 0.75rem; +} +.storyline-ref-panel dl { margin: 0; } +.storyline-ref-panel dl > div { + display: flex; + gap: 0.6rem; + padding: 0.2rem 0; +} +.storyline-ref-panel dt { + flex: 0 0 6.5rem; + font-variant-numeric: tabular-nums; + color: var(--bs-body-color); +} +.storyline-ref-panel dd { + margin: 0; + color: var(--bs-secondary-color); +} +.storyline-ref-panel ul { + margin: 0; + padding-left: 1.1rem; +} +.storyline-ref-panel li { margin-bottom: 0.5rem; } +.storyline-ref-panel li:last-child { margin-bottom: 0; } + +/* Chapter pager */ +.storyline-pager { + display: flex; + justify-content: space-between; + gap: 1rem; + margin-top: 3rem; + padding-top: 1.25rem; + border-top: 1px solid var(--bs-border-color); +} +.storyline-pager-link { + display: flex; + flex-direction: column; + gap: 0.15rem; + text-decoration: none; + color: inherit; + padding: 0.5rem 0.75rem; + border: 1px solid var(--bs-border-color); + border-radius: 0.4rem; + max-width: 48%; +} +.storyline-pager-link:hover { border-color: var(--bs-primary); } +.storyline-pager-dir { + font-size: 0.8rem; + color: var(--bs-secondary-color); +} +.storyline-pager-title { font-weight: 600; } diff --git a/src/Controller/StorylineController.php b/src/Controller/StorylineController.php index 5aa7b3e..d280abd 100644 --- a/src/Controller/StorylineController.php +++ b/src/Controller/StorylineController.php @@ -10,9 +10,46 @@ use Symfony\Component\Security\Http\Attribute\IsGranted; #[IsGranted('IS_AUTHENTICATED_FULLY')] class StorylineController extends AbstractController { - #[Route('/storyline', name: 'app_storyline')] + /** + * The storyline is a set of hand-written chapters. Each entry points at a + * Twig partial under templates/storyline/chapters/. Add a chapter by adding + * a row here and the matching template; order in this array is reading order. + * + * @var array + */ + private const CHAPTERS = [ + 'overview' => [ + 'title' => 'Campaign Overview', + 'summary' => 'The whole shape of the Brackenford campaign, from the sealing of Kyuss four centuries ago to the final crisis — the truth behind the blood, the red herring around Sir Aldren, and how it can resolve.', + 'template' => 'storyline/chapters/overview.html.twig', + ], + ]; + + #[Route('/storyline', name: 'app_storyline', methods: ['GET'])] public function index(): Response { - return $this->render('storyline/index.html.twig'); + return $this->render('storyline/index.html.twig', [ + 'chapters' => self::CHAPTERS, + ]); + } + + #[Route('/storyline/{slug}', name: 'app_storyline_chapter', requirements: ['slug' => '[a-z0-9-]+'], methods: ['GET'])] + public function chapter(string $slug): Response + { + if (!isset(self::CHAPTERS[$slug])) { + throw $this->createNotFoundException('No such storyline chapter.'); + } + + $slugs = array_keys(self::CHAPTERS); + $pos = array_search($slug, $slugs, true); + + return $this->render('storyline/chapter.html.twig', [ + 'chapters' => self::CHAPTERS, + 'slug' => $slug, + 'number' => $pos + 1, + 'chapter' => self::CHAPTERS[$slug], + 'prev' => $pos > 0 ? $slugs[$pos - 1] : null, + 'next' => $pos < \count($slugs) - 1 ? $slugs[$pos + 1] : null, + ]); } } diff --git a/templates/storyline/chapter.html.twig b/templates/storyline/chapter.html.twig new file mode 100644 index 0000000..0a81069 --- /dev/null +++ b/templates/storyline/chapter.html.twig @@ -0,0 +1,51 @@ +{% extends 'base.html.twig' %} + +{% block title %}{{ chapter.title }} · Storyline · dmtools{% endblock %} + +{% block body %} +
+ + +
+
+
+
Storyline · Chapter {{ number }}
+

{{ chapter.title }}

+

{{ chapter.summary }}

+
+ + {{ include(chapter.template) }} +
+ + +
+
+{% endblock %} diff --git a/templates/storyline/chapters/overview.html.twig b/templates/storyline/chapters/overview.html.twig new file mode 100644 index 0000000..8258a57 --- /dev/null +++ b/templates/storyline/chapters/overview.html.twig @@ -0,0 +1,169 @@ +{# Campaign Overview — hand-formatted from the campaign bible text. #} + +
+
+

By the numbers

+
+
~400 years
since the five heroes sealed Kyuss
+
~175
villagers in Brackenford
+
~133
eligible blood donors
+
~15 litres
of blood collected every month
+
~2 litres
per month is all Aldren needs for himself
+
~1 hour
village to castle (the party travels in for ~2)
+
Age 15
when village-born children are told the secret
+
+
+
+

The cast

+
    +
  • Sir Aldren — one of the original five heroes; cursed with vampirism by Kyuss; for four centuries the prison's unseen Warden.
  • +
  • Kyuss — the ancient evil (or a fragment of it) sealed beneath the castle, now leaking out as the seal fails.
  • +
  • Eamon Vale — 41, a Brackenford farmer; inherits; meets Aldren; misunderstands him; is hidden away by the council.
  • +
  • Garrick Voss — 61, a village elder; not evil, but afraid; the party's fiercest opposition.
  • +
  • Bastian Cooper — drives the blood cart to the castle each month.
  • +
  • The halfling family — the player characters: hired to deliver the inheritance and come back with Eamon's signed, magically authenticated receipt.
  • +
+
+
+ +

01 The Truth, Four Hundred Years Ago

+

Roughly four centuries ago, something connected to Kyuss began spreading through the region that now surrounds Brackenford.

+

Whether it was Kyuss himself, an aspect of him, or a fragment of his power can stay deliberately uncertain. What matters is that it could not simply be killed. Its influence corrupted living creatures, animated the dead, and gradually transformed the land.

+

Five adventurers confronted it. Among them was Sir Aldren, then a mortal warrior and accomplished fighter.

+

They eventually discovered that destroying the entity was impossible. Instead they devised a prison: an ancient magical seal capable of containing the fragment and keeping its influence from the world. They succeeded — but not without cost.

+

During the final confrontation, Kyuss cursed Aldren with vampirism. It was meant as a last act of cruelty: one of the heroes who had fought undeath would himself become an undead monster.

+

Kyuss made one critical mistake. Aldren did not leave. Rather than surrender to his curse, he became the guardian of the very prison he had helped create. The other four heroes eventually died. Aldren remained. For four hundred years.

+ +

02 The Blood

+

The prison was never completely self-sustaining. Maintaining the seal requires life energy, and blood is an effective conduit for it.

+

Aldren also requires blood to remain sane — but the amount he personally consumes is surprisingly small, roughly two litres per month. The remainder goes toward maintaining the prison.

+

Over the centuries the details were lost. The people of Brackenford remember only the simplified version:

+
There is a vampire in the castle. Give him blood, and he leaves Brackenford alone.
+

Nobody alive remembers that Aldren is protecting them. Nobody remembers Kyuss. Nobody remembers what the blood actually does. Even the village council doesn't know the full truth. They aren't maintaining a sinister conspiracy — they're maintaining a tradition they are terrified to stop.

+ +

03 Modern Brackenford

+

Brackenford is an isolated but prosperous, largely self-sufficient village in the hills, with roughly 175 inhabitants.

+

It farms its own food, raises livestock, makes clothing, and keeps the craftsmen, hunters, woodcutters, mill, temple, tavern and inn a village needs. It isn't completely cut off: several other villages lie about a day's journey apart, and apprentices move between settlements — bringing new skills, marriages and “new blood.”

+

Outsiders who settle permanently eventually undergo a ceremony in which they are told the village's great secret. Children born in Brackenford learn it during a ceremony in the year they turn fifteen. The secret is simple: Sir Aldren is a vampire, and once a month Brackenford sends him blood.

+

There are currently around 133 eligible donors. About 15 litres of blood are collected each month, spread across enough villagers that individual donors get substantial recovery time. The villagers believe this is tribute. The blood is loaded onto a specially maintained cart and driven by Bastian Cooper to Aldren's castle, roughly an hour away. For generations it has worked. Nobody asks why.

+ +

04 But the System Is Failing

+

Aldren has known for some time that something is wrong. The seal is deteriorating. At first he compensated himself. Then maintaining it began requiring more blood. Then more. The normal fifteen litres eventually became insufficient.

+

Unlike the previous centuries, the deterioration has now reached a tipping point, and the failure is accelerating. Aldren is exhausted. He has been doing this for four hundred years and has lost everyone he once knew. The purpose of many parts of the original mechanism is becoming difficult even for him to remember, and parts of the prison have physically decayed.

+

And Aldren himself is starving. Of the blood sent each month, he takes only what he needs to retain control of himself. He refuses to take more simply to make himself stronger when that blood is desperately needed by the seal.

+ + +

05 The Heir

+

Meanwhile, somewhere completely unrelated to this ancient problem, someone dies. The deceased leaves an inheritance to Eamon Vale, a 41-year-old farmer living in Brackenford.

+

A group of halflings — a family trying to earn enough money for their farm — accepts what sounds like an unusually lucrative but very easy job: deliver the inheritance to Eamon Vale. They receive half their payment in advance. To collect the rest they must return with a magically authenticated receipt signed by Eamon himself. No Eamon, no signature. No signature, no money.

+ + +

06 The Night Before Their Arrival

+

Shortly before the characters reach Brackenford, Eamon is outside the village in the early evening. He encounters Sir Aldren, who is desperate enough to break centuries of distance between himself and the villagers.

+

Exactly what Aldren says should be carefully chosen. Something along the lines of:

+
Tell them the blood is no longer enough.
+

Aldren means: the seal is failing. Eamon hears: the vampire wants more.

+

He runs back to Brackenford terrified, telling people that Aldren approached him, that the vampire says the blood isn't enough, and that he wants more. Panic begins to spread. The council intervenes: Eamon is taken somewhere secure, supposedly for his own protection and to let him calm down, because they don't want rumours running through the village before they understand what happened.

+

Then five complete outsiders arrive and ask:

+
Where can we find Eamon Vale?
+ +

07 The Beginning of the Mystery

+

The characters reach Brackenford after roughly two hours' travel. Eamon is nowhere to be found. At the tavern or inn they ask after him, and the reaction is strange — people exchange glances. Someone tells them:

+
We'll take the inheritance for him.
+

Unfortunately that isn't possible: they specifically need Eamon's magically authenticated signature. At first nobody wants to explain where he is.

+

Now the players have exactly the incentive we wanted. They don't investigate because a mysterious village sounds interesting. Someone in this village is preventing them from finishing their job and getting their money.

+ +

08 What the Players Begin to Discover

+

Their search for Eamon gradually exposes cracks in Brackenford's pleasant exterior. People become evasive when the castle is mentioned. Certain subjects immediately end conversations. The council insists Eamon is safe. The villagers have peculiar customs surrounding blood. Some are frightened of outsiders asking too many questions.

+

Eventually the party learns about the blood cart. Once every month, blood leaves Brackenford, travels to the castle, and in that castle lives Sir Aldren — a vampire.

+

Suddenly everything makes sense. Eamon encountered the vampire. The vampire demanded more blood. The council silenced Eamon. The village secretly feeds the vampire. The elders don't want outsiders investigating. There is a monster in a castle overlooking Brackenford.

+

The players have solved the mystery. Except they haven't.

+ +

09 Garrick Voss

+

Garrick Voss, a 61-year-old village elder, becomes one of the strongest sources of resistance. Garrick isn't evil. He is afraid. His family has survived in Brackenford because generations before him followed the rules: blood goes into the cart, the cart goes to Aldren, Aldren stays away, nobody dies.

+

As far as Garrick is concerned, that is an extraordinarily successful arrangement — and then five outsiders arrive and immediately start questioning something that has kept his family alive for centuries. Eventually his frustration can produce the line:

+
You are outside blood.
+

They have no right, in his eyes, to risk Brackenford's children because they're curious. And if they insist on interfering with the arrangement:

+
Then perhaps you can pay with your blood.
+

This strongly reinforces the apparent truth: the elders are protecting the vampire.

+ +

10 Finding Eamon

+

Importantly, the characters should not find Eamon immediately. By the time they reach him, they should already know enough about the blood cart that his story matters.

+

Eamon tells them what happened. He was outside. He encountered Aldren. And Aldren told him: the blood is no longer enough.

+ + +

11 Sir Aldren, the Monster in the Castle

+

Everything should push the players toward one obvious conclusion: Aldren is the campaign's villain.

+

He is a four-hundred-year-old vampire. Villagers send him blood. He lives alone in an old castle. He recently approached a villager and demanded more. The council is terrified of angering him. Strange things are beginning to happen around Brackenford. And the worse the seal becomes, the more undead phenomena appear — which, from the players' perspective, seems to prove that Aldren is becoming more powerful.

+ + +

12 The World Begins to Turn Against Them

+

The manifestations start small. Animals behave strangely. Plants rot unexpectedly. People hear things at night. Dead animals don't stay where they were buried. Worms — or similar imagery associated with Kyuss — begin appearing.

+

Then things escalate. Undead appear. The countryside becomes increasingly hostile. Old places around Brackenford begin reacting to something. Ancient symbols are uncovered. Parts of the forgotten history of the region resurface.

+

The party learns that roughly four hundred years ago, five heroes fought some terrible evil here. This is a local legend in Brackenford; a village or two away it's merely an old story; further away almost nobody has heard of it, and much of the tale has been distorted.

+

Eventually the party discovers something deeply inconvenient: Sir Aldren was one of the five heroes.

+ +

13 The Five and the Five

+

There is a deliberate parallel. Four hundred years ago, five adventurers arrived to confront the evil. Now, five adventurers stand in Brackenford.

+ + +

14 Meeting Aldren

+

The eventual meeting with Aldren should overturn the campaign. The party expects a vampire lord. Instead they find a tired soldier — still dangerous, still frightening, still unmistakably a vampire, but exhausted.

+

Aldren isn't surrounded by victims. There is no court of vampire spawn. There are no dungeons filled with villagers. There is a man who has spent four centuries guarding something. Perhaps they find him physically beside part of the seal.

+

And when they confront him about demanding more blood, they finally learn the truth:

+
You think the blood is for me?
+

Only a fraction is. The rest keeps something else imprisoned.

+ +

15 Kyuss

+

Now the campaign changes. The question is no longer how do we kill Aldren? It becomes what has Aldren been keeping underneath this place for four hundred years?

+

The ancient evil associated with Kyuss has been contained beneath or around the castle. Aldren isn't its master — he is its jailer. His vampirism isn't evidence that he serves the ancient evil; it is the scar Kyuss left on him when Aldren helped imprison it.

+

And now the prison is failing. The strange events around Brackenford aren't Aldren's influence spreading. They're Kyuss leaking out.

+ +

16 The Dangerous Possibility

+ + +

17 The Blood Cart

+

Eventually the monthly collection occurs. This should be the only actual blood-cart departure the characters witness during the campaign. By then the cart isn't abstract anymore: they know these people. They've eaten with them. They've spoken to their children. They know Bastian Cooper. They know the families whose blood is being collected.

+

Around 15 litres are prepared — and now the characters know something most of those villagers don't: it isn't enough.

+

The village has around 133 eligible donors. There is physically enough blood available to temporarily reinforce the seal. But convincing Brackenford to provide dramatically more than the traditional amount means asking an entire community to overturn four centuries of fear.

+

The villagers believe additional blood means feeding the monster. The characters know it means buying time. And Garrick Voss — who earlier told them to pay with their own “outside blood” — now has to confront the possibility that the tradition he fiercely protected was based on a misunderstanding.

+ +

18 Solving the Problem

+

More blood cannot be the permanent solution. The extra blood should only stabilise the prison temporarily. It buys the characters enough time to actually repair what is broken.

+

The final portion of the campaign therefore becomes about reconstructing knowledge lost over four centuries. The five original adventurers knew how the prison worked. That knowledge is now fragmented: some is in Aldren's memory, some survives in local legends, and some may exist in ruins, inscriptions, old family records or forgotten structures around Brackenford.

+ +

The players must understand what their predecessors did before they can repair it. The campaign's investigation finally becomes its solution. Everything they've learned matters.

+ +

19 The Final Crisis

+

As the seal approaches failure, Kyuss's influence becomes undeniable. The campaign moves through four stages:

+
    +
  1. “Something is strange about this village.”
  2. +
  3. “There is a vampire in that castle.”
  4. +
  5. “The vampire isn't the monster.”
  6. +
  7. “The world itself is turning against us.”
  8. +
+

The characters have only a short period in which to act. They need blood to stabilise the mechanism. They need Aldren's knowledge. They need information left behind by the original five. They need the cooperation of Brackenford. And they need to survive whatever manifestations of Kyuss emerge as the prison begins opening.

+

Nobody else can realistically solve it in time. Not because they're chosen. Because they're here.

+ +

20 The Ending

+

If they succeed, Kyuss is contained again. Brackenford survives. But things cannot simply return to normal.

+

The villagers now know — or at least some of them know — that their ancestors misunderstood the blood ritual. Their supposed monster was their protector. Their council maintained a lie without even knowing it was a lie. Aldren has spent four centuries protecting people who feared and hated him. And the prison still exists.

+

The characters haven't destroyed Kyuss. They've bought Brackenford another future.

+

Aldren's fate can depend on the choices made during the campaign. He might remain the Warden. He might finally be freed from his duty at enormous cost. He might die ensuring the repaired prison closes. This can stay undecided until the mechanics of the seal and the five original heroes are finalised. But the village is safe. For now.

+

The halflings can finally get Eamon's signature. They can return with proof that the inheritance was delivered, and collect the other half of their payment. They leave Brackenford considerably less innocent than when they entered it.

+
We should have done more for those people. But right now, they're safe.
diff --git a/templates/storyline/index.html.twig b/templates/storyline/index.html.twig index 1be6bda..f016c43 100644 --- a/templates/storyline/index.html.twig +++ b/templates/storyline/index.html.twig @@ -3,9 +3,29 @@ {% block title %}Storyline · dmtools{% endblock %} {% block body %} - {{ include('_partials/page_placeholder.html.twig', { - title: 'Storyline', - icon: '📜', - blurb: 'The campaign storyline will live here. Shape still to be decided — session log, plot threads, or a wiki-style document.', - }) }} +
+
+
Storyline
+

Brackenford

+

+ The campaign bible, split into chapters. Start with the overview for the whole + arc; later chapters will cover sessions, locations and the people of the village. +

+
+ +
    + {% for slug, chapter in chapters %} +
  1. + + {{ loop.index }} + + {{ chapter.title }} + {{ chapter.summary }} + + + +
  2. + {% endfor %} +
+
{% endblock %}