{{define "title"}}{{.Mark.Name}} — {{.SiteTitle}}{{end}} {{/* One item, wherever it's sitting. Worn, backpack, and vault all show the same facts; only the panel around them differs. The description and the effect line come from gogobee already resolved — Pete renders them, it does not compute them. "inert" is the honest word for a worn item that wants a bond and hasn't got one: it's on you and doing nothing. */}} {{define "itemrow"}}
  • {{.Name}}{{if .Temper}} +{{.Temper}}{{end}} {{if .Tier}}T{{.Tier}}{{end}}{{if .Value}}{{if .Tier}} · {{end}}{{.Value}}g{{end}}
    {{if or .Slot .SkillSource .Attunement}}
    {{if .Slot}}{{.Slot}}{{end}} {{if .SkillSource}}{{.SkillSource}}{{end}} {{if .Attunement}}{{if .Attuned}}bonded{{else if .Worn}}inert · no bond free{{else}}needs a bond{{end}}{{end}}
    {{end}} {{if .Desc}}

    {{.Desc}}

    {{end}} {{if .Effect}}

    {{.Effect}}

    {{end}} {{/* The compare card: how this backpack item stacks up against what's worn in the slot it would equip into. gogobee decided the verdict and the per-stat deltas (it holds the tempering and bond math); Pete only colours them. The verdict chip always shows — a phone has no hover to lean on — with the deltas beside it, each already flagged better/worse. */}} {{with .Compare}}
    {{if eq .Verdict "upgrade"}}↑ upgrade {{else if eq .Verdict "downgrade"}}↓ downgrade {{else if eq .Verdict "sidegrade"}}⇄ sidegrade {{else if eq .Verdict "new"}}✦ fills {{.VsSlot}} {{else if eq .Verdict "inert"}}⚠ would sit inert {{else if eq .Verdict "same"}}= no change{{end}} {{range .Deltas}}{{.Text}}{{end}}
    {{if .VsName}}

    vs worn {{.VsName}}

    {{end}} {{end}} {{if .EquipAction}}
    {{end}}
  • {{end}} {{/* The dungeon map. Laid out server-side (who_map.go) into an x/y node field; this just draws it. Edges first so nodes sit on top of the lines. Nothing here is a player-authored string — node kinds are a fixed vocabulary and ids are zone-prefixed — so the SVG carries no injection surface. */}} {{define "dungeonmap"}} {{range .Edges}}{{if .Lock}}{{.Lock}}{{end}}{{end}} {{range .Nodes}}{{if .Current}}{{end}}{{.Glyph}}{{.Label}}{{if .Current}} — you are here{{end}}{{end}} {{end}} {{define "main"}}

    adventurer

    {{.Mark.Name}}

    Level {{.Mark.Level}} {{.Mark.ClassRace}}

    {{if .Mark.OnRun}}{{.Mark.Where}}{{else}}In town{{if .Mark.Idle}} · {{.Mark.Idle}}{{end}}{{end}}

    {{if .HasDetail}}

    Vitals

    Hit points {{.Detail.HPCurrent}} / {{.Detail.HPMax}}{{if .Detail.TempHP}} +{{.Detail.TempHP}}{{end}}
    Armor class {{.Detail.ArmorClass}}
    {{range .Abilities}}
    {{.Label}}
    {{.Score}}
    {{.ModStr}}
    {{end}}
    {{if .Mark.OnRun}}
    {{if .Detail.Room}}
    Room{{.Detail.Room}}
    {{end}}
    Supplies{{.Detail.Supplies}}
    {{if .Detail.ThreatLevel}}
    Threat{{.Detail.ThreatLevel}}
    {{end}}
    {{end}} {{/* The party. Absent on a solo run, so this block answers "is anybody else down there" rather than drawing a chair count. Server-rendered only: a roster is settled before a party leaves town, so unlike the HP and supply numbers above it there is nothing here for the poll to move. An unnamed seat is an opted-out player, kept on purpose — see partySeat.Anonymous for why it is not simply dropped. */}} {{if .Detail.Party}}

    Down there together

      {{range .Detail.Party}}
    • {{.Kind}} {{if .Anonymous}} an adventurer who keeps out of the news {{else if .Token}} {{.Name}} {{else}} {{.Name}} {{end}} {{if .Level}}lv {{.Level}}{{end}}
    • {{end}}
    {{end}}
    {{/* Public gear list. Hidden only when the owner's "Equipment" panel below will actually render (it needs pushed Slots) — otherwise an owner whose detail predates ask 7's Slots would see no standard gear at all. */}} {{if not .Self.Slots}}

    Gear

    {{if .Detail.Gear}}
      {{range .Detail.Gear}}
    • {{.Slot}} {{.Name}}{{if .Masterwork}} {{end}} T{{.Tier}}{{if .Condition}} · {{.Condition}}%{{end}}
    • {{end}}
    {{else}}

    Traveling light — nothing equipped.

    {{end}}
    {{end}}
    {{if .MapView}}

    The map

    {{if .Detail.Room}}Room {{.Detail.Room}}{{end}}
    {{template "dungeonmap" .MapView}}
    entrance boss unexplored locked door
    {{end}} {{else}}

    No detailed sheet on file for this adventurer yet — check back after the next snapshot.

    {{end}}

    The run

    {{if .RunLog.Live}}under way{{else if .RunLog.Outcome}}{{.RunLog.Outcome}}{{else}}finished{{end}}{{if .RunLog.Rooms}} · room {{.RunLog.Rooms}}{{end}}
      {{range .RunLog.Lines}}
    1. {{.Text}} {{if .Room}}{{.Room}} · {{end}}{{.When}}
    2. {{end}}
    The full report
    {{if .HasSelf}}

    Your call

    Asked for here, done on the game box. It picks these up within a few seconds.

    {{/* data-offer marks what stops being true once the extraction lands: there is no run left to pull out of. Hidden by the script on an applied verdict, restored on a refusal, which is the one case where the reader still needs the retry. W9: withheld from a party MEMBER, and only from them. W5a's rule is that an idle-looking mark still gets this button, because the board is two minutes stale and refusing a live extraction is worse than a no-op — but a member is not a staleness question. Pete knows from the same seat it just read that gogobee will answer rejected_not_leader, and offering a certain refusal directly above "Turn back alone", which is the thing that does work, is the page contradicting itself. CanLeave is only ever true for a seat that says "member". */}} {{if not .CanLeave}}
    {{end}} {{/* W9. The two ways out that are not an extraction, each in its own data-offer wrapper rather than sharing the extract one above — an extracted run is still abandonable, so hiding "Call it off" when the extraction lands would take away the very button that case needs. They are mutually exclusive by construction (offersToUndo reads the viewer's own party seat: a leader is offered one, a member the other), which is why neither mentions the other. */}} {{if .CanAbandon}}
    {{end}} {{if .CanLeave}}
    {{end}} {{/* W5b. Everything below is server-rendered from the offer list gogobee pushed onto this owner's own private row, so the page can only ever propose a zone, a loadout and a price the game itself quoted. The order carries those keys back and gogobee re-resolves all of them — an offer is a quote, never a permission. */}} {{if .Self.Resume}} {{/* The way back in, first: it is the only offer here with a deadline on it, and it is the answer to the verdict "Pull out" leaves behind, which until now told a web player to go and type !resume in Matrix. */}}

    Go back in

    {{.Self.Resume.Display}}, day {{.Self.Resume.Day}} — waiting where you left it. {{with untilUnix .Self.Resume.ExpiresAt}}{{.}}.{{end}} You re-stock before you go, so pick a pack.

    {{$rz := .Self.Resume}} {{range $rz.Loadouts}} {{end}}
    {{end}} {{if .Self.Zones}} {{/* Set out. The zone list is already level-gated and postgame-gated by gogobee, and it arrives EMPTY while this adventurer is already out — which is why there is no "you're on an expedition" branch here. */}}

    Set out

    Pick where, then pick how much to carry. The pack is what you pay for.

    {{/* One group per zone, all rendered, one shown. The costs differ by tier, so a single shared row of buttons would have to be repriced in the browser — and Pete does no arithmetic on the game's money. */}} {{range .Self.Zones}} {{$z := .}} {{end}}
    {{end}} {{if .Self.Babysit}}

    The sitter

    {{if .Self.Babysit.Active}} {{/* Engaged: say so, and offer only the way back out. Buying a second one is refused by the game anyway, and a buy button under "already engaged" reads as a page that has not noticed. */}}

    Somebody is looking after the camp{{with untilUnix .Self.Babysit.ExpiresAt}} — {{.}}{{end}}. Your pet is being tended daily and standard camps rest like fortified ones.

    {{if .CanCancelSitter}}
    {{end}} {{else}}

    Pet tended daily, standard camps rest like fortified ones, rival duels declined for you.

    {{end}}
    {{end}}
    {{end}} {{if .HasHistory}}

    The record

    {{.Trophies.BossKills}}
    bosses down
    {{.Trophies.ZoneClears}}
    zones cleared
    {{.Trophies.TreasuresFound}}
    treasure{{if ne .Trophies.TreasuresFound 1}}s{{end}}
    {{.Trophies.Deaths}}
    death{{if ne .Trophies.Deaths 1}}s{{end}}
    {{.Trophies.Retreats}}
    walked out
    {{if or .Trophies.BossFirsts .Trophies.ZoneFirsts .Trophies.TreasureFirsts}}

    ★ {{if .Trophies.BossFirsts}}{{.Trophies.BossFirsts}} realm-first boss{{if ne .Trophies.BossFirsts 1}}es{{end}}{{end}}{{if and .Trophies.BossFirsts (or .Trophies.ZoneFirsts .Trophies.TreasureFirsts)}}, {{end}}{{if .Trophies.ZoneFirsts}}{{.Trophies.ZoneFirsts}} first clear{{if ne .Trophies.ZoneFirsts 1}}s{{end}}{{end}}{{if and .Trophies.ZoneFirsts .Trophies.TreasureFirsts}}, {{end}}{{if .Trophies.TreasureFirsts}}{{.Trophies.TreasureFirsts}} first hoard{{if ne .Trophies.TreasureFirsts 1}}s{{end}}{{end}}. Nobody had done it before.

    {{end}}
    {{if .Trophies.Bosses}}

    Bosses fought

      {{range .Trophies.Bosses}}
    • {{.Boss}}{{if .First}} {{end}} ×{{.Kills}}
    • {{end}}
    {{end}} {{if .Trophies.Zones}}

    Ground covered

      {{range .Trophies.Zones}}
    • {{.Zone}}{{if .First}} {{end}}{{if .Region}} {{.Region}}{{end}} ×{{.Clears}}
    • {{end}}
    {{end}}
    {{if .Trophies.Treasures}}

    Treasures found

      {{range .Trophies.Treasures}}
    • 💎 {{.Item}}{{if .First}} {{end}} {{if .Zone}}{{.Zone}}{{end}}
    • {{end}}
    {{end}} {{if .Trophies.Milestones}}

    Milestones

    {{range .Trophies.Milestones}} 🏅 {{.}} {{end}}
    {{end}}

    The trail

      {{range .Timeline}}
    1. {{.Label}} {{.When}}
      {{if .Line}}

      {{.Line}}

      {{end}}
    2. {{end}}
    {{if .MoreHistory}}

    Showing the most recent {{len .Timeline}}. The counts above cover everything.

    {{end}}
    {{end}} {{if .HasSelf}}
    Your adventurer only you can see the panels below
    {{/* Equipment: the 5 standard slots, owner-only management. Take off round-trips a masterwork/arena piece to the pack (no money); Upgrade buys the next tier and Repair mends condition, both spending euros behind a confirm that shows the cost and the resulting balance. Every price and tier here is gogobee's, resolved server-side — the buttons only carry what was pushed. */}} {{if .Self.Slots}}

    Equipment

    Balance €{{printf "%.2f" .Self.Balance}}
      {{range .Self.Slots}}
    • {{.Slot}} T{{.Tier}}{{if .Condition}} · {{.Condition}}%{{end}}
      {{.Name}} {{if .Masterwork}}{{end}} {{if .ArenaTier}}arena T{{.ArenaTier}}{{end}}
      {{if or .CanTakeOff .NextTier .RepairCost}}
      {{if .CanTakeOff}}{{end}} {{if .NextTier}}{{end}} {{if .RepairCost}}{{end}}
      {{end}}
    • {{end}}
    {{end}}

    Home

    House tier{{if .Self.House.Tier}}{{.Self.House.Tier}}{{else}}no house{{end}}
    {{if .Self.House.LoanBalance}}
    Loan balance{{.Self.House.LoanBalance}}
    {{end}} {{if .Self.House.Rate}}
    Rate{{.Self.House.Rate}}
    {{end}} {{if .Self.House.Autopay}}
    Autopayon
    {{end}}
    {{/* Pets, with their levelling shown. They have been earning XP from every won fight since that wiring was fixed, and until now the only place a level appeared was a Matrix line that scrolled away. Ranges over .PetRows, not .Self.Pets: the progress figures are worked out in Go from the centi-XP gogobee sends, because the curve behind the target is the engine's and there is no copy of it here. */}}

    Pets

    {{if .PetRows}}
      {{range .PetRows}}
    • {{if .Name}}{{.Name}}{{else}}your {{.Type}}{{end}} {{.Type}} lv {{.Level}}{{if .ArmorTier}} · barding T{{.ArmorTier}}{{end}}

      {{if .Capped}}fully grown{{else}}{{.Progress}} to level {{.NextLevel}}{{end}}

    • {{end}}
    {{else}}

    No pets right now.

    {{end}}
    {{if .Worn}}

    Worn

    {{.BondsUsed}} of 3 bonds in use

      {{range .Worn}}{{template "itemrow" .}}{{end}}

    Backpack

    {{else}}

    Backpack

    {{end}} {{if .Backpack}}
      {{range .Backpack}}{{template "itemrow" .}}{{end}}
    {{else}}

    Backpack's empty.

    {{end}} {{if .VaultRows}}

    Vault

      {{range .VaultRows}}{{template "itemrow" .}}{{end}}
    {{end}}
    {{end}}
    {{end}} {{define "scripts"}}{{end}}