adventure: give the realm a map, a board and a history

Everything Pete published so far was either the present moment (the roster,
the Siege bar) or one thing that happened (a dispatch, a run report). None of
it said what this place IS — how many zones there are, which are harder, or
whether anybody has ever actually beaten them.

Three pages off one snapshot, because they are one question and every number
on all three comes off the same scan of the same run history upstream:

  /adventure/realm      the world, in difficulty order, with who is inside it
  /adventure/standings  the board, plus Pete's own duel record
  /adventure/firsts     the hall of firsts, as a dated history

The map is deliberately not who_map.go's layout engine. That lays out a graph,
and the realm has no edges — zones aren't connected, you pick one and go.
Forcing a graph onto a set would imply a topology the game doesn't have. What
it has is an order, so tier bands are what get drawn.

A zone nobody has ever cleared looks different rather than saying so in small
text, and that styling keys off the clear count, never off whether a name is
attached — otherwise an opt-out would silently redraw a conquered place as one
nobody has come out of.

The per-kind first dot goes through a custom property instead of a
.firsts-entry-zone::before rule: input.css is in Tailwind's content glob, so a
hand-written class survives the purge only if its literal name can be lifted
out of the file, and a name glued to ::before cannot. It failed silently.

Claude-Session: https://claude.ai/code/session_012bxpQQJDjC1mTtLN3VVtBQ
This commit is contained in:
prosolis
2026-07-24 17:54:49 -07:00
parent b4a276da36
commit 1dfd3ac9fb
12 changed files with 1745 additions and 7 deletions
+76
View File
@@ -0,0 +1,76 @@
{{define "title"}}Hall of firsts — {{.SiteTitle}}{{end}}
{{define "main"}}
<article class="mt-2 mb-10 max-w-3xl mx-auto">
{{template "realmnav" .}}
<header class="rounded-3xl bg-theme-adventure text-white p-6 sm:p-10 shadow-pete relative overflow-hidden">
<div class="absolute -top-10 -right-6 text-[12rem] opacity-20 select-none" aria-hidden="true">📜</div>
<div class="relative">
<p class="text-sm uppercase tracking-[0.2em] opacity-80">📜 Hall of firsts</p>
<h1 class="font-display text-3xl sm:text-4xl font-bold mt-2 leading-tight">Everything that has only ever happened once.</h1>
<p class="mt-3 opacity-90 max-w-2xl">
The first time anyone walked out of a place alive. The first time a thing
came out of the ground. Each of these happened exactly once in the history
of the realm and can't happen again.
</p>
{{if .Firsts.Total}}
<div class="mt-6 flex flex-wrap gap-x-8 gap-y-3 text-xs uppercase tracking-wider opacity-85">
<span>Entries · <span class="font-semibold tabular-nums normal-case tracking-normal text-base">{{.Firsts.Total}}</span></span>
<span>Places opened · <span class="font-semibold tabular-nums normal-case tracking-normal text-base">{{.Firsts.Zones}}</span></span>
{{if .Firsts.Others}}<span>Things found · <span class="font-semibold tabular-nums normal-case tracking-normal text-base">{{.Firsts.Others}}</span></span>{{end}}
</div>
{{end}}
{{if and .Firsts.Known .Firsts.Stale}}
<p class="mt-4 text-xs bg-black/25 rounded-xl px-3 py-2">
Ledger as of {{.Firsts.LastSeenAgo}}. Nothing in a history book goes stale exactly, but a new entry might not be here yet.
</p>
{{end}}
</div>
</header>
{{if .Firsts.Years}}
{{range .Firsts.Years}}
<section class="mt-8">
<h2 class="font-display text-2xl font-bold mb-4 tabular-nums">
{{if .Year}}{{.Year}}{{else}}Before the records{{end}}
</h2>
<ol class="firsts-ledger">
{{range .Firsts}}
<li class="firsts-entry firsts-entry-{{.Kind}}">
<div class="flex flex-wrap items-baseline gap-x-2 gap-y-1">
<span class="text-[11px] uppercase tracking-wider text-[color:var(--ink)]/40 font-semibold">{{.Label}}</span>
<span class="font-display font-bold text-base">{{.Display}}</span>
{{if .Tier}}<span class="text-[11px] text-[color:var(--ink)]/35 tabular-nums">T{{.Tier}}</span>{{end}}
</div>
<p class="mt-0.5 text-sm text-[color:var(--ink)]/60">
{{if .Token}}<a href="/adventure/who/{{.Token}}" class="font-semibold text-[color:var(--ink)]/80 hover:text-theme-adventure transition">{{.Holder}}</a>
{{else if .Holder}}<span class="font-semibold text-[color:var(--ink)]/80">{{.Holder}}</span>
{{else}}<span class="italic text-[color:var(--ink)]/40">nobody left who'll admit to it</span>{{end}}
{{if .When}}<span class="text-[color:var(--ink)]/35"> · {{.When}}</span>{{end}}
</p>
</li>
{{end}}
</ol>
</section>
{{end}}
<p class="mt-8 text-xs text-[color:var(--ink)]/40 text-center max-w-xl mx-auto">
An entry with no name is one where the record has outlived the record-holder —
a thing found and long since given away, or somebody who'd rather I didn't say.
The claim still stands.
</p>
{{else}}
<div class="mt-6 rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 p-6 shadow-pete">
<p class="text-sm text-[color:var(--ink)]/60">
{{if .Firsts.Known}}
Nothing's happened for the first time yet. Everything is about to be a first.
{{else}}
Haven't had the ledger through from the field yet.
{{end}}
</p>
</div>
{{end}}
</article>
{{end}}