{{define "title"}}{{.Channel.Title}} — {{.SiteTitle}}{{end}}

{{define "main"}}
<section class="mt-2 mb-8">
  <div class="rounded-3xl bg-theme-{{.Channel.Theme}} 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">{{.Channel.Emoji}}</div>
    <div class="relative">
      <p class="text-sm uppercase tracking-[0.2em] opacity-80">section</p>
      <h1 class="font-display text-4xl sm:text-5xl font-bold mt-1">{{.Channel.Title}}</h1>
      <p class="mt-2 max-w-xl text-white/85">{{.Channel.Blurb}}</p>
      <p class="mt-4 text-xs uppercase tracking-wider opacity-75">{{.Total}} stories archived</p>
    </div>
  </div>
</section>

{{if .ShowRoster}}
<section class="mb-10" id="roster" data-stale="{{.RosterStale}}">
  <div class="flex items-baseline justify-between mb-3">
    <h2 class="font-display text-2xl font-bold">Out there right now</h2>
    <p class="text-xs uppercase tracking-wider text-[color:var(--ink)]/50" id="roster-status">
      {{if .RosterStale}}last known — the wire's gone quiet{{else}}live{{end}}
    </p>
  </div>

  <div class="rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 shadow-pete overflow-hidden {{if .RosterStale}}opacity-60{{end}}" id="roster-card">
    <ul class="divide-y divide-[color:var(--ink)]/10" id="roster-list">
      {{range .Roster}}
      <li class="flex items-center gap-4 px-5 py-3" data-token="{{.Token}}" data-name="{{.Name}}">
        <span class="text-lg" aria-hidden="true">{{if .OnRun}}⚔{{else}}🏠{{end}}</span>
        <a href="/adventure/who/{{.Token}}" class="font-semibold hover:text-theme-adventure hover:underline">{{.Name}}</a>
        <span class="text-sm text-[color:var(--ink)]/60">lv {{.Level}} {{.ClassRace}}</span>
        <span class="ml-auto text-sm {{if .OnRun}}font-semibold{{else}}text-[color:var(--ink)]/60{{end}}">
          {{.Where}}{{if .Idle}} <span class="text-[color:var(--ink)]/45">· {{.Idle}}</span>{{end}}
        </span>
        {{if and $.User .OnRun}}
        <button type="button" class="mischief-send shrink-0 rounded-full bg-[color:var(--ink)]/5 hover:bg-red-500 hover:text-white border border-[color:var(--ink)]/15 px-3 py-1 text-xs font-semibold transition" data-token="{{.Token}}" data-name="{{.Name}}">send trouble</button>
        {{end}}
      </li>
      {{else}}
      <li class="px-5 py-8 text-center text-[color:var(--ink)]/60">Nobody's out at the moment. Quiet week in the realm.</li>
      {{end}}
    </ul>
  </div>

  {{if not .User}}
  <p class="mt-3 text-xs text-[color:var(--ink)]/50">
    <a href="/auth/login?next={{.Path}}" class="underline hover:text-red-500">Sign in</a> to send something unpleasant their way.
  </p>
  {{end}}

  {{if .User}}
  <!-- Your open contracts. Rendered from filed facts, never from live game state:
       Pete only knows what gogobee told it happened. -->
  <div id="mischief-orders" class="mt-4 hidden">
    <h3 class="text-xs uppercase tracking-wider text-[color:var(--ink)]/50 mb-2">Trouble you've paid for</h3>
    <ul id="mischief-orders-list" class="space-y-2 text-sm"></ul>
  </div>

  <!-- The buy dialog. Prices are gogobee's, pulled live from /api/mischief/catalog;
       nothing here asserts a number of its own. -->
  <div id="mischief-modal" class="fixed inset-0 z-50 hidden items-center justify-center bg-black/50 p-4">
    <div class="w-full max-w-md rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 shadow-pete overflow-hidden">
      <div class="flex items-center justify-between px-5 py-4 border-b border-[color:var(--ink)]/10">
        <h3 class="font-display text-xl font-bold">Send trouble to <span id="mischief-target">—</span></h3>
        <button type="button" id="mischief-close" class="text-2xl leading-none text-[color:var(--ink)]/50 hover:text-[color:var(--ink)]" aria-label="close">×</button>
      </div>
      <div class="px-5 py-4 space-y-3">
        <p class="text-sm text-[color:var(--ink)]/60">Pick how bad. They won't know who sent it — unless you sign it, or they live to read the receipt.</p>
        <div id="mischief-tiers" class="space-y-2"></div>
        <label class="flex items-center gap-2 text-sm">
          <input type="checkbox" id="mischief-signed" class="rounded border-[color:var(--ink)]/30">
          <span>Sign my name to it <span class="text-[color:var(--ink)]/45">(+25%, and they'll know)</span></span>
        </label>
        <p id="mischief-balance" class="text-xs text-[color:var(--ink)]/50"></p>
        <p id="mischief-result" class="text-sm hidden"></p>
      </div>
      <div class="flex items-center justify-end gap-2 px-5 py-4 border-t border-[color:var(--ink)]/10">
        <button type="button" id="mischief-cancel" class="rounded-full px-4 py-2 text-sm font-semibold text-[color:var(--ink)]/60 hover:text-[color:var(--ink)]">Never mind</button>
        <button type="button" id="mischief-confirm" class="rounded-full bg-red-500 text-white px-5 py-2 text-sm font-semibold shadow-pete hover:-translate-y-0.5 transition disabled:opacity-40 disabled:translate-y-0" disabled>Put the word out</button>
      </div>
    </div>
  </div>
  {{end}}
</section>

<script>
// The board is state, not a story: an open tab should keep telling the truth
// without a reload. Cheap poll — a handful of rows, no auth, no cache.
(function () {
  var list = document.getElementById('roster-list');
  var status = document.getElementById('roster-status');
  var card = document.getElementById('roster-card');
  if (!list) return;

  var signedIn = {{if .User}}true{{else}}false{{end}};

  function esc(s) {
    var d = document.createElement('div');
    d.textContent = s == null ? '' : String(s);
    return d.innerHTML;
  }

  function row(a) {
    var idle = a.Idle ? ' <span class="text-[color:var(--ink)]/45">· ' + esc(a.Idle) + '</span>' : '';
    var button = (signedIn && a.OnRun)
      ? '<button type="button" class="mischief-send shrink-0 rounded-full bg-[color:var(--ink)]/5 hover:bg-red-500 hover:text-white border border-[color:var(--ink)]/15 px-3 py-1 text-xs font-semibold transition" data-token="' + esc(a.Token) + '" data-name="' + esc(a.Name) + '">send trouble</button>'
      : '';
    return '<li class="flex items-center gap-4 px-5 py-3" data-token="' + esc(a.Token) + '" data-name="' + esc(a.Name) + '">' +
      '<span class="text-lg" aria-hidden="true">' + (a.OnRun ? '⚔' : '🏠') + '</span>' +
      '<a href="/adventure/who/' + esc(a.Token) + '" class="font-semibold hover:text-theme-adventure hover:underline">' + esc(a.Name) + '</a>' +
      '<span class="text-sm text-[color:var(--ink)]/60">lv ' + esc(a.Level) + ' ' + esc(a.ClassRace) + '</span>' +
      '<span class="ml-auto text-sm ' + (a.OnRun ? 'font-semibold' : 'text-[color:var(--ink)]/60') + '">' +
      esc(a.Where) + idle + '</span>' + button + '</li>';
  }

  function refresh() {
    fetch('/api/roster', { headers: { 'Accept': 'application/json' } })
      .then(function (r) { return r.ok ? r.json() : null; })
      .then(function (data) {
        if (!data) return;
        var rows = data.adventurers || [];
        list.innerHTML = rows.length
          ? rows.map(row).join('')
          : '<li class="px-5 py-8 text-center text-[color:var(--ink)]/60">Nobody\'s out at the moment. Quiet week in the realm.</li>';
        status.textContent = data.stale ? "last known — the wire's gone quiet" : 'live';
        card.classList.toggle('opacity-60', !!data.stale);
      })
      .catch(function () { /* transient — the next tick will do */ });
  }

  setInterval(refresh, 60000);
  document.addEventListener('visibilitychange', function () {
    if (!document.hidden) refresh();
  });

  if (signedIn) initMischief(list, esc);
})();

// The storefront: pick a mark, pick how bad, pay. The only thing this code is
// authoritative about is the buyer's intent — every price, every yes/no, and the
// buyer's actual balance are gogobee's, reached through the API.
function initMischief(list, esc) {
  var modal = document.getElementById('mischief-modal');
  if (!modal) return;
  var targetEl = document.getElementById('mischief-target');
  var tiersEl = document.getElementById('mischief-tiers');
  var signedEl = document.getElementById('mischief-signed');
  var balanceEl = document.getElementById('mischief-balance');
  var resultEl = document.getElementById('mischief-result');
  var confirmEl = document.getElementById('mischief-confirm');
  var ordersWrap = document.getElementById('mischief-orders');
  var ordersList = document.getElementById('mischief-orders-list');

  var catalog = null;   // {tiers, euro, has_balance}
  var chosen = null;    // selected tier key
  var current = null;   // {token, name}

  function euros(n) { return '€' + Number(n).toLocaleString(); }

  function loadCatalog() {
    return fetch('/api/mischief/catalog', { headers: { 'Accept': 'application/json' } })
      .then(function (r) { return r.ok ? r.json() : null; })
      .then(function (c) { catalog = c; return c; });
  }

  function drawTiers() {
    if (!catalog || !catalog.tiers || !catalog.tiers.length) {
      tiersEl.innerHTML = '<p class="text-sm text-[color:var(--ink)]/50">The catalogue is between snapshots. Try again in a moment.</p>';
      return;
    }
    var signed = signedEl.checked;
    var known = catalog.has_balance;
    var euro = catalog.euro;
    tiersEl.innerHTML = catalog.tiers.map(function (t) {
      var price = signed ? t.signed_fee : t.fee;
      var tooDear = known && euro < price;
      return '<button type="button" class="mischief-tier w-full text-left rounded-2xl border-2 px-4 py-2 transition ' +
        (chosen === t.key ? 'border-red-500 bg-red-500/10' : 'border-[color:var(--ink)]/10 hover:border-[color:var(--ink)]/25') +
        (tooDear ? ' opacity-40 cursor-not-allowed' : '') + '" data-key="' + esc(t.key) + '"' + (tooDear ? ' disabled' : '') + '>' +
        '<span class="flex items-baseline justify-between"><span class="font-semibold">' + esc(t.display) + '</span>' +
        '<span class="font-semibold">' + euros(price) + '</span></span>' +
        (t.blurb ? '<span class="block text-xs text-[color:var(--ink)]/55 mt-0.5">' + esc(t.blurb) + '</span>' : '') +
        '</button>';
    }).join('');
    balanceEl.textContent = known ? 'You have about ' + euros(euro) + ' to spend.' : '';
    Array.prototype.forEach.call(tiersEl.querySelectorAll('.mischief-tier'), function (b) {
      b.addEventListener('click', function () {
        chosen = b.getAttribute('data-key');
        confirmEl.disabled = false;
        drawTiers();
      });
    });
  }

  function open(token, name) {
    current = { token: token, name: name };
    chosen = null;
    confirmEl.disabled = true;
    resultEl.classList.add('hidden');
    resultEl.textContent = '';
    signedEl.checked = false;
    targetEl.textContent = name;
    modal.classList.remove('hidden');
    modal.classList.add('flex');
    (catalog ? Promise.resolve() : loadCatalog()).then(drawTiers);
  }

  function close() {
    modal.classList.add('hidden');
    modal.classList.remove('flex');
  }

  function badge(statusText) {
    var map = {
      pending: ['on its way', 'bg-amber-500/15 text-amber-700'],
      placed: ['out for delivery', 'bg-red-500/15 text-red-600'],
      bounced_funds: ["couldn't afford it", 'bg-[color:var(--ink)]/10 text-[color:var(--ink)]/60'],
      bounced_ineligible: ['no longer a mark', 'bg-[color:var(--ink)]/10 text-[color:var(--ink)]/60']
    };
    var m = map[statusText] || [statusText, 'bg-[color:var(--ink)]/10 text-[color:var(--ink)]/60'];
    return '<span class="rounded-full px-2 py-0.5 text-xs font-semibold ' + m[1] + '">' + esc(m[0]) + '</span>';
  }

  function loadOrders() {
    fetch('/api/mischief/orders', { headers: { 'Accept': 'application/json' } })
      .then(function (r) { return r.ok ? r.json() : null; })
      .then(function (orders) {
        if (!orders || !orders.length) { ordersWrap.classList.add('hidden'); return; }
        ordersWrap.classList.remove('hidden');
        ordersList.innerHTML = orders.map(function (o) {
          var detail = o.detail ? ' <span class="text-[color:var(--ink)]/45">· ' + esc(o.detail) + '</span>' : '';
          return '<li class="flex items-center gap-3">' +
            '<span class="font-semibold">' + esc(o.target_name) + '</span>' +
            '<span class="text-[color:var(--ink)]/55">' + esc(o.tier) + (o.signed ? ', signed' : '') + '</span>' +
            '<span class="ml-auto">' + badge(o.status) + '</span>' + detail + '</li>';
        }).join('');
      })
      .catch(function () {});
  }

  list.addEventListener('click', function (e) {
    var b = e.target.closest('.mischief-send');
    if (!b) return;
    open(b.getAttribute('data-token'), b.getAttribute('data-name'));
  });
  signedEl.addEventListener('change', drawTiers);
  document.getElementById('mischief-close').addEventListener('click', close);
  document.getElementById('mischief-cancel').addEventListener('click', close);
  modal.addEventListener('click', function (e) { if (e.target === modal) close(); });

  confirmEl.addEventListener('click', function () {
    if (!current || !chosen) return;
    confirmEl.disabled = true;
    resultEl.classList.add('hidden');
    fetch('/api/mischief/order', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ target_token: current.token, tier: chosen, signed: signedEl.checked })
    })
      .then(function (r) { return r.json().then(function (b) { return { ok: r.ok, body: b }; }); })
      .then(function (res) {
        resultEl.classList.remove('hidden');
        if (res.ok) {
          resultEl.className = 'text-sm text-red-600 font-semibold';
          resultEl.textContent = 'The word is out. Watch the board.';
          catalog = null; // balance moved; refetch next open
          loadOrders();
          setTimeout(close, 1200);
        } else {
          resultEl.className = 'text-sm text-[color:var(--ink)]/70';
          resultEl.textContent = (res.body && res.body.error) || 'That didn\'t take. Try again.';
          confirmEl.disabled = false;
        }
      })
      .catch(function () {
        resultEl.classList.remove('hidden');
        resultEl.className = 'text-sm text-[color:var(--ink)]/70';
        resultEl.textContent = 'The line dropped. Try again.';
        confirmEl.disabled = false;
      });
  });

  loadOrders();
  setInterval(loadOrders, 60000);
}
</script>
{{end}}

{{if .Stories}}
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5">
  {{range .Stories}}
    {{template "card" dict "Story" . "Theme" $.Channel.Theme}}
  {{end}}
</div>

<nav class="mt-10 flex items-center justify-between">
  {{if .HasPrev}}
    <a href="{{.PrevURL}}" class="rounded-full bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 px-5 py-2 font-semibold shadow-pete hover:-translate-y-0.5 transition">← newer</a>
  {{else}}<span></span>{{end}}
  <span class="text-sm text-[color:var(--ink)]/60">page {{.Page}}</span>
  {{if .HasNext}}
    <a href="{{.NextURL}}" class="rounded-full bg-theme-{{.Channel.Theme}} text-white px-5 py-2 font-semibold shadow-pete hover:-translate-y-0.5 transition">older →</a>
  {{else}}<span></span>{{end}}
</nav>
{{else}}
<div class="rounded-3xl bg-[color:var(--card)] border-2 border-dashed border-[color:var(--ink)]/15 p-10 text-center text-[color:var(--ink)]/60">
  Pete hasn't catalogued anything for {{.Channel.Title}} yet.
</div>
{{end}}
{{end}}
