import type { FC } from "hono/jsx";

export interface OnboardingV2Props {
  situation?: string;
  user?: { username: string };
}

export const OnboardingV2: FC<OnboardingV2Props> = (props) => {
  const initSit = props.situation ?? "broken-codebase";

  return (
    <>
      <style dangerouslySetInnerHTML={{ __html: css }} />
      <div class="ob-root" {...{ "data-init-situation": initSit }}>
        <header class="ob-header">
          <a href="/" class="ob-logo">
            <span class="ob-logo-mark"></span>gluecron
          </a>
          <span class="ob-sep">/</span>
          <span class="ob-page-label">Welcome</span>
          <span class="ob-tagline">First five minutes — the wizard adapts to who's arriving</span>
        </header>

        <main class="ob-main">
          <div class="ob-intro">
            <div class="ob-eyebrow">Adaptive onboarding · one flow, four shapes</div>
            <h1 class="ob-h1">Onboarding that reads the situation.</h1>
            <p class="ob-lead">
              The same signup senses who's arriving — a human with a broken repo, an agent teammate,
              a locked-down enterprise, or someone just browsing — and reshapes itself. Pick a situation
              to see the wizard adapt.
            </p>
          </div>

          <div class="ob-pills" id="ob-pills"></div>

          <div class="ob-grid">
            <section class="ob-wizard">
              <div class="ob-wiz-head" id="ob-wiz-head"></div>
              <div class="ob-wiz-steps" id="ob-wiz-steps"></div>
              <div class="ob-wiz-footer" id="ob-wiz-footer"></div>
            </section>

            <aside class="ob-rail">
              <div>
                <h3 class="ob-rail-hed">What the system sensed</h3>
                <div class="ob-sensed-card" id="ob-sensed-card"></div>
              </div>
              <div>
                <h3 class="ob-rail-hed">The three invariants</h3>
                <div class="ob-invs">
                  <div class="ob-inv">
                    <div class="ob-inv-title">Identity coherence</div>
                    <p class="ob-inv-body">
                      Playground → verified profile keeps your full sandbox history. Nothing is lost on upgrade.
                    </p>
                  </div>
                  <div class="ob-inv">
                    <div class="ob-inv-title">Telemetry handshake</div>
                    <p class="ob-inv-body">
                      Setup parameters log to flywheel telemetry from the first second — onboarding health is
                      measured, not assumed.
                    </p>
                  </div>
                  <div class="ob-inv">
                    <div class="ob-inv-title">Local sync rails</div>
                    <p class="ob-inv-body">
                      The setup wizard connects your machine straight to the sovereign array — no external edge
                      network in the path.
                    </p>
                  </div>
                </div>
              </div>
            </aside>
          </div>
        </main>
      </div>
      <script dangerouslySetInnerHTML={{ __html: js }} />
    </>
  );
};

export default OnboardingV2;

/* ─── styles ─────────────────────────────────────────────────────────────── */

const css = `
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;450;500;600;700&family=Inter+Tight:wght@500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap');

@keyframes gcPulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

*, *::before, *::after { box-sizing: border-box; }

.ob-root {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  font-size: 14px;
  line-height: 1.55;
  letter-spacing: -0.008em;
  color: #16181d;
  background: #fcfcfd;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ── header ─────────────────────────────────────────────────────────────── */
.ob-header {
  height: 56px;
  border-bottom: 1px solid rgba(22,24,29,0.07);
  background: #fcfcfd;
  display: flex;
  align-items: center;
  padding: 0 28px;
  gap: 20px;
  position: sticky;
  top: 0;
  z-index: 50;
}
.ob-logo {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: 'Inter Tight', sans-serif;
  font-weight: 600;
  font-size: 15px;
  letter-spacing: -0.02em;
  color: #16181d;
  text-decoration: none;
}
.ob-logo-mark {
  width: 16px;
  height: 16px;
  border-radius: 5px;
  background: #4353c9;
  display: inline-block;
  flex-shrink: 0;
}
.ob-sep { color: #c4c6cf; }
.ob-page-label { font-size: 13px; color: #16181d; font-weight: 500; }
.ob-tagline { margin-left: auto; font-size: 12.5px; color: #6b7080; }

/* ── main layout ─────────────────────────────────────────────────────────── */
.ob-main {
  flex: 1;
  max-width: 1180px;
  width: 100%;
  margin: 0 auto;
  padding: 44px 32px 88px;
}

/* ── intro ───────────────────────────────────────────────────────────────── */
.ob-intro { max-width: 680px; margin-bottom: 32px; }
.ob-eyebrow {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #8a8d99;
  margin-bottom: 10px;
}
.ob-h1 {
  font-family: 'Inter Tight', sans-serif;
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -0.025em;
  line-height: 1.15;
  margin: 0 0 8px;
  color: #111318;
}
.ob-lead { font-size: 14px; color: #6b7080; margin: 0; line-height: 1.6; }

/* ── situation pills ─────────────────────────────────────────────────────── */
.ob-pills { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 28px; }
.ob-pill {
  padding: 8px 16px;
  border-radius: 9999px;
  font-size: 13px;
  font-weight: 500;
  border: 1px solid rgba(22,24,29,0.12);
  background: #ffffff;
  color: #6b7080;
  cursor: pointer;
  font-family: inherit;
  white-space: nowrap;
  transition: border-color 0.12s;
}
.ob-pill:hover { border-color: rgba(22,24,29,0.30); }
.ob-pill--active {
  background: #16181d;
  color: #ffffff;
  border-color: #16181d;
  font-weight: 600;
}

/* ── two-col grid ────────────────────────────────────────────────────────── */
.ob-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(300px, 1fr);
  gap: 44px;
  align-items: start;
}

/* ── wizard card ─────────────────────────────────────────────────────────── */
.ob-wizard {
  border: 1px solid rgba(22,24,29,0.09);
  border-radius: 16px;
  background: #ffffff;
  overflow: hidden;
}

.ob-wiz-head {
  padding: 20px 26px;
  border-bottom: 1px solid rgba(22,24,29,0.06);
  background: #fcfcfd;
}
.ob-wiz-head-row { display: flex; align-items: center; gap: 10px; }
.ob-wiz-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
  animation: gcPulse 1.4s ease-in-out infinite;
}
.ob-wiz-title { font-size: 14.5px; font-weight: 600; color: #16181d; }
.ob-wiz-body { font-size: 13px; color: #6b7080; margin: 6px 0 0; line-height: 1.6; }

/* ── step timeline ───────────────────────────────────────────────────────── */
.ob-wiz-steps { padding: 22px 26px; display: flex; flex-direction: column; gap: 0; }
.ob-step {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr);
  gap: 16px;
  position: relative;
}
.ob-step:not(:last-child) { padding-bottom: 24px; }
.ob-step-track { display: flex; flex-direction: column; align-items: center; }
.ob-step-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-top: 6px;
  flex-shrink: 0;
}
.ob-step-line {
  width: 1px;
  flex: 1;
  background: rgba(22,24,29,0.08);
  margin-top: 6px;
}
.ob-step-title { font-size: 13.5px; font-weight: 600; color: #16181d; }
.ob-step-body {
  font-size: 13px;
  color: #6b7080;
  margin: 3px 0 0;
  line-height: 1.6;
  max-width: 56ch;
}

/* ── terminal snippet ────────────────────────────────────────────────────── */
.ob-term {
  margin: 12px 0 0;
  border: 1px solid rgba(22,24,29,0.9);
  border-radius: 10px;
  background: #0f1116;
  overflow: hidden;
}
.ob-term-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 14px;
  border-bottom: 1px solid rgba(255,255,255,0.07);
}
.ob-term-label {
  font-family: 'JetBrains Mono', monospace;
  font-size: 10.5px;
  color: #8a8d99;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.ob-copy-btn {
  padding: 4px 12px;
  border-radius: 6px;
  font-size: 11px;
  font-weight: 600;
  border: 1px solid rgba(255,255,255,0.14);
  background: transparent;
  color: #d6d8e0;
  cursor: pointer;
  font-family: inherit;
  transition: background 0.1s;
}
.ob-copy-btn:hover { background: rgba(255,255,255,0.06); }
.ob-term-body {
  padding: 12px 16px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 12px;
  line-height: 1.8;
  color: #d6d8e0;
}
.ob-term-prompt { color: #5f8fa0; }
.ob-term-comment { color: #8a8d99; }

/* ── connection status row ───────────────────────────────────────────────── */
.ob-conn {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  font-size: 12.5px;
}
.ob-conn-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.ob-conn-text { flex: 1; }
.ob-conn-btn {
  margin-left: 8px;
  padding: 3px 12px;
  border-radius: 6px;
  font-size: 11.5px;
  font-weight: 600;
  border: 1px solid rgba(22,24,29,0.14);
  background: #ffffff;
  color: #16181d;
  cursor: pointer;
  font-family: inherit;
  white-space: nowrap;
}
.ob-conn-btn:hover { background: #f5f5f7; }

/* ── wizard footer / CTA ─────────────────────────────────────────────────── */
.ob-wiz-footer {
  padding: 16px 26px;
  border-top: 1px solid rgba(22,24,29,0.06);
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.ob-cta-btn {
  padding: 9px 18px;
  border-radius: 9px;
  font-size: 13.5px;
  font-weight: 600;
  background: #16181d;
  color: #fff;
  text-decoration: none;
  white-space: nowrap;
  display: inline-block;
}
.ob-cta-btn:hover { opacity: 0.88; }
.ob-cta-note { font-size: 12px; color: #8a8d99; }

/* ── right rail ──────────────────────────────────────────────────────────── */
.ob-rail {
  display: flex;
  flex-direction: column;
  gap: 32px;
  position: sticky;
  top: 92px;
}
.ob-rail-hed {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #8a8d99;
  font-weight: 500;
  margin: 0 0 14px;
}

/* ── sensed card ─────────────────────────────────────────────────────────── */
.ob-sensed-card {
  border: 1px solid rgba(22,24,29,0.07);
  border-radius: 12px;
  background: #ffffff;
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 11px;
}
.ob-sensed-row { display: flex; justify-content: space-between; gap: 12px; font-size: 12.5px; }
.ob-sensed-k { color: #6b7080; }
.ob-sensed-v {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11.5px;
  color: #16181d;
  text-align: right;
}

/* ── invariant cards ─────────────────────────────────────────────────────── */
.ob-invs { display: flex; flex-direction: column; gap: 10px; }
.ob-inv {
  border: 1px solid rgba(22,24,29,0.07);
  border-radius: 12px;
  background: #ffffff;
  padding: 14px 18px;
}
.ob-inv-title { font-size: 13px; font-weight: 600; color: #16181d; margin-bottom: 3px; }
.ob-inv-body { font-size: 12px; color: #6b7080; margin: 0; line-height: 1.55; }

/* ── responsive ──────────────────────────────────────────────────────────── */
@media (max-width: 860px) {
  .ob-grid { grid-template-columns: 1fr; }
  .ob-rail { position: static; }
  .ob-tagline { display: none; }
}
`;

/* ─── client script ───────────────────────────────────────────────────────── */

const js = `(function () {
  var GREEN   = '#1e7f5c';
  var AMBER   = '#b45309';
  var INDIGO  = '#4353c9';
  var INK     = '#16181d';
  var GREY    = '#c4c6cf';
  var PULSE   = 'gcPulse 1.4s ease-in-out infinite';

  var SITUATIONS = [
    {
      id: 'broken-codebase',
      label: 'Broken codebase import',
      headDot: AMBER,
      headTitle: 'Auto-Repair Mode engaged',
      headBody: 'Your import arrived with 7 failing tests and a missing config. Instead of a red build bar, your first experience is a fix.',
      steps: [
        {
          title: 'Repo imported · full history + branches',
          body: '142 commits, 3 branches, semantic index building in the background.',
          dot: GREEN, snippet: false
        },
        {
          title: 'Archaeology scan complete',
          body: 'Tech-debt hotspots flagged, symbols mapped, architecture topology drawn — your team gets the map on day one.',
          dot: GREEN, snippet: false
        },
        {
          title: 'Auto-repair drafted your first patch',
          body: 'The healer reproduced the failures in a sandbox and drafted a fix for the missing config and 5 of 7 tests. Review it like any PR — or fix it manually.',
          dot: AMBER, snippet: false
        },
        {
          title: 'One-shot local setup',
          body: 'Configures git, injects host keys, and tests the connection — no manual terminal spelunking.',
          dot: INK, snippet: true
        }
      ],
      ctaLabel: 'Review your first repair PR',
      ctaHref: '/',
      ctaNote: 'Nothing merges without you — the manual diff is one click away.',
      sensed: [
        { k: 'Import source', v: 'github mirror' },
        { k: 'Build status',  v: '7 tests failing' },
        { k: 'Config',        v: '.env.example missing' },
        { k: 'Flow selected', v: 'auto-repair mode' }
      ]
    },
    {
      id: 'agent-teammate',
      label: 'Agent teammate',
      headDot: INDIGO,
      headTitle: 'Machine user provisioning',
      headBody: 'A non-human teammate is joining. No email drips, no UI tours — scoped credentials and machine-speed rails, immediately.',
      steps: [
        {
          title: 'Bot identity created · migration-pilot',
          body: 'Scoped API token minted with exactly the permissions its manifest declares — nothing more.',
          dot: GREEN, snippet: false
        },
        {
          title: 'Personal semantic space seeded',
          body: 'The agent gets its own index scope so its queries never leak across tenants.',
          dot: GREEN, snippet: false
        },
        {
          title: 'Fast-lane execution rights',
          body: 'Trusted-agent patches skip human-task scheduling; agent-to-agent verification runs at machine speed — still gated, still audited.',
          dot: INDIGO, snippet: false
        },
        {
          title: 'Reversibility contract signed',
          body: 'Every action the agent takes obeys the same one-click-undo guarantee as ours. No exceptions for bots.',
          dot: INK, snippet: false
        }
      ],
      ctaLabel: 'See agents at work',
      ctaHref: '/',
      ctaNote: 'Every agent action lands in the same audit trail as human ones.',
      sensed: [
        { k: 'Account type',     v: 'machine user' },
        { k: 'Manifest scopes',  v: 'repo:write, pr:open' },
        { k: 'Drip sequence',    v: 'skipped' },
        { k: 'Flow selected',    v: 'fast-lane provisioning' }
      ]
    },
    {
      id: 'air-gapped-enterprise',
      label: 'Air-gapped enterprise',
      headDot: INK,
      headTitle: 'Sovereign tenant setup',
      headBody: 'This tenant is flagged bare-metal / private-cloud. Public identity options are gone; everything stays inside your network boundary.',
      steps: [
        {
          title: 'Public OAuth removed',
          body: 'GitHub and Google sign-in don\'t appear. Mandatory SSH key + passkey registration, enforced at the door.',
          dot: INK, snippet: false
        },
        {
          title: 'Local signing keys seeded',
          body: 'Commit and artifact signing keys generated on your hardware — verification never phones home.',
          dot: GREEN, snippet: false
        },
        {
          title: 'OCI registry fenced to your perimeter',
          body: 'Container builds, package caches, and signed binaries route entirely inside your Caddy boundary. Zero external CDN or proxy.',
          dot: GREEN, snippet: false
        },
        {
          title: 'Air-gapped sync scheduled',
          body: 'Repos, semantic indices, and registries replicate hourly to your offline array.',
          dot: INK, snippet: false
        }
      ],
      ctaLabel: 'Inspect the sovereign stack',
      ctaHref: '/',
      ctaNote: 'All 13 production layers, self-owned and self-healing.',
      sensed: [
        { k: 'Tenant flag',    v: 'air-gapped' },
        { k: 'Public OAuth',   v: 'disabled' },
        { k: 'Registry scope', v: 'local network only' },
        { k: 'Flow selected',  v: 'sovereign setup' }
      ]
    },
    {
      id: 'just-browsing',
      label: 'Just browsing',
      headDot: GREEN,
      headTitle: 'Playground account · zero strings',
      headBody: 'No migration, no commitment. An ephemeral, isolated sandbox with everything switched on — expires on its own unless you keep it.',
      steps: [
        {
          title: 'Instant sandbox account',
          body: 'No email required. A demo repo with real history, gates, and an agent ready to talk.',
          dot: GREEN, snippet: false
        },
        {
          title: 'Try the whole loop',
          body: 'Push a change, watch gates fire at the wire, see Sonnet 5 review it, break something and watch the repair.',
          dot: GREEN, snippet: false
        },
        {
          title: 'Keep it when you\'re ready',
          body: 'Claiming your account bridges everything — your sandbox history, repos, and sessions come with you. Nothing resets.',
          dot: INDIGO, snippet: false
        }
      ],
      ctaLabel: 'Enter the playground',
      ctaHref: '/',
      ctaNote: 'Expires in 24h unless claimed — claim keeps all history.',
      sensed: [
        { k: 'Signup',       v: 'none — anonymous' },
        { k: 'Environment',  v: 'ephemeral sandbox' },
        { k: 'Expiry',       v: '24h · claimable' },
        { k: 'Flow selected', v: 'playground' }
      ]
    }
  ];

  /* ── state ─────────────────────────────────────────────────────────────── */
  var state = { situation: 0, copied: false, conn: 'idle' };

  var root = document.querySelector('.ob-root');
  if (root) {
    var initSit = root.getAttribute('data-init-situation') || 'broken-codebase';
    for (var si = 0; si < SITUATIONS.length; si++) {
      if (SITUATIONS[si].id === initSit) { state.situation = si; break; }
    }
  }

  function setState(patch) {
    for (var k in patch) { if (Object.prototype.hasOwnProperty.call(patch, k)) state[k] = patch[k]; }
    render();
  }

  /* ── html escaping ─────────────────────────────────────────────────────── */
  function esc(str) {
    return String(str)
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;');
  }

  /* ── render pills ──────────────────────────────────────────────────────── */
  function renderPills() {
    var el = document.getElementById('ob-pills');
    if (!el) return;
    var html = '';
    for (var i = 0; i < SITUATIONS.length; i++) {
      var active = i === state.situation;
      html += '<button type="button" class="ob-pill' + (active ? ' ob-pill--active' : '') +
              '" data-sit="' + i + '">' + esc(SITUATIONS[i].label) + '</button>';
    }
    el.innerHTML = html;
    var pills = el.querySelectorAll('.ob-pill');
    for (var j = 0; j < pills.length; j++) {
      (function (pill) {
        pill.addEventListener('click', function () {
          setState({ situation: parseInt(pill.getAttribute('data-sit'), 10), copied: false, conn: 'idle' });
        });
      })(pills[j]);
    }
  }

  /* ── render wizard header ──────────────────────────────────────────────── */
  function renderWizHead() {
    var el = document.getElementById('ob-wiz-head');
    if (!el) return;
    var d = SITUATIONS[state.situation];
    el.innerHTML =
      '<div class="ob-wiz-head-row">' +
        '<span class="ob-wiz-dot" style="background:' + d.headDot + '"></span>' +
        '<span class="ob-wiz-title">' + esc(d.headTitle) + '</span>' +
      '</div>' +
      '<p class="ob-wiz-body">' + esc(d.headBody) + '</p>';
  }

  /* ── render steps ──────────────────────────────────────────────────────── */
  function renderSteps() {
    var el = document.getElementById('ob-wiz-steps');
    if (!el) return;
    var d = SITUATIONS[state.situation];
    var html = '';

    for (var i = 0; i < d.steps.length; i++) {
      var s = d.steps[i];
      var isLast = i === d.steps.length - 1;
      var snippetHtml = '';

      if (s.snippet) {
        var copyLabel = state.copied ? 'Copied ✓' : 'Copy';
        var connLabel = state.conn === 'ok'
          ? 'SSH + OAuth verified — direct to sovereign array, no external edge'
          : state.conn === 'testing'
          ? 'Testing local → sovereign connection…'
          : 'Connection not yet tested';
        var connDot   = state.conn === 'ok' ? GREEN : state.conn === 'testing' ? AMBER : GREY;
        var connColor = state.conn === 'ok' ? GREEN : '#6b7080';
        var connAnim  = state.conn === 'testing' ? PULSE : 'none';
        var testLabel = state.conn === 'testing' ? 'Testing…'
                      : state.conn === 'ok'      ? 'Re-test'
                      : 'Test connection';

        snippetHtml =
          '<div class="ob-term">' +
            '<div class="ob-term-bar">' +
              '<span class="ob-term-label">one-shot local setup</span>' +
              '<button type="button" class="ob-copy-btn" id="ob-copy-btn">' + esc(copyLabel) + '</button>' +
            '</div>' +
            '<div class="ob-term-body">' +
              '<div><span class="ob-term-prompt">$</span> curl -sSL gluecron.com/setup | bash</div>' +
              '<div class="ob-term-comment"># configures git · injects host keys · tests the connection</div>' +
            '</div>' +
          '</div>' +
          '<div class="ob-conn" style="color:' + connColor + '">' +
            '<span class="ob-conn-dot" style="background:' + connDot + ';animation:' + connAnim + '"></span>' +
            '<span class="ob-conn-text">' + esc(connLabel) + '</span>' +
            '<button type="button" class="ob-conn-btn" id="ob-conn-btn">' + esc(testLabel) + '</button>' +
          '</div>';
      }

      html +=
        '<div class="ob-step">' +
          '<div class="ob-step-track">' +
            '<span class="ob-step-dot" style="background:' + s.dot + '"></span>' +
            (!isLast ? '<span class="ob-step-line"></span>' : '') +
          '</div>' +
          '<div>' +
            '<div class="ob-step-title">' + esc(s.title) + '</div>' +
            '<p class="ob-step-body">' + esc(s.body) + '</p>' +
            snippetHtml +
          '</div>' +
        '</div>';
    }

    el.innerHTML = html;

    /* copy button */
    var copyBtn = document.getElementById('ob-copy-btn');
    if (copyBtn) {
      copyBtn.addEventListener('click', function () {
        try { navigator.clipboard.writeText('curl -sSL https://gluecron.com/setup | bash'); } catch (e) {}
        setState({ copied: true });
        setTimeout(function () { setState({ copied: false }); }, 1200);
      });
    }

    /* test-connection button */
    var connBtn = document.getElementById('ob-conn-btn');
    if (connBtn) {
      connBtn.addEventListener('click', function () {
        if (state.conn === 'testing') return;
        setState({ conn: 'testing' });
        setTimeout(function () { setState({ conn: 'ok' }); }, 1600);
      });
    }
  }

  /* ── render footer / CTA ───────────────────────────────────────────────── */
  function renderFooter() {
    var el = document.getElementById('ob-wiz-footer');
    if (!el) return;
    var d = SITUATIONS[state.situation];
    el.innerHTML =
      '<a href="' + esc(d.ctaHref) + '" class="ob-cta-btn">' + esc(d.ctaLabel) + '</a>' +
      '<span class="ob-cta-note">' + esc(d.ctaNote) + '</span>';
  }

  /* ── render sensed card ────────────────────────────────────────────────── */
  function renderSensed() {
    var el = document.getElementById('ob-sensed-card');
    if (!el) return;
    var d = SITUATIONS[state.situation];
    var html = '';
    for (var i = 0; i < d.sensed.length; i++) {
      html +=
        '<div class="ob-sensed-row">' +
          '<span class="ob-sensed-k">' + esc(d.sensed[i].k) + '</span>' +
          '<span class="ob-sensed-v">' + esc(d.sensed[i].v) + '</span>' +
        '</div>';
    }
    el.innerHTML = html;
  }

  /* ── full render ───────────────────────────────────────────────────────── */
  function render() {
    renderPills();
    renderWizHead();
    renderSteps();
    renderFooter();
    renderSensed();
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', render);
  } else {
    render();
  }
})();`;
