{{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"}}
<li>
  <div class="flex items-baseline justify-between gap-3">
    <span class="flex-1 font-semibold">{{.Name}}{{if .Temper}} <span class="text-theme-adventure">+{{.Temper}}</span>{{end}}</span>
    <span class="text-xs text-[color:var(--ink)]/50 shrink-0">{{if .Tier}}T{{.Tier}}{{end}}{{if .Value}}{{if .Tier}} · {{end}}{{.Value}}g{{end}}</span>
  </div>
  {{if or .Slot .SkillSource .Attunement}}
  <div class="flex flex-wrap gap-1.5 mt-1">
    {{if .Slot}}<span class="text-[11px] rounded-full bg-[color:var(--ink)]/5 px-2 py-0.5 text-[color:var(--ink)]/60">{{.Slot}}</span>{{end}}
    {{if .SkillSource}}<span class="text-[11px] rounded-full bg-[color:var(--ink)]/5 px-2 py-0.5 text-[color:var(--ink)]/60">{{.SkillSource}}</span>{{end}}
    {{if .Attunement}}{{if .Attuned}}<span class="text-[11px] rounded-full bg-theme-adventure/20 px-2 py-0.5 text-theme-adventure font-semibold">bonded</span>{{else if .Worn}}<span class="text-[11px] rounded-full bg-amber-400/20 px-2 py-0.5 text-[color:var(--warn)] font-semibold">inert · no bond free</span>{{else}}<span class="text-[11px] rounded-full bg-[color:var(--ink)]/10 px-2 py-0.5 text-[color:var(--ink)]/60">needs a bond</span>{{end}}{{end}}
  </div>
  {{end}}
  {{if .Desc}}<p class="text-xs text-[color:var(--ink)]/55 mt-1 leading-snug">{{.Desc}}</p>{{end}}
  {{if .Effect}}<p class="text-xs text-theme-adventure/80 mt-0.5">{{.Effect}}</p>{{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}}
  <div class="mt-1 flex flex-wrap items-center gap-1.5">
    {{if eq .Verdict "upgrade"}}<span class="cmp-chip cmp-up">↑ upgrade</span>
    {{else if eq .Verdict "downgrade"}}<span class="cmp-chip cmp-down">↓ downgrade</span>
    {{else if eq .Verdict "sidegrade"}}<span class="cmp-chip cmp-side">⇄ sidegrade</span>
    {{else if eq .Verdict "new"}}<span class="cmp-chip cmp-new">✦ fills {{.VsSlot}}</span>
    {{else if eq .Verdict "inert"}}<span class="cmp-chip cmp-inert">⚠ would sit inert</span>
    {{else if eq .Verdict "same"}}<span class="cmp-chip cmp-same">= no change</span>{{end}}
    {{range .Deltas}}<span class="cmp-delta {{if .Better}}cmp-delta-up{{else}}cmp-delta-down{{end}}">{{.Text}}</span>{{end}}
  </div>
  {{if .VsName}}<p class="text-[11px] text-[color:var(--ink)]/45 mt-0.5">vs worn {{.VsName}}</p>{{end}}
  {{end}}
  {{if .EquipAction}}
  <div class="mt-1.5">
    <button type="button" class="equip-btn text-[11px] rounded-full border border-theme-adventure/40 text-theme-adventure hover:bg-theme-adventure/10 px-2.5 py-0.5 font-semibold transition-colors"
            data-action="{{.EquipAction}}" data-item-id="{{.ID}}" data-slot="{{.Slot}}" data-item-name="{{.Name}}">{{if eq .EquipAction "equip"}}Equip{{else}}Take off{{end}}</button>
  </div>
  {{end}}
</li>
{{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"}}
<svg viewBox="0 0 {{.W}} {{.H}}" width="{{.W}}" height="{{.H}}" role="img" aria-label="Dungeon map" class="map-svg max-w-full h-auto">
  <g>
    {{range .Edges}}<line x1="{{.X1}}" y1="{{.Y1}}" x2="{{.X2}}" y2="{{.Y2}}" class="map-edge{{if .Locked}} map-edge-locked{{end}}">{{if .Lock}}<title>{{.Lock}}</title>{{end}}</line>{{end}}
  </g>
  <g>
    {{range .Nodes}}<g class="map-node {{.Class}}{{if .Current}} map-node-current{{end}}" transform="translate({{.X}},{{.Y}})">{{if .Current}}<circle r="21" class="map-ring"></circle>{{end}}<circle r="15" class="map-disc"></circle><text text-anchor="middle" dominant-baseline="central" class="map-glyph">{{.Glyph}}</text><title>{{.Label}}{{if .Current}} — you are here{{end}}</title></g>{{end}}
  </g>
</svg>
{{end}}

{{define "main"}}
<article class="mt-2 mb-10 max-w-3xl mx-auto" id="who" data-token="{{.Mark.Token}}">
  <nav class="mb-4">
    <a href="/adventure" class="inline-flex items-center gap-1.5 text-sm font-semibold text-[color:var(--ink)]/60 hover:text-[color:var(--ink)] transition">
      <span aria-hidden="true">←</span> The board
    </a>
  </nav>

  <header class="rounded-3xl bg-theme-adventure text-white p-6 sm:p-10 shadow-pete relative overflow-hidden">
    <div class="absolute -top-6 -right-6 text-[12rem] opacity-20 select-none" aria-hidden="true">{{if .Mark.OnRun}}⚔{{else}}🏠{{end}}</div>
    <div class="relative">
      <p class="text-sm uppercase tracking-[0.2em] opacity-80">adventurer</p>
      <h1 class="font-display text-3xl sm:text-4xl font-bold mt-2 leading-tight">{{.Mark.Name}}</h1>
      <p class="mt-2 opacity-90">Level {{.Mark.Level}} {{.Mark.ClassRace}}</p>
      <p class="mt-4 text-xs uppercase tracking-wider opacity-75" id="who-where">
        {{if .Mark.OnRun}}{{.Mark.Where}}{{else}}In town{{if .Mark.Idle}} · {{.Mark.Idle}}{{end}}{{end}}
      </p>
    </div>
  </header>

  {{if .HasDetail}}
  <!-- Stats + gear: public, the same anonymity model as the board. -->
  <section class="mt-8 grid gap-6 sm:grid-cols-2">
    <div class="rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 p-6 shadow-pete">
      <h2 class="font-display text-xl font-bold mb-4">Vitals</h2>
      <div class="flex items-baseline justify-between">
        <span class="text-sm uppercase tracking-wider text-[color:var(--ink)]/50">Hit points</span>
        <span class="font-semibold" id="who-hp">{{.Detail.HPCurrent}} / {{.Detail.HPMax}}{{if .Detail.TempHP}} <span class="text-theme-adventure">+{{.Detail.TempHP}}</span>{{end}}</span>
      </div>
      <div class="mt-2 h-2 rounded-full bg-[color:var(--ink)]/10 overflow-hidden">
        <div class="h-full rounded-full bg-theme-adventure transition-all" id="who-hp-bar" style="width:0%"></div>
      </div>
      <div class="mt-4 flex items-baseline justify-between">
        <span class="text-sm uppercase tracking-wider text-[color:var(--ink)]/50">Armor class</span>
        <span class="font-semibold" id="who-ac">{{.Detail.ArmorClass}}</span>
      </div>

      <div class="mt-5 grid grid-cols-3 gap-2">
        {{range .Abilities}}
        <div class="rounded-2xl bg-[color:var(--ink)]/5 px-2 py-3 text-center">
          <div class="text-[10px] uppercase tracking-wider text-[color:var(--ink)]/50">{{.Label}}</div>
          <div class="font-display text-lg font-bold leading-none mt-1">{{.Score}}</div>
          <div class="text-xs text-[color:var(--ink)]/60">{{.ModStr}}</div>
        </div>
        {{end}}
      </div>

      {{if .Mark.OnRun}}
      <div class="mt-5 pt-4 border-t border-[color:var(--ink)]/10 space-y-1.5 text-sm" id="who-expedition">
        {{if .Detail.Room}}<div class="flex justify-between"><span class="text-[color:var(--ink)]/50">Room</span><span class="font-semibold" id="who-room">{{.Detail.Room}}</span></div>{{end}}
        <div class="flex justify-between"><span class="text-[color:var(--ink)]/50">Supplies</span><span class="font-semibold" id="who-supplies">{{.Detail.Supplies}}</span></div>
        {{if .Detail.ThreatLevel}}<div class="flex justify-between"><span class="text-[color:var(--ink)]/50">Threat</span><span class="font-semibold" id="who-threat">{{.Detail.ThreatLevel}}</span></div>{{end}}
      </div>
      {{end}}
    </div>

    <div class="rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 p-6 shadow-pete">
      <h2 class="font-display text-xl font-bold mb-4">Gear</h2>
      {{if .Detail.Gear}}
      <ul class="space-y-2">
        {{range .Detail.Gear}}
        <li class="flex items-baseline justify-between gap-3">
          <span class="text-xs uppercase tracking-wider text-[color:var(--ink)]/45 w-16 shrink-0">{{.Slot}}</span>
          <span class="font-semibold flex-1">{{.Name}}{{if .Masterwork}} <span title="masterwork">★</span>{{end}}</span>
          <span class="text-xs text-[color:var(--ink)]/50 shrink-0">T{{.Tier}}{{if .Condition}} · {{.Condition}}%{{end}}</span>
        </li>
        {{end}}
      </ul>
      {{else}}
      <p class="text-sm text-[color:var(--ink)]/50">Traveling light — nothing equipped.</p>
      {{end}}
    </div>
  </section>

  {{if .MapView}}
  <!-- The dungeon map: the fog-of-war cut gogobee sent. Visited rooms drawn in
       full, the doors leading out of them, and the unopened rooms behind those
       doors as question marks. Pete lays it out; it is never told what a room
       holds, only that a door is there. -->
  <section class="mt-6 rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 p-6 shadow-pete">
    <div class="flex items-baseline justify-between mb-4">
      <h2 class="font-display text-xl font-bold">The map</h2>
      {{if .Detail.Room}}<span class="text-sm text-[color:var(--ink)]/50">Room <span class="font-semibold text-[color:var(--ink)]/70">{{.Detail.Room}}</span></span>{{end}}
    </div>
    <div class="overflow-x-auto">
      {{template "dungeonmap" .MapView}}
    </div>
    <div class="mt-4 flex flex-wrap gap-x-4 gap-y-1.5 text-xs text-[color:var(--ink)]/55">
      <span class="inline-flex items-center gap-1.5"><span class="map-dot map-node-entry"></span>entrance</span>
      <span class="inline-flex items-center gap-1.5"><span class="map-dot map-node-boss"></span>boss</span>
      <span class="inline-flex items-center gap-1.5"><span class="map-dot map-node-unknown"></span>unexplored</span>
      <span class="inline-flex items-center gap-1.5"><span class="map-door-legend"></span>locked door</span>
    </div>
  </section>
  {{end}}
  {{else}}
  <section class="mt-8 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">No detailed sheet on file for this adventurer yet — check back after the next snapshot.</p>
  </section>
  {{end}}

  {{if .HasHistory}}
  <!-- The record. Public, like the dispatches it's counted from — this is the
       same information the /adventure feed already printed, only as numbers
       instead of as forty separate sentences. Not a gogobee snapshot: Pete
       counted these itself out of the facts it kept. -->
  <section class="mt-8 rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 p-6 shadow-pete">
    <h2 class="font-display text-xl font-bold mb-4">The record</h2>

    <div class="grid grid-cols-2 sm:grid-cols-5 gap-2">
      <div class="rounded-2xl bg-[color:var(--ink)]/5 px-2 py-3 text-center">
        <div class="font-display text-2xl font-bold leading-none">{{.Trophies.BossKills}}</div>
        <div class="text-[10px] uppercase tracking-wider text-[color:var(--ink)]/50 mt-1.5">bosses down</div>
      </div>
      <div class="rounded-2xl bg-[color:var(--ink)]/5 px-2 py-3 text-center">
        <div class="font-display text-2xl font-bold leading-none">{{.Trophies.ZoneClears}}</div>
        <div class="text-[10px] uppercase tracking-wider text-[color:var(--ink)]/50 mt-1.5">zones cleared</div>
      </div>
      <div class="rounded-2xl bg-[color:var(--ink)]/5 px-2 py-3 text-center">
        <div class="font-display text-2xl font-bold leading-none">{{.Trophies.TreasuresFound}}</div>
        <div class="text-[10px] uppercase tracking-wider text-[color:var(--ink)]/50 mt-1.5">treasure{{if ne .Trophies.TreasuresFound 1}}s{{end}}</div>
      </div>
      <div class="rounded-2xl bg-[color:var(--ink)]/5 px-2 py-3 text-center">
        <div class="font-display text-2xl font-bold leading-none">{{.Trophies.Deaths}}</div>
        <div class="text-[10px] uppercase tracking-wider text-[color:var(--ink)]/50 mt-1.5">death{{if ne .Trophies.Deaths 1}}s{{end}}</div>
      </div>
      <div class="rounded-2xl bg-[color:var(--ink)]/5 px-2 py-3 text-center">
        <div class="font-display text-2xl font-bold leading-none">{{.Trophies.Retreats}}</div>
        <div class="text-[10px] uppercase tracking-wider text-[color:var(--ink)]/50 mt-1.5">walked out</div>
      </div>
    </div>

    {{if or .Trophies.BossFirsts .Trophies.ZoneFirsts .Trophies.TreasureFirsts}}
    <p class="mt-3 text-sm text-theme-adventure font-semibold">
      ★ {{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.
    </p>
    {{end}}

    <div class="mt-6 grid gap-6 sm:grid-cols-2">
      {{if .Trophies.Bosses}}
      <div>
        <h3 class="font-display text-lg font-bold mb-3">Bosses fought</h3>
        <ul class="space-y-1.5 text-sm max-h-56 overflow-y-auto pr-1">
          {{range .Trophies.Bosses}}
          <li class="flex items-baseline justify-between gap-3">
            <span class="flex-1">{{.Boss}}{{if .First}} <span class="text-theme-adventure" title="first in the realm to clear it">★</span>{{end}}</span>
            <span class="text-xs text-[color:var(--ink)]/50 shrink-0">×{{.Kills}}</span>
          </li>
          {{end}}
        </ul>
      </div>
      {{end}}

      {{if .Trophies.Zones}}
      <div>
        <h3 class="font-display text-lg font-bold mb-3">Ground covered</h3>
        <ul class="space-y-1.5 text-sm max-h-56 overflow-y-auto pr-1">
          {{range .Trophies.Zones}}
          <li class="flex items-baseline justify-between gap-3">
            <span class="flex-1">{{.Zone}}{{if .First}} <span class="text-theme-adventure" title="first clear in the realm">★</span>{{end}}{{if .Region}} <span class="text-[color:var(--ink)]/45">{{.Region}}</span>{{end}}</span>
            <span class="text-xs text-[color:var(--ink)]/50 shrink-0">×{{.Clears}}</span>
          </li>
          {{end}}
        </ul>
      </div>
      {{end}}
    </div>

    {{if .Trophies.Treasures}}
    <div class="mt-6 pt-4 border-t border-[color:var(--ink)]/10">
      <h3 class="font-display text-lg font-bold mb-3">Treasures found</h3>
      <ul class="space-y-1.5 text-sm max-h-56 overflow-y-auto pr-1">
        {{range .Trophies.Treasures}}
        <li class="flex items-baseline justify-between gap-3">
          <span class="flex-1">💎 {{.Item}}{{if .First}} <span class="text-theme-adventure" title="first in the realm to pull this hoard">★</span>{{end}}</span>
          {{if .Zone}}<span class="text-xs text-[color:var(--ink)]/45 shrink-0">{{.Zone}}</span>{{end}}
        </li>
        {{end}}
      </ul>
    </div>
    {{end}}

    {{if .Trophies.Milestones}}
    <div class="mt-6 pt-4 border-t border-[color:var(--ink)]/10">
      <h3 class="font-display text-lg font-bold mb-3">Milestones</h3>
      <div class="flex flex-wrap gap-2">
        {{range .Trophies.Milestones}}
        <span class="rounded-full bg-theme-adventure/10 text-theme-adventure text-xs font-semibold px-3 py-1">🏅 {{.}}</span>
        {{end}}
      </div>
    </div>
    {{end}}
  </section>

  <!-- The trail: every fact we have, newest first, each linking to the dispatch
       that told it. -->
  <section class="mt-8 rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 p-6 shadow-pete">
    <h2 class="font-display text-xl font-bold mb-4">The trail</h2>
    <ol class="relative border-l-2 border-[color:var(--ink)]/10 ml-3 space-y-4">
      {{range .Timeline}}
      <li class="relative pl-6">
        <span class="absolute -left-[13px] top-0.5 flex h-6 w-6 items-center justify-center rounded-full bg-[color:var(--card)] border-2 {{if .Notable}}border-theme-adventure{{else}}border-[color:var(--ink)]/10{{end}} text-xs" aria-hidden="true">{{.Emoji}}</span>
        <a href="{{.Permalink}}" class="group block">
          <div class="flex items-baseline justify-between gap-3">
            <span class="font-semibold group-hover:text-theme-adventure transition {{if .Notable}}text-theme-adventure{{end}}">{{.Label}}</span>
            <span class="text-xs text-[color:var(--ink)]/45 shrink-0">{{.When}}</span>
          </div>
          {{if .Line}}<p class="text-sm text-[color:var(--ink)]/60 mt-0.5">{{.Line}}</p>{{end}}
        </a>
      </li>
      {{end}}
    </ol>
    {{if .MoreHistory}}
    <p class="mt-5 pt-4 border-t border-[color:var(--ink)]/10 text-xs text-[color:var(--ink)]/45">
      Showing the most recent {{len .Timeline}}. The counts above cover everything.
    </p>
    {{end}}
  </section>
  {{end}}

  {{if .HasSelf}}
  <!-- Owner-only: this is you. Inventory, vault, house, pets — private, served
       only because your signed-in localpart owns this page's token. -->
  <section class="mt-8">
    <div class="flex items-center gap-2 mb-3">
      <span class="rounded-full bg-theme-adventure text-white text-xs font-semibold px-3 py-1">Your adventurer</span>
      <span class="text-xs text-[color:var(--ink)]/45">only you can see the panels below</span>
    </div>

    <div class="grid gap-6 sm:grid-cols-2">
      <div class="rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 p-6 shadow-pete">
        <h2 class="font-display text-xl font-bold mb-4">Home</h2>
        <div class="space-y-1.5 text-sm">
          <div class="flex justify-between"><span class="text-[color:var(--ink)]/50">House tier</span><span class="font-semibold">{{if .Self.House.Tier}}{{.Self.House.Tier}}{{else}}no house{{end}}</span></div>
          {{if .Self.House.LoanBalance}}<div class="flex justify-between"><span class="text-[color:var(--ink)]/50">Loan balance</span><span class="font-semibold">{{.Self.House.LoanBalance}}</span></div>{{end}}
          {{if .Self.House.Rate}}<div class="flex justify-between"><span class="text-[color:var(--ink)]/50">Rate</span><span class="font-semibold">{{.Self.House.Rate}}</span></div>{{end}}
          {{if .Self.House.Autopay}}<div class="flex justify-between"><span class="text-[color:var(--ink)]/50">Autopay</span><span class="font-semibold">on</span></div>{{end}}
        </div>

        <h3 class="font-display text-lg font-bold mt-6 mb-3">Pets</h3>
        {{if .Self.Pets}}
        <ul class="space-y-2 text-sm">
          {{range .Self.Pets}}
          <li class="flex items-baseline justify-between gap-3">
            <span class="font-semibold flex-1">{{if .Name}}{{.Name}}{{else}}your {{.Type}}{{end}} <span class="text-[color:var(--ink)]/45 font-normal">{{.Type}}</span></span>
            <span class="text-xs text-[color:var(--ink)]/50 shrink-0">lv {{.Level}}{{if .ArmorTier}} · barding T{{.ArmorTier}}{{end}}</span>
          </li>
          {{end}}
        </ul>
        {{else}}
        <p class="text-sm text-[color:var(--ink)]/50">No pets right now.</p>
        {{end}}
      </div>

      <div id="gear-panel" data-token="{{.Mark.Token}}" class="rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 p-6 shadow-pete">
        {{if .Worn}}
        <h2 class="font-display text-xl font-bold mb-1">Worn</h2>
        <p class="text-xs text-[color:var(--ink)]/45 mb-3">{{.BondsUsed}} of 3 bonds in use</p>
        <ul class="space-y-2.5 text-sm">
          {{range .Worn}}{{template "itemrow" .}}{{end}}
        </ul>
        <h3 class="font-display text-lg font-bold mt-6 mb-3">Backpack</h3>
        {{else}}
        <h2 class="font-display text-xl font-bold mb-4">Backpack</h2>
        {{end}}
        {{if .Backpack}}
        <ul class="space-y-2.5 text-sm max-h-72 overflow-y-auto pr-1">
          {{range .Backpack}}{{template "itemrow" .}}{{end}}
        </ul>
        {{else}}
        <p class="text-sm text-[color:var(--ink)]/50">Backpack's empty.</p>
        {{end}}

        {{if .VaultRows}}
        <h3 class="font-display text-lg font-bold mt-6 mb-3">Vault</h3>
        <ul class="space-y-2.5 text-sm max-h-52 overflow-y-auto pr-1">
          {{range .VaultRows}}{{template "itemrow" .}}{{end}}
        </ul>
        {{end}}

        <!-- Changes you've asked for. The queue is honest: an equip lands on the
             game box's next poll, so a fresh order reads "queued", never "done".
             JS fills this from /api/equip/orders. -->
        <div id="equip-orders" class="mt-6 hidden">
          <h3 class="font-display text-lg font-bold mb-2">Pending changes</h3>
          <ul id="equip-orders-list" class="space-y-1.5 text-xs"></ul>
        </div>
      </div>
    </div>
  </section>
  {{end}}
</article>

<script>
// The sheet is state, like the board: keep an open tab honest without a reload.
// Public detail only (HP, room, supplies) — the private panels stay as rendered.
(function () {
  var root = document.getElementById('who');
  if (!root) return;
  var token = root.getAttribute('data-token');

  function setBar() {
    var el = document.getElementById('who-hp-bar');
    var hp = document.getElementById('who-hp');
    if (!el || !hp) return;
    var m = hp.textContent.match(/(-?\d+)\s*\/\s*(\d+)/);
    if (!m) return;
    var cur = parseInt(m[1], 10), max = parseInt(m[2], 10);
    var pct = max > 0 ? Math.max(0, Math.min(100, Math.round(cur / max * 100))) : 0;
    el.style.width = pct + '%';
  }
  setBar();

  function txt(id, v) { var el = document.getElementById(id); if (el && v != null) el.textContent = v; }

  var timer = null;
  function refresh() {
    fetch('/api/adventure/who/' + encodeURIComponent(token), { headers: { 'Accept': 'application/json' } })
      .then(function (r) { return r.ok ? r.json() : null; })
      .then(function (data) {
        if (!data) return;
        if (!data.live) {
          // Off the board entirely (expedition ended, opted out): nothing more to
          // poll for, so stop the timer rather than hammer a token that's gone.
          txt('who-where', 'Back in town');
          if (timer) clearInterval(timer);
          return;
        }
        var d = data.detail || {};
        var mark = data.mark || {};
        if (data.has_detail) {
          var temp = d.temp_hp ? ' +' + d.temp_hp : '';
          txt('who-hp', d.hp_current + ' / ' + d.hp_max + temp);
          txt('who-ac', d.armor_class);
          txt('who-room', d.room);
          txt('who-supplies', d.supplies);
          txt('who-threat', d.threat_level);
          setBar();
        }
        // Keep the location line honest in both directions: a mark that came back
        // to town while still on the board must stop showing its old expedition.
        if (mark.OnRun) {
          txt('who-where', mark.Where);
        } else {
          txt('who-where', 'In town' + (mark.Idle ? ' · ' + mark.Idle : ''));
        }
      })
      .catch(function () { /* transient — next tick will do */ });
  }
  timer = setInterval(refresh, 60000);
})();

// The equip queue, owner side. Clicking Equip / Take off records the intent; the
// item actually moves on the game box's next poll. So the UI never claims a change
// landed — it shows "queued" and lets the order's own status be the truth.
(function () {
  var panel = document.getElementById('gear-panel');
  if (!panel) return; // only the owner gets this panel at all
  var token = panel.getAttribute('data-token');
  var box = document.getElementById('equip-orders');
  var list = document.getElementById('equip-orders-list');

  // How each terminal status reads to the owner. gogobee only rejects when the
  // item slipped out from under the order or can't be worn — a bond-cap "inert"
  // is still an applied change, and its detail line says so.
  var STATUS = {
    pending: 'queued',
    applied: 'done',
    rejected_not_owned: "couldn't — that item had already moved",
    rejected_not_worn: "couldn't — that slot was already empty",
    rejected_not_equippable: "couldn't — that item can't be worn"
  };

  var pollTimer = null;

  function render(orders) {
    list.innerHTML = '';
    if (!orders || !orders.length) { box.classList.add('hidden'); return; }
    box.classList.remove('hidden');
    var anyPending = false;
    orders.forEach(function (o) {
      if (o.status === 'pending') anyPending = true;
      var li = document.createElement('li');
      li.className = 'flex items-baseline justify-between gap-3';
      var verb = o.action === 'equip' ? 'Equip' : 'Take off';
      var left = document.createElement('span');
      left.className = 'flex-1';
      left.textContent = verb + ' ' + (o.item_name || o.slot || 'item');
      var right = document.createElement('span');
      right.className = 'shrink-0 ' + (o.status === 'pending'
        ? 'text-[color:var(--ink)]/45'
        : (o.status === 'applied' ? 'text-theme-adventure font-semibold' : 'text-[color:var(--warn)]'));
      right.textContent = o.detail || STATUS[o.status] || o.status;
      li.appendChild(left); li.appendChild(right);
      list.appendChild(li);
    });
    // While anything is still queued, keep refreshing so the verdict lands without
    // a reload; stop once everything is terminal.
    if (anyPending && !pollTimer) {
      pollTimer = setInterval(loadOrders, 15000);
    } else if (!anyPending && pollTimer) {
      clearInterval(pollTimer); pollTimer = null;
    }
  }

  function loadOrders() {
    fetch('/api/equip/orders', { headers: { 'Accept': 'application/json' } })
      .then(function (r) { return r.ok ? r.json() : null; })
      .then(function (o) { if (o) render(o); })
      .catch(function () { /* transient — a later tick will do */ });
  }

  panel.addEventListener('click', function (e) {
    var btn = e.target.closest('.equip-btn');
    if (!btn || btn.disabled) return;
    btn.disabled = true;
    btn.classList.add('opacity-50');
    btn.textContent = 'queuing…';
    fetch('/api/equip/order', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        token: token,
        action: btn.getAttribute('data-action'),
        item_id: parseInt(btn.getAttribute('data-item-id') || '0', 10),
        slot: btn.getAttribute('data-slot') || ''
      })
    })
      .then(function (r) { return r.json().then(function (j) { return { ok: r.ok, body: j }; }); })
      .then(function (res) {
        if (!res.ok) {
          btn.disabled = false;
          btn.classList.remove('opacity-50');
          btn.textContent = (res.body && res.body.error) || 'try again';
          return;
        }
        btn.textContent = 'queued';
        loadOrders();
      })
      .catch(function () {
        btn.disabled = false;
        btn.classList.remove('opacity-50');
        btn.textContent = 'try again';
      });
  });

  loadOrders();
})();
</script>
{{end}}
