Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/monetize

crates/monetize-site-buy/tests/browser/page.html

<!doctype html>
<!-- **The buy page's markup, as the sites draw it** (Gunnar's `auth/signup.html` <main>, its
     look left out): every element the shared `buy.js` reads, for the crate's browser test.
     The `verdict` script reads what a buyer SEES (computed style, boxes), never the page's
     own claim about itself. -->
<html lang="en"><head><meta charset="utf-8"><title>Sign up</title>
<style>
  /* The sites' own layout rules that decide what a `hidden` element looks like (Gunnar's and
     Holger's signup.html, PAID-3): an author `display` beats the browser's own `[hidden]`, so
     a form drawn as a grid shows although it is `hidden`. */
  form { display: grid; grid-template-columns: 1fr 1fr; gap: .2rem 1rem; margin: 0; }
  .field[hidden] { display: none; }
</style></head>
<body>
<main class="entry">
    <h1 id="heading">Sign up for an account</h1>

    <!-- What this visitor may do here, as the server answered it. -->
    <div class="choice" id="choice" role="group" aria-label="What to do" hidden></div>

    <!-- The account's plans (owner 2026-10-15): drawn only when the account
         is chosen; the server picture below only for a private Gunnar. -->
    <div class="plans" id="plans" role="radiogroup" aria-label="Account plans" hidden></div>

    <div id="state" role="status" aria-live="polite" hidden></div>

    <form id="form" autocomplete="on" novalidate>
      <p class="hint" id="first-account" hidden></p>
      <div class="field" id="f-account">
        <label for="account">Account name</label>
        <input id="account" name="account" type="text" autocomplete="username" autocapitalize="none"
               spellcheck="false" maxlength="32" placeholder="e.g. alice" required>
        <p class="hint" id="account-hint"></p>
      </div>
      <div class="field" id="f-email">
        <label for="email" id="email-label">E-mail for your welcome mail</label>
        <input id="email" name="email" type="email" autocomplete="email" autocapitalize="none"
               spellcheck="false" maxlength="254" placeholder="you@example.com" required>
      </div>
      <div class="field" id="f-company">
        <label for="company">Company</label>
        <input id="company" name="company" type="text" autocapitalize="none" spellcheck="false"
               maxlength="16" placeholder="e.g. acme" required>
        <p class="hint" id="company-hint"></p>
      </div>
      <div class="field" id="f-domain">
        <label for="domain">Own domain (optional)</label>
        <input id="domain" name="domain" type="text" autocapitalize="none" spellcheck="false"
               placeholder="e.g. git.acme.com">
        <p class="hint"></p>
      </div>
    </form>

    <!-- ★ The picture: UI, SERVER and REPLICA — the front, main and twin —
         inside the WireGuard overlay, drawn as two dashed rings. Its labels
         are HOST_LABELS in the script (one line each). -->
    <figure class="estate" id="estate" aria-label="Gunnar instance protected by VPN">
      <svg id="pic" viewBox="0 0 640 150" role="img" aria-labelledby="pic-caption">
        <rect class="ring outer" id="ring-outer" x="4" y="4" width="632" height="122" rx="28"></rect>
        <rect class="ring inner" id="ring-inner" x="12" y="12" width="616" height="106" rx="21"></rect>
        <g id="host-front">
          <rect class="host" id="box-front" x="60" y="34" width="100" height="44" rx="7"></rect>
          <text class="label" id="label-front" x="110" y="60"></text>
          <text class="res" id="res-front" x="110" y="104"></text>
        </g>
        <g id="host-main">
          <rect class="host" id="box-main" x="262" y="30" width="116" height="52" rx="7"></rect>
          <rect class="slice" id="slice-main" x="263" y="70" width="20" height="11" hidden></rect>
          <text class="label" id="label-main" x="320" y="60"></text>
          <text class="res" id="res-main" x="320" y="104"></text>
        </g>
        <g id="host-twin">
          <rect class="host" id="box-twin" x="480" y="34" width="100" height="44" rx="7"></rect>
          <rect class="slice" id="slice-twin" x="481" y="66" width="20" height="11" hidden></rect>
          <text class="label" id="label-twin" x="530" y="60"></text>
          <text class="res" id="res-twin" x="530" y="104"></text>
        </g>
        <text class="caption" id="pic-caption" x="320" y="144">Gunnar instance protected by VPN</text>
      </svg>
    </figure>

    <!-- ONE − / + for the whole setup. -->
    <div class="size" id="size">
      <button type="button" id="minus" aria-label="Smaller">−</button>
      <div class="step" aria-live="polite">
        <strong id="step-name">…</strong>
        <span id="step-res"></span>
      </div>
      <button type="button" id="plus" aria-label="Bigger">+</button>
    </div>
    <p class="price" id="price"><strong id="price-month"></strong><span id="price-total"></span></p>

    <p class="coming" id="coming" hidden></p>
    <button id="go" class="primary" type="button" disabled>Pay</button>
    <div id="msg" class="msg" role="status" aria-live="polite"></div>
</main>
<pre id="verdict"></pre>
<script>
/*@buy-script*/
</script>
<script>
(function () {
  // What the buyer SEES: laid out boxes, never the `hidden` attribute's claim (PAID-3: a
  // `hidden` form a site's CSS draws as a grid is on the screen).
  const seen = (e) => !!e && getComputedStyle(e).visibility !== 'hidden' && e.getClientRects().length > 0;
  const verdict = () => {
    const shown = (id) => seen(document.getElementById(id));
    document.getElementById('verdict').textContent = JSON.stringify({
      buttons: [...document.querySelectorAll('button')].filter(seen).map(b => b.textContent.trim()),
      fields: [...document.querySelectorAll('input')].filter(seen).map(i => i.id),
      form: shown('form'), choice: shown('choice'), plans: shown('plans'), estate: shown('estate'),
      size: shown('size'), price: shown('price'), go: shown('go'),
      state: shown('state'), stateText: (document.getElementById('state').textContent || '').trim(),
      heading: document.getElementById('heading').textContent.trim(),
      labels: [...document.querySelectorAll('label')].filter(seen).map(l => l.textContent.trim()),
      choices: [...document.querySelectorAll('#choice button')].filter(seen).map(b => b.textContent.trim()),
      pressed: [...document.querySelectorAll('#choice button')].filter(b => b.getAttribute('aria-pressed') === 'true').map(b => b.textContent.trim()),
      first: seen(document.getElementById('first-account')) ? document.getElementById('first-account').textContent.trim() : '',
      hint: seen(document.getElementById('account-hint')) ? document.getElementById('account-hint').textContent.trim() : '',
      beta: [...document.querySelectorAll('#beta-line')].filter(seen).map(e => e.textContent.trim()).join(''),
      rows: [...document.querySelectorAll('#plans > *')].filter(seen).map(r => r.textContent.trim()),
      msg: (document.getElementById('msg').textContent || '').trim(),
      step: (document.getElementById('step-name').textContent || '').trim(),
      progress: [...document.querySelectorAll('#progress li')].filter(seen).map(li => ({ text: li.textContent.trim(), at: li.className })),
      links: [...document.querySelectorAll('#state a')].filter(seen).map(a => a.getAttribute('href')),
    });
  };
  setInterval(verdict, 250);
})();
</script>
</body></html>