<!DOCTYPE html>
<!--
  Familiar - Self-hosted AI Agent
  Copyright (c) 2026 George Scott Foley
  Licensed under the MIT License
-->
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Familiar — Setup</title>
    <style>
        :root {
            --bg-dark: #0f0f1a;
            --bg-card: #1a1a2e;
            --bg-input: #252540;
            --accent: #e94560;
            --accent-hover: #ff6b6b;
            --text: #eaeaea;
            --text-dim: #888;
            --success: #4ade80;
            --warning: #fbbf24;
            --error: #f87171;
            --border: #333;
        }

        * { margin: 0; padding: 0; box-sizing: border-box; }

        body {
            font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
            background: var(--bg-dark);
            color: var(--text);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: flex-start;
            padding: 40px 20px;
        }

        .wizard {
            max-width: 640px;
            width: 100%;
        }

        /* Progress Bar */
        .progress-bar {
            display: flex;
            gap: 6px;
            margin-bottom: 40px;
        }

        .progress-bar .step {
            flex: 1;
            height: 4px;
            background: var(--bg-input);
            border-radius: 2px;
            transition: background 0.3s;
        }

        .progress-bar .step.active { background: var(--accent); }
        .progress-bar .step.done { background: var(--success); }

        /* Step Container */
        .step-content { display: none; }
        .step-content.active { display: block; }

        /* Header */
        .step-header {
            text-align: center;
            margin-bottom: 32px;
            position: relative;
        }
        .onboard-hat-btn {
            position: absolute;
            top: 0;
            right: 0;
            background: transparent;
            border: 1px solid var(--accent);
            color: var(--accent);
            border-radius: 8px;
            padding: 6px 12px;
            cursor: pointer;
            font-size: 0.8rem;
            transition: all 0.2s;
        }
        .onboard-hat-btn:hover {
            background: var(--accent);
            color: white;
        }

        .step-header h2 {
            font-size: 1.5rem;
            margin-bottom: 8px;
        }

        .step-header p {
            color: var(--text-dim);
            font-size: 0.95rem;
            line-height: 1.5;
        }

        /* Logo */
        .logo-banner {
            font-family: monospace;
            font-size: 1.6rem;
            font-weight: 700;
            letter-spacing: 0.12em;
            text-align: center;
            margin-bottom: 8px;
        }

        .logo-cyan { color: #00e5ff; }
        .logo-blue { color: #5c6bc0; }
        .logo-magenta { color: #e040fb; }

        .logo-divider {
            border: none;
            border-top: 1px solid #00e5ff33;
            max-width: 400px;
            margin: 16px auto;
        }

        .logo-tagline {
            text-align: center;
            color: var(--text-dim);
            font-size: 0.9rem;
            margin-bottom: 32px;
        }

        /* Constitution */
        .constitution-article {
            background: var(--bg-card);
            border-radius: 8px;
            padding: 16px 20px;
            margin-bottom: 12px;
            border-left: 3px solid #e040fb;
        }

        .constitution-article h4 {
            color: #00e5ff;
            font-size: 0.9rem;
            margin-bottom: 6px;
        }

        .constitution-article p {
            color: var(--text-dim);
            font-size: 0.85rem;
            line-height: 1.5;
        }

        .consent-row {
            display: flex;
            align-items: center;
            gap: 12px;
            padding: 16px 0;
        }

        .consent-row input[type="checkbox"] {
            width: 20px;
            height: 20px;
            accent-color: var(--accent);
            cursor: pointer;
        }

        .consent-row label {
            color: var(--text);
            font-size: 0.95rem;
            cursor: pointer;
        }

        /* Species grid */
        .species-grid {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 12px;
            margin-bottom: 24px;
        }

        .species-card {
            background: var(--bg-card);
            border: 2px solid transparent;
            border-radius: 12px;
            padding: 16px;
            cursor: pointer;
            text-align: center;
            transition: border-color 0.2s, transform 0.1s;
        }

        .species-card:hover { border-color: var(--accent); transform: translateY(-1px); }
        .species-card.selected { border-color: var(--accent); background: #1e1e36; }

        .species-preview {
            font-family: monospace;
            white-space: pre;
            color: var(--text-dim);
            font-size: 0.65rem;
            line-height: 1.1;
            margin-bottom: 8px;
        }

        .species-card h4 { font-size: 0.95rem; margin-bottom: 4px; }
        .species-card p { font-size: 0.75rem; color: var(--text-dim); }

        /* Palette */
        .palette-section { margin-bottom: 20px; }
        .palette-section label {
            display: block;
            font-size: 0.85rem;
            color: var(--text-dim);
            margin-bottom: 8px;
        }

        .palette-grid {
            display: grid;
            grid-template-columns: repeat(8, 1fr);
            gap: 6px;
            margin-bottom: 8px;
        }

        .palette-swatch {
            width: 100%;
            aspect-ratio: 1;
            border-radius: 6px;
            border: 2px solid transparent;
            cursor: pointer;
            transition: border-color 0.2s, box-shadow 0.2s;
        }

        .palette-swatch:hover { border-color: #fff5; }
        .palette-swatch.selected { border-color: #fff; box-shadow: 0 0 0 2px var(--accent); }

        .hex-input {
            width: 100%;
            padding: 8px 12px;
            background: var(--bg-input);
            border: 1px solid var(--border);
            border-radius: 6px;
            color: var(--text);
            font-size: 0.85rem;
            font-family: monospace;
            outline: none;
        }

        .hex-input:focus { border-color: var(--accent); }

        /* Hatching */
        .hatch-stage {
            text-align: center;
            min-height: 200px;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
        }

        .egg-sprite, .creature-sprite {
            font-family: monospace;
            line-height: 1;
            font-size: 1.2rem;
        }
        .species-sprite-img {
            width: 96px;
            height: 96px;
            image-rendering: pixelated;
            object-fit: contain;
        }
        .species-card .species-sprite-img {
            width: 72px;
            height: 72px;
            margin: 0 auto 8px;
            display: block;
        }
        .color-preview-sprite .species-sprite-img,
        .color-preview-sprite canvas {
            width: 128px;
            height: 128px;
            image-rendering: pixelated;
        }
        .css-egg {
            width: 72px;
            height: 88px;
            background: linear-gradient(135deg, #e8e0d4 0%, #d4c9b8 50%, #c0b4a0 100%);
            border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
            margin: 0 auto;
            box-shadow: inset -8px -8px 16px rgba(0,0,0,0.1), inset 4px 4px 8px rgba(255,255,255,0.3);
        }

        @keyframes egg-wobble {
            0%, 100% { transform: rotate(0deg); }
            25% { transform: rotate(-3deg); }
            75% { transform: rotate(3deg); }
        }

        @keyframes egg-shake {
            0%, 100% { transform: translateX(0); }
            25% { transform: translateX(-3px); }
            75% { transform: translateX(3px); }
        }

        @keyframes creature-reveal {
            0% { opacity: 0; transform: scale(0.5); }
            100% { opacity: 1; transform: scale(1); }
        }

        .wobble { animation: egg-wobble 0.4s ease-in-out infinite; }
        .shake { animation: egg-shake 0.15s ease-in-out infinite; }
        .reveal { animation: creature-reveal 0.8s ease-out forwards; }

        .hatch-text {
            margin-top: 16px;
            color: var(--text-dim);
            font-size: 0.9rem;
        }

        .hatch-name {
            color: #00e5ff;
            font-size: 1.2rem;
            font-weight: 700;
            margin-top: 12px;
        }

        /* Cards */
        .card-grid {
            display: grid;
            gap: 12px;
            margin-bottom: 24px;
        }

        .card {
            background: var(--bg-card);
            border: 2px solid var(--border);
            border-radius: 12px;
            padding: 16px 20px;
            cursor: pointer;
            transition: border-color 0.2s, transform 0.1s;
            display: flex;
            align-items: center;
            gap: 14px;
        }

        .card:hover { border-color: var(--accent); transform: translateY(-1px); }
        .card.selected { border-color: var(--accent); background: #1e1e36; }
        .card.detected { border-color: var(--success); }

        .card .icon {
            font-size: 1.5rem;
            width: 40px;
            text-align: center;
            flex-shrink: 0;
        }

        .card .card-body { flex: 1; }
        .card .card-body h4 { font-size: 0.95rem; margin-bottom: 2px; }
        .card .card-body p { font-size: 0.8rem; color: var(--text-dim); }

        .card .badge {
            font-size: 0.7rem;
            padding: 2px 8px;
            border-radius: 10px;
            background: var(--success);
            color: #000;
            font-weight: 600;
        }

        .card input[type="checkbox"],
        .card input[type="radio"] {
            display: none;
        }

        /* Inputs */
        .form-group {
            margin-bottom: 20px;
        }

        .form-group label {
            display: block;
            font-size: 0.85rem;
            color: var(--text-dim);
            margin-bottom: 6px;
        }

        .form-group input[type="text"],
        .form-group input[type="password"],
        .form-group input[type="time"] {
            width: 100%;
            padding: 12px 16px;
            background: var(--bg-input);
            border: 1px solid var(--border);
            border-radius: 8px;
            color: var(--text);
            font-size: 0.95rem;
            outline: none;
            transition: border-color 0.2s;
        }

        .form-group input:focus { border-color: var(--accent); }

        .form-group .input-wrapper {
            position: relative;
        }

        .form-group .toggle-vis {
            position: absolute;
            right: 12px;
            top: 50%;
            transform: translateY(-50%);
            background: none;
            border: none;
            color: var(--text-dim);
            cursor: pointer;
            font-size: 0.85rem;
        }

        .form-group .hint {
            font-size: 0.8rem;
            color: var(--text-dim);
            margin-top: 4px;
        }

        .form-group a {
            color: var(--accent);
            text-decoration: none;
        }

        .form-group a:hover { text-decoration: underline; }

        .conditional { display: none; margin-top: 12px; }
        .conditional.show { display: block; }

        /* Toggle switch */
        .toggle-row {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 12px 0;
        }

        .toggle-row .label { font-size: 0.95rem; }
        .toggle-row .sublabel { font-size: 0.8rem; color: var(--text-dim); }

        .toggle {
            position: relative;
            width: 48px;
            height: 26px;
            cursor: pointer;
        }

        .toggle input { display: none; }

        .toggle .slider {
            position: absolute;
            inset: 0;
            background: var(--bg-input);
            border-radius: 13px;
            transition: background 0.2s;
        }

        .toggle .slider::after {
            content: '';
            position: absolute;
            width: 20px;
            height: 20px;
            background: var(--text);
            border-radius: 50%;
            top: 3px;
            left: 3px;
            transition: transform 0.2s;
        }

        .toggle input:checked + .slider { background: var(--accent); }
        .toggle input:checked + .slider::after { transform: translateX(22px); }

        /* Buttons */
        .btn-row {
            display: flex;
            justify-content: space-between;
            margin-top: 32px;
        }

        .btn {
            padding: 12px 28px;
            border: none;
            border-radius: 8px;
            font-size: 0.95rem;
            font-weight: 600;
            cursor: pointer;
            transition: background 0.2s, transform 0.1s;
        }

        .btn:active { transform: scale(0.97); }

        .btn-primary {
            background: var(--accent);
            color: #fff;
        }

        .btn-primary:hover { background: var(--accent-hover); }
        .btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }

        .btn-secondary {
            background: var(--bg-card);
            color: var(--text);
            border: 1px solid var(--border);
        }

        .btn-secondary:hover { border-color: var(--text-dim); }

        .btn-big {
            display: block;
            width: 100%;
            padding: 16px;
            font-size: 1.1rem;
            margin-top: 24px;
        }

        /* Summary table */
        .summary-table {
            width: 100%;
            border-collapse: collapse;
            margin-bottom: 24px;
        }

        .summary-table td {
            padding: 10px 12px;
            border-bottom: 1px solid var(--border);
            font-size: 0.9rem;
        }

        .summary-table td:first-child {
            color: var(--text-dim);
            width: 40%;
        }

        /* Error box */
        .error-box {
            background: #f8717122;
            border: 1px solid var(--error);
            border-radius: 8px;
            padding: 12px 16px;
            margin-bottom: 16px;
            color: var(--error);
            font-size: 0.9rem;
            display: none;
        }

        .error-box.show { display: block; }

        /* Activation progress */
        .activation-progress {
            background: var(--bg-input);
            border-radius: 8px;
            height: 8px;
            margin-bottom: 24px;
            overflow: hidden;
        }

        .activation-progress .fill {
            height: 100%;
            background: var(--accent);
            border-radius: 8px;
            width: 0%;
            transition: width 0.5s ease;
        }

        .activation-log {
            background: var(--bg-card);
            border-radius: 8px;
            padding: 16px;
            max-height: 200px;
            overflow-y: auto;
            font-family: monospace;
            font-size: 0.8rem;
            line-height: 1.8;
        }

        .activation-log .line { color: var(--text-dim); }
        .activation-log .line.ok { color: var(--success); }
        .activation-log .line.error { color: var(--error); }

        .complete-icon {
            text-align: center;
            font-size: 3rem;
            margin-bottom: 16px;
        }

        /* Color preview */
        .color-preview-row {
            display: flex;
            align-items: center;
            gap: 12px;
            margin-bottom: 16px;
        }

        .color-preview-sprite {
            font-family: monospace;
            line-height: 1;
            font-size: 0.8rem;
            flex-shrink: 0;
        }
    </style>
</head>
<body>
<div class="wizard">
    <!-- Progress Bar -->
    <div style="display:flex; align-items:center; gap:12px; margin-bottom:40px;">
        <div class="progress-bar" style="flex:1; margin-bottom:0;">
            <div class="step" data-step="0"></div>
            <div class="step" data-step="1"></div>
            <div class="step" data-step="2"></div>
            <div class="step" data-step="3"></div>
            <div class="step" data-step="4"></div>
            <div class="step" data-step="5"></div>
            <div class="step" data-step="6"></div>
            <div class="step" data-step="7"></div>
            <div class="step" data-step="8"></div>
            <div class="step" data-step="9"></div>
            <div class="step" data-step="10"></div>
        </div>
        <button onclick="startOver()" id="start-over-btn" style="display:none; background:none; border:none; color:var(--text-dim); font-size:0.75rem; cursor:pointer; white-space:nowrap;" title="Start over from the beginning">restart</button>
    </div>

    <!-- Step 0: Welcome + Logo -->
    <div class="step-content active" data-step="0">
        <div class="logo-banner">
            <span class="logo-cyan">FA</span><span class="logo-blue">MIL</span><span class="logo-magenta">IAR</span>
        </div>
        <hr class="logo-divider">
        <div class="logo-tagline">Your self-hosted AI companion</div>
        <div class="step-header">
            <p>A personal AI agent that lives on your machine, learns your workflow, and works for you — not a corporation.</p>
        </div>
        <button class="btn btn-primary btn-big" onclick="nextStep()">Get Started</button>
    </div>

    <!-- Step 1: Constitution -->
    <div class="step-content" data-step="1">
        <div class="step-header">
            <h2>The Constitution</h2>
            <p>Every Familiar is born with these principles</p>
        </div>
        <div id="constitution-articles"></div>
        <div class="consent-row">
            <input type="checkbox" id="consent-check">
            <label for="consent-check">I have read and accept the Constitution</label>
        </div>
        <div id="error-constitution" class="error-box"></div>
        <div class="btn-row">
            <button class="btn btn-secondary" onclick="prevStep()">Back</button>
            <button class="btn btn-primary" onclick="validateConstitution()">Next</button>
        </div>
    </div>

    <!-- Step 2: Your Name -->
    <div class="step-content" data-step="2">
        <div class="step-header">
            <h2>Who are you?</h2>
            <p>Your Familiar needs to know its owner</p>
        </div>
        <div class="form-group">
            <label>Your Name</label>
            <input type="text" id="owner-name" placeholder="e.g. George">
        </div>
        <div class="btn-row">
            <button class="btn btn-secondary" onclick="prevStep()">Back</button>
            <button class="btn btn-primary" onclick="validateOwnerName()">Next</button>
        </div>
    </div>

    <!-- Step 3: Name Your Familiar -->
    <div class="step-content" data-step="3">
        <div class="step-header">
            <div class="egg-sprite" id="egg-display"></div>
            <h2 style="margin-top:16px">What would you like to call me?</h2>
            <p>Give your creature a name</p>
        </div>
        <div class="form-group">
            <label>Creature Name</label>
            <input type="text" id="creature-name" value="Familiar" placeholder="Familiar">
        </div>
        <div class="btn-row">
            <button class="btn btn-secondary" onclick="prevStep()">Back</button>
            <button class="btn btn-primary" onclick="validateCreatureName()">Next</button>
        </div>
    </div>

    <!-- Step 4: Species -->
    <div class="step-content" data-step="4">
        <div class="step-header">
            <h2>Choose a Species</h2>
            <p>Each species gives your Familiar a unique personality</p>
        </div>
        <div id="error-species" class="error-box"></div>
        <div class="species-grid" id="species-grid"></div>
        <div class="btn-row">
            <button class="btn btn-secondary" onclick="prevStep()">Back</button>
            <button class="btn btn-primary" onclick="validateSpecies()">Next</button>
        </div>
    </div>

    <!-- Step 5: Colors -->
    <div class="step-content" data-step="5">
        <div class="step-header">
            <h2>Choose Colors</h2>
            <p>Pick body, eye, and accent colors for your creature</p>
        </div>
        <div id="color-preview" class="color-preview-row" style="justify-content:center; margin-bottom:20px;">
            <div class="color-preview-sprite" id="color-preview-sprite"></div>
        </div>
        <div class="palette-section">
            <label>Body Color</label>
            <div class="palette-grid" id="palette-body"></div>
            <input type="text" class="hex-input" id="hex-body" placeholder="#RRGGBB" maxlength="7">
        </div>
        <div class="palette-section">
            <label>Eye Color</label>
            <div class="palette-grid" id="palette-eyes"></div>
            <input type="text" class="hex-input" id="hex-eyes" placeholder="#RRGGBB" maxlength="7">
        </div>
        <div class="palette-section">
            <label>Accent Color</label>
            <div class="palette-grid" id="palette-accent"></div>
            <input type="text" class="hex-input" id="hex-accent" placeholder="#RRGGBB" maxlength="7">
        </div>
        <div class="btn-row">
            <button class="btn btn-secondary" onclick="prevStep()">Back</button>
            <button class="btn btn-primary" onclick="validateColors()">Next</button>
        </div>
    </div>

    <!-- Step 6: Hatching -->
    <div class="step-content" data-step="6">
        <div class="hatch-stage" id="hatch-stage">
            <div class="egg-sprite" id="hatch-egg"></div>
            <div class="hatch-text" id="hatch-text">Preparing...</div>
        </div>
        <div class="btn-row" id="hatch-back-row">
            <button class="btn btn-secondary" onclick="resetHatchAndGoBack()">Back</button>
            <span></span>
        </div>
    </div>

    <!-- Step 7: LLM Provider -->
    <div class="step-content" data-step="7">
        <div class="step-header">
            <button class="onboard-hat-btn" onclick="onboardHandoff('LLM Provider setup', 'provider')">
                <i class="fa-solid fa-hat-wizard"></i> Let Helper do it
            </button>
            <h2>Choose Your LLM</h2>
            <p>Which AI brain powers your Familiar?</p>
        </div>
        <div id="error-provider" class="error-box"></div>
        <div class="card-grid" id="provider-cards"></div>
        <!-- API key input -->
        <div id="provider-key-section" class="conditional">
            <div class="form-group">
                <label id="provider-key-label">API Key</label>
                <div class="input-wrapper">
                    <input type="password" id="provider-key" placeholder="sk-...">
                    <button class="toggle-vis" onclick="toggleVis('provider-key')">show</button>
                </div>
                <div class="hint">
                    <a id="provider-key-link" href="#" target="_blank">Get an API key</a>
                    &nbsp; Your key stays on your machine.
                </div>
            </div>
        </div>
        <!-- Ollama model picker -->
        <div id="ollama-model-section" class="conditional">
            <div class="form-group">
                <label>Model Setup</label>
                <div class="hint" id="ollama-ram-hint"></div>
            </div>
            <div class="card-grid" id="ollama-bundle-cards"></div>
        </div>
        <div class="btn-row">
            <button class="btn btn-secondary" onclick="prevStep()">Back</button>
            <button class="btn btn-primary" onclick="validateProvider()">Next</button>
        </div>
    </div>

    <!-- Step 8: Security -->
    <div class="step-content" data-step="8">
        <div class="step-header">
            <button class="onboard-hat-btn" onclick="onboardHandoff('Security and encryption setup', 'security')">
                <i class="fa-solid fa-hat-wizard"></i> Let Helper do it
            </button>
            <h2>Security</h2>
            <p>Set an owner PIN and optional encryption</p>
        </div>
        <div id="error-security" class="error-box"></div>
        <div class="form-group">
            <label>Owner PIN (4-8 digits)</label>
            <div class="input-wrapper">
                <input type="text" id="owner-pin" maxlength="8"
                       pattern="[0-9]*" inputmode="numeric" placeholder="e.g. 1234">
                <button class="toggle-vis" onclick="toggleVis('owner-pin')">show</button>
            </div>
            <div class="hint">
                This PIN lets you claim ownership when messaging your bot for the first time.
            </div>
        </div>
        <div style="margin-top: 24px;">
            <div class="toggle-row">
                <div>
                    <div class="label">Encrypt data at rest</div>
                    <div class="sublabel">Conversations, tasks, contacts encrypted on disk</div>
                </div>
                <label class="toggle">
                    <input type="checkbox" id="encrypt-toggle" checked onchange="toggleEncryption()">
                    <span class="slider"></span>
                </label>
            </div>
        </div>
        <div id="encryption-config" class="conditional">
            <div class="form-group">
                <label>Encryption Passphrase</label>
                <div class="input-wrapper">
                    <input type="text" id="encryption-passphrase" placeholder="Auto-generated if blank">
                    <button class="toggle-vis" onclick="toggleVis('encryption-passphrase')">show</button>
                </div>
                <div class="hint">If you lose this passphrase, your data is unrecoverable.</div>
            </div>
        </div>
        <div class="btn-row">
            <button class="btn btn-secondary" onclick="prevStep()">Back</button>
            <button class="btn btn-primary" onclick="validateSecurity()">Next</button>
        </div>
    </div>

    <!-- Step 9: Review -->
    <div class="step-content" data-step="9">
        <div class="step-header">
            <h2>Review</h2>
            <p>Confirm your configuration</p>
        </div>
        <table class="summary-table" id="summary-table"></table>
        <div class="btn-row">
            <button class="btn btn-secondary" onclick="prevStep()">Back</button>
            <button class="btn btn-primary btn-big" onclick="activate()" id="activate-btn">
                Activate
            </button>
        </div>
    </div>

    <!-- Step 10: Activation -->
    <div class="step-content" data-step="10">
        <div class="step-header" id="activation-header">
            <h2>Setting up...</h2>
        </div>
        <div class="activation-progress"><div class="fill" id="activation-fill"></div></div>
        <div class="activation-log" id="activation-log"></div>
        <div id="complete-section" style="display:none">
            <div class="complete-icon"></div>
            <div class="step-header">
                <h2 id="complete-title">Ready!</h2>
                <p id="complete-subtitle">Your Familiar is alive and ready to help.</p>
            </div>
            <div id="credentials-section" style="display:none; background:var(--bg-card); border:1px solid var(--accent); border-radius:10px; padding:20px; margin-bottom:20px; text-align:center;">
                <div style="font-size:1rem; font-weight:600; margin-bottom:8px;">Download your credentials before continuing</div>
                <div style="font-size:0.85rem; color:var(--text-dim); margin-bottom:16px;">This PDF contains your dashboard URL and API key.<br>Keep it somewhere safe — you'll need it to access your dashboard.</div>
                <button class="btn btn-primary btn-big" id="download-credentials-btn" style="margin-bottom:8px;">
                    Download Credentials PDF
                </button>
                <div id="download-done-msg" style="display:none; color:var(--success); font-size:0.85rem; margin-top:8px;">Downloaded! You can now open the dashboard.</div>
            </div>
            <table class="summary-table" id="complete-summary"></table>
            <div style="text-align:center">
                <p style="color:var(--text-dim); margin-bottom:16px; font-size:0.9rem" id="complete-instructions"></p>
                <button class="btn btn-big" id="open-dashboard-btn" disabled style="opacity:0.4; cursor:not-allowed;">
                    Open Dashboard
                </button>
            </div>
        </div>
    </div>
</div>

<script>
// ── State ────────────────────────────────────────────────────────
let currentStep = 0;
const totalSteps = 11;
let state = {
    ownerName: '',
    creatureName: 'Familiar',
    species: '',
    colorBody: '',
    colorEyes: '',
    colorAccent: '',
    constitutionAccepted: false,
    provider: '', apiKey: '',
    pin: '',
    encryptEnabled: true, passphrase: '',
};
let detectedProviders = {};
let creatureData = null;

// ── Init ─────────────────────────────────────────────────────────
document.addEventListener('DOMContentLoaded', () => {
    updateProgress();
    // Generate default PIN
    const pin = String(Math.floor(Math.random() * 10000)).padStart(4, '0');
    document.getElementById('owner-pin').value = pin;
    state.pin = pin;
    // Show encryption config since encryption defaults ON
    document.getElementById('encryption-config').classList.add('show');
    // Load creature data
    loadCreatureData();
    // Restore saved wizard state
    restoreState();
});

async function loadCreatureData() {
    try {
        const resp = await fetch('/api/onboard/creature-data');
        creatureData = await resp.json();
        renderConstitution(creatureData.constitution);
        renderSpeciesCards(creatureData.species);
        renderAllPalettes(creatureData.palette);
        loadEggPreview();
    } catch(e) {
        console.error('Failed to load creature data:', e);
    }
}

async function loadEggPreview() {
    const egg = '<div class="css-egg"></div>';
    document.getElementById('egg-display').innerHTML = egg;
    document.getElementById('hatch-egg').innerHTML = egg;
}

// ── Save & Resume ────────────────────────────────────────────────
const STORAGE_KEY = 'familiar_onboard_state';

function saveState() {
    try {
        const saved = {step: currentStep, state: state};
        localStorage.setItem(STORAGE_KEY, JSON.stringify(saved));
    } catch(e) {}
    fetch('/api/onboard/save-state', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({step: currentStep, state: state})
    }).catch(() => {});
}

function restoreState() {
    fetch('/api/onboard/load-state')
        .then(r => r.json())
        .then(saved => {
            if (saved.found) {
                _applyRestoredState(saved);
            } else {
                _restoreFromLocalStorage();
            }
        })
        .catch(() => _restoreFromLocalStorage());
}

function _restoreFromLocalStorage() {
    try {
        const raw = localStorage.getItem(STORAGE_KEY);
        if (!raw) return;
        const saved = JSON.parse(raw);
        _applyRestoredState(saved);
    } catch(e) {}
}

function _applyRestoredState(saved) {
    if (saved.state) {
        Object.assign(state, saved.state);
        if (state.ownerName) {
            const el = document.getElementById('owner-name');
            if (el) el.value = state.ownerName;
        }
        if (state.creatureName) {
            const el = document.getElementById('creature-name');
            if (el) el.value = state.creatureName;
        }
        if (state.pin) document.getElementById('owner-pin').value = state.pin;
        if (state.encryptEnabled) {
            document.getElementById('encrypt-toggle').checked = true;
            document.getElementById('encryption-config').classList.add('show');
        }
        if (state.constitutionAccepted) {
            const cb = document.getElementById('consent-check');
            if (cb) cb.checked = true;
        }
    }
    if (saved.step && saved.step > 0) {
        // Don't restore to transient steps (hatching animation or activation)
        let targetStep = saved.step;
        if (targetStep === 6) targetStep = 5;   // hatching → back to colors
        if (targetStep >= 10) targetStep = 9;    // activation → back to review
        showStep(targetStep);
    }
}

function clearSavedState() {
    try { localStorage.removeItem(STORAGE_KEY); } catch(e) {}
    fetch('/api/onboard/clear-state', {method: 'POST'}).catch(() => {});
}

// ── Progress Bar ─────────────────────────────────────────────────
function updateProgress() {
    document.querySelectorAll('.progress-bar .step').forEach(el => {
        const s = parseInt(el.dataset.step);
        el.classList.toggle('active', s === currentStep);
        el.classList.toggle('done', s < currentStep);
    });
}

// ── Navigation ───────────────────────────────────────────────────
function showStep(n) {
    document.querySelectorAll('.step-content').forEach(el => {
        el.classList.toggle('active', parseInt(el.dataset.step) === n);
    });
    currentStep = n;
    updateProgress();
    window.scrollTo(0, 0);
    // Show restart button after leaving welcome
    const restartBtn = document.getElementById('start-over-btn');
    if (restartBtn) restartBtn.style.display = n > 0 ? '' : 'none';
}

function nextStep() { showStep(currentStep + 1); saveState(); }
function prevStep() { showStep(currentStep - 1); saveState(); }

function onboardHandoff(serviceName, step) {
    // Store handoff request in sessionStorage so the dashboard can pick it up
    sessionStorage.setItem('familiar_handoff', JSON.stringify({
        service_name: serviceName,
        step: step,
        source: 'onboard',
        context: 'User clicked Helper Hat during onboarding wizard step: ' + step,
    }));
    // Redirect to dashboard
    window.location.href = '/';
}

function startOver() {
    clearSavedState();
    state = {
        ownerName: '', creatureName: 'Familiar', species: '',
        colorBody: '', colorEyes: '', colorAccent: '',
        constitutionAccepted: false, provider: '', apiKey: '',
        pin: '', encryptEnabled: true, passphrase: '',
    };
    // Reset form fields
    const fields = {
        'owner-name': '', 'creature-name': 'Familiar', 'owner-pin': '',
        'consent-check': false, 'encrypt-toggle': true,
    };
    for (const [id, val] of Object.entries(fields)) {
        const el = document.getElementById(id);
        if (!el) continue;
        if (el.type === 'checkbox') el.checked = val;
        else el.value = val;
    }
    // Generate new PIN
    const pin = String(Math.floor(Math.random() * 10000)).padStart(4, '0');
    document.getElementById('owner-pin').value = pin;
    state.pin = pin;
    showStep(0);
}

function resetHatchAndGoBack() {
    // Reset the hatching stage to its initial state
    const stage = document.getElementById('hatch-stage');
    const eggEl = document.getElementById('hatch-egg');
    const textEl = document.getElementById('hatch-text');
    // Remove any dynamically added elements (name, "was born!" text)
    stage.querySelectorAll('.hatch-name').forEach(el => el.remove());
    // Reset egg
    eggEl.className = 'egg-sprite';
    eggEl.style.opacity = '1';
    eggEl.style.transition = '';
    textEl.textContent = 'Preparing...';
    // Reload egg preview
    loadEggPreview();
    showStep(5); // back to colors
    saveState();
}

// ── Error display ────────────────────────────────────────────────
function showError(id, msg) {
    const el = document.getElementById(id);
    if (el) { el.textContent = msg; el.classList.add('show'); }
}
function clearError(id) {
    const el = document.getElementById(id);
    if (el) { el.textContent = ''; el.classList.remove('show'); }
}

// ── Toggle visibility ────────────────────────────────────────────
function toggleVis(inputId) {
    const inp = document.getElementById(inputId);
    const btn = inp.parentElement.querySelector('.toggle-vis');
    if (inp.type === 'password') {
        inp.type = 'text'; btn.textContent = 'hide';
    } else {
        inp.type = 'password'; btn.textContent = 'show';
    }
}

// ── Step 1: Constitution ─────────────────────────────────────────
function renderConstitution(articles) {
    const container = document.getElementById('constitution-articles');
    if (!articles || articles.length === 0) return;
    container.innerHTML = articles.map((a, i) =>
        `<div class="constitution-article">
            <h4>${a.title}</h4>
            <p>${a.summary}</p>
        </div>`
    ).join('');
}

function validateConstitution() {
    clearError('error-constitution');
    state.constitutionAccepted = document.getElementById('consent-check').checked;
    if (!state.constitutionAccepted) {
        showError('error-constitution', 'You must accept the Constitution to continue');
        return;
    }
    nextStep();
}

// ── Step 2: Owner Name ───────────────────────────────────────────
function validateOwnerName() {
    state.ownerName = document.getElementById('owner-name').value.trim();
    nextStep();
}

// ── Step 3: Creature Name ────────────────────────────────────────
function validateCreatureName() {
    state.creatureName = document.getElementById('creature-name').value.trim() || 'Familiar';
    nextStep();
}

// ── Step 4: Species ──────────────────────────────────────────────
function renderSpeciesCards(speciesList) {
    const grid = document.getElementById('species-grid');
    if (!speciesList || speciesList.length === 0) return;
    grid.innerHTML = speciesList.map(sp =>
        `<div class="species-card" onclick="selectSpecies(this, '${sp.key}')">
            <img src="/sprites/${sp.key}-baby.png" alt="${sp.label}" class="species-sprite-img" />
            <h4>${sp.label}</h4>
            <p>${sp.personality}</p>
        </div>`
    ).join('');
}

function selectSpecies(el, key) {
    document.querySelectorAll('.species-card').forEach(c => c.classList.remove('selected'));
    el.classList.add('selected');
    state.species = key;
}

function validateSpecies() {
    clearError('error-species');
    if (!state.species) {
        showError('error-species', 'Please choose a species');
        return;
    }
    // Update color preview with selected species
    updateColorPreview();
    nextStep();
}

// ── Step 5: Colors ───────────────────────────────────────────────
function renderAllPalettes(palette) {
    if (!palette || palette.length === 0) return;
    renderPaletteGrid('palette-body', 'hex-body', palette, 'colorBody');
    renderPaletteGrid('palette-eyes', 'hex-eyes', palette, 'colorEyes');
    renderPaletteGrid('palette-accent', 'hex-accent', palette, 'colorAccent');

    // Set defaults
    if (!state.colorBody && palette.length > 0) {
        state.colorBody = palette[6].hex; // sapphire
        state.colorEyes = palette[2].hex; // gold
        state.colorAccent = palette[0].hex; // crimson
        document.getElementById('hex-body').value = state.colorBody;
        document.getElementById('hex-eyes').value = state.colorEyes;
        document.getElementById('hex-accent').value = state.colorAccent;
        // Pre-select swatches
        _preselectSwatch('palette-body', state.colorBody);
        _preselectSwatch('palette-eyes', state.colorEyes);
        _preselectSwatch('palette-accent', state.colorAccent);
    }
}

function _preselectSwatch(gridId, hex) {
    const swatches = document.querySelectorAll('#' + gridId + ' .palette-swatch');
    swatches.forEach(s => {
        if (s.dataset.hex === hex) s.classList.add('selected');
    });
}

function renderPaletteGrid(gridId, hexInputId, palette, stateKey) {
    const grid = document.getElementById(gridId);
    grid.innerHTML = palette.map(p =>
        `<div class="palette-swatch" style="background:${p.hex};"
              data-hex="${p.hex}" title="${p.name}"
              onclick="selectColor('${gridId}', '${hexInputId}', '${stateKey}', this, '${p.hex}')">
        </div>`
    ).join('');

    // Hex input handler
    const hexInput = document.getElementById(hexInputId);
    hexInput.addEventListener('input', () => {
        const val = hexInput.value.trim();
        if (/^#[0-9a-fA-F]{6}$/.test(val)) {
            state[stateKey] = val;
            // Deselect all swatches in this grid
            grid.querySelectorAll('.palette-swatch').forEach(s => s.classList.remove('selected'));
            // Try to select matching swatch
            grid.querySelectorAll('.palette-swatch').forEach(s => {
                if (s.dataset.hex.toLowerCase() === val.toLowerCase()) s.classList.add('selected');
            });
            updateColorPreview();
        }
    });
}

function selectColor(gridId, hexInputId, stateKey, el, hex) {
    const grid = document.getElementById(gridId);
    grid.querySelectorAll('.palette-swatch').forEach(s => s.classList.remove('selected'));
    el.classList.add('selected');
    state[stateKey] = hex;
    document.getElementById(hexInputId).value = hex;
    updateColorPreview();
}

// ── Palette-based sprite recolor engine (ported from v2 spriteRecolor.ts) ──
let _spritePalettes = null;
const _imgCache = {};
const _recolorCache = {};

async function _loadPalettes() {
    if (_spritePalettes) return _spritePalettes;
    try {
        const resp = await fetch('/sprite-palettes.json');
        _spritePalettes = await resp.json();
    } catch(e) { _spritePalettes = {}; }
    return _spritePalettes;
}

function _hexToRgb(hex) {
    const h = hex.replace('#','');
    return [parseInt(h.slice(0,2),16), parseInt(h.slice(2,4),16), parseInt(h.slice(4,6),16)];
}

function _rgbToHsv(r, g, b) {
    const rf=r/255, gf=g/255, bf=b/255;
    const max=Math.max(rf,gf,bf), min=Math.min(rf,gf,bf), d=max-min;
    let h=0;
    if (d>0) {
        if (max===rf) h=((gf-bf)/d+(gf<bf?6:0))/6;
        else if (max===gf) h=((bf-rf)/d+2)/6;
        else h=((rf-gf)/d+4)/6;
    }
    return [h, max===0?0:d/max, max];
}

function _hsvToRgb(h, s, v) {
    let r=0,g=0,b=0;
    const i=Math.floor(h*6), f=h*6-i, p=v*(1-s), q=v*(1-f*s), t=v*(1-(1-f)*s);
    switch(i%6){
        case 0:r=v;g=t;b=p;break;case 1:r=q;g=v;b=p;break;case 2:r=p;g=v;b=t;break;
        case 3:r=p;g=q;b=v;break;case 4:r=t;g=p;b=v;break;case 5:r=v;g=p;b=q;break;
    }
    return [Math.round(r*255),Math.round(g*255),Math.round(b*255)];
}

function _computeTarget(origHex, targetHex, midV) {
    const [,,origV] = _rgbToHsv(..._hexToRgb(origHex));
    const [tH,tS,tV] = _rgbToHsv(..._hexToRgb(targetHex));
    const ratio = midV > 0 ? origV / midV : 1;
    const newV = Math.min(1, Math.max(0, tV * ratio));
    const newS = ratio > 1.3 ? tS * 0.7 : tS;
    return _hsvToRgb(tH, Math.min(1, newS), newV);
}

function _computeMidV(palette, role) {
    const entries = Object.entries(palette.colors).filter(([,e]) => e.role === role);
    if (!entries.length) return 0.5;
    const mid = entries.filter(([,e]) => e.shade === 'mid');
    const targets = mid.length ? mid : entries;
    let total = 0;
    for (const [hex] of targets) { const [,,v] = _rgbToHsv(..._hexToRgb(hex)); total += v; }
    return total / targets.length;
}

let _previewDebounce = null;
function updateColorPreview() {
    if (_previewDebounce) clearTimeout(_previewDebounce);
    _previewDebounce = setTimeout(() => _renderColorPreview(), 150);
}

async function _renderColorPreview() {
    const species = state.species || 'fox';
    const bodyHex = state.colorBody || '#4A90D9';
    const eyeHex = state.colorEyes || '#FFD700';
    const accentHex = state.colorAccent || '#FF6B6B';
    const spriteKey = species + '-baby';
    const container = document.getElementById('color-preview-sprite');

    const palettes = await _loadPalettes();
    const palette = palettes[spriteKey];

    // No palette — just show unmodified sprite
    if (!palette) {
        container.innerHTML = `<img src="/sprites/${spriteKey}.png" class="species-sprite-img" />`;
        return;
    }

    // Cache check
    const ck = `${spriteKey}:${bodyHex}:${eyeHex}:${accentHex}`;
    if (_recolorCache[ck]) {
        container.innerHTML = `<img src="${_recolorCache[ck]}" class="species-sprite-img" />`;
        return;
    }

    // Load image
    let img = _imgCache[spriteKey];
    if (!img) {
        img = await new Promise((resolve, reject) => {
            const i = new Image(); i.crossOrigin='anonymous';
            i.onload = () => resolve(i); i.onerror = reject;
            i.src = `/sprites/${spriteKey}.png`;
        });
        _imgCache[spriteKey] = img;
    }

    const canvas = document.createElement('canvas');
    canvas.width = img.naturalWidth; canvas.height = img.naturalHeight;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0);
    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const px = imageData.data;

    const bodyMidV = palette.bodyMidV ?? _computeMidV(palette, 'body');
    const eyeMidV = palette.eyeMidV ?? _computeMidV(palette, 'eye');
    const accentMidV = palette.accentMidV ?? _computeMidV(palette, 'accent');

    // Build color lookup map
    const colorMap = new Map();
    for (const [hex, entry] of Object.entries(palette.colors)) {
        const [r,g,b] = _hexToRgb(hex);
        const k = r*65536+g*256+b;
        if (entry.role === 'outline') { colorMap.set(k, null); }
        else if (entry.role === 'highlight') {
            const [tH] = _rgbToHsv(..._hexToRgb(bodyHex));
            const [,,origV] = _rgbToHsv(r,g,b);
            colorMap.set(k, _hsvToRgb(tH, 0.05, origV));
        }
        else if (entry.role === 'body') colorMap.set(k, _computeTarget(hex, bodyHex, bodyMidV));
        else if (entry.role === 'eye') colorMap.set(k, _computeTarget(hex, eyeHex, eyeMidV));
        else if (entry.role === 'accent') colorMap.set(k, _computeTarget(hex, accentHex, accentMidV));
    }

    for (let i = 0; i < px.length; i += 4) {
        if (px[i+3] === 0) continue;
        const k = px[i]*65536+px[i+1]*256+px[i+2];
        const mapped = colorMap.get(k);
        if (mapped === undefined || mapped === null) continue;
        px[i]=mapped[0]; px[i+1]=mapped[1]; px[i+2]=mapped[2];
    }

    ctx.putImageData(imageData, 0, 0);
    const dataUrl = canvas.toDataURL('image/png');
    _recolorCache[ck] = dataUrl;
    container.innerHTML = `<img src="${dataUrl}" class="species-sprite-img" />`;
}

function validateColors() {
    if (!state.colorBody) state.colorBody = '#4A90D9';
    if (!state.colorEyes) state.colorEyes = '#FFD700';
    if (!state.colorAccent) state.colorAccent = '#FF6B6B';
    // Validate creature on the server
    validateCreature();
}

async function validateCreature() {
    try {
        const resp = await fetch('/api/onboard/validate/creature', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({
                owner_name: state.ownerName,
                creature_name: state.creatureName,
                species: state.species,
                color_body: state.colorBody,
                color_eyes: state.colorEyes,
                color_accent: state.colorAccent,
                constitution_accepted: state.constitutionAccepted,
            })
        });
        const data = await resp.json();
        if (data.success) {
            runHatchingAnimation();
        } else {
            showError('error-species', data.errors.join('. '));
            showStep(4); // Go back to species step
        }
    } catch(e) {
        showError('error-species', 'Connection error');
        showStep(4);
    }
}

// ── Step 6: Hatching Animation ───────────────────────────────────
async function runHatchingAnimation() {
    showStep(6);
    const eggEl = document.getElementById('hatch-egg');
    const textEl = document.getElementById('hatch-text');
    const stage = document.getElementById('hatch-stage');

    // Phase 1: Wobble
    textEl.textContent = 'Something is stirring...';
    eggEl.className = 'egg-sprite wobble';
    await sleep(2000);

    // Phase 2: Shake
    textEl.textContent = 'Almost there...';
    eggEl.className = 'egg-sprite shake';
    await sleep(1500);

    // Phase 3: Fade out egg
    eggEl.style.transition = 'opacity 0.5s';
    eggEl.style.opacity = '0';
    textEl.textContent = '';
    await sleep(600);

    // Phase 4: Creature reveal — use PixelLab PNG sprite
    eggEl.innerHTML = `<img src="/sprites/${state.species}-baby.png" alt="${state.species}" class="species-sprite-img" />`;

    eggEl.className = 'creature-sprite reveal';
    eggEl.style.opacity = '1';

    // Show name
    const nameEl = document.createElement('div');
    nameEl.className = 'hatch-name';
    nameEl.textContent = state.creatureName;
    stage.appendChild(nameEl);

    const subtextEl = document.createElement('div');
    subtextEl.className = 'hatch-text';
    subtextEl.textContent = 'was born!';
    stage.appendChild(subtextEl);

    await sleep(2000);

    // Auto-advance to provider step and detect providers
    detectProviders();
    nextStep();
}

function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }

// ── Step 7: LLM Provider ────────────────────────────────────────
async function detectProviders() {
    try {
        const resp = await fetch('/api/onboard/detect');
        const data = await resp.json();
        detectedProviders = data.providers || {};
        buildProviderCards();
    } catch(e) {
        buildProviderCards();
    }
}

function buildProviderCards() {
    const providers = [
        {key: 'anthropic', icon: '&#x1f9e0;', label: 'Anthropic Claude', desc: 'Best quality cloud AI', url: 'https://console.anthropic.com/'},
        {key: 'openai', icon: '&#x1f916;', label: 'OpenAI GPT', desc: 'GPT-4o and GPT-4o-mini', url: 'https://platform.openai.com/api-keys'},
        {key: 'gemini', icon: '&#x1f48e;', label: 'Google Gemini', desc: 'Gemini 2.5 Flash', url: 'https://aistudio.google.com/apikey'},
        {key: 'ollama', icon: '&#x1f3e0;', label: 'Ollama (Local)', desc: 'Free, private, runs on your machine', url: 'https://ollama.ai/'}
    ];
    const container = document.getElementById('provider-cards');
    container.innerHTML = '';
    providers.forEach(p => {
        const detected = detectedProviders[p.key];
        const card = document.createElement('div');
        card.className = 'card' + (detected ? ' detected' : '');
        card.onclick = () => selectProvider(card, p.key, p.url);
        card.innerHTML = `
            <div class="icon">${p.icon}</div>
            <div class="card-body">
                <h4>${p.label}</h4>
                <p>${detected ? detected.label : p.desc}</p>
            </div>
            ${detected ? '<span class="badge">Detected</span>' : ''}
        `;
        container.appendChild(card);
    });

    // Auto-select first detected
    const autoKey = Object.keys(detectedProviders)[0];
    if (autoKey) {
        const cards = container.querySelectorAll('.card');
        const idx = providers.findIndex(p => p.key === autoKey);
        if (idx >= 0) selectProvider(cards[idx], autoKey, providers[idx].url);
    }
}

let ollamaBundles = null;

function selectProvider(el, key, url) {
    document.querySelectorAll('#provider-cards .card').forEach(c => c.classList.remove('selected'));
    el.classList.add('selected');
    state.provider = key;
    const keySection = document.getElementById('provider-key-section');
    const ollamaSection = document.getElementById('ollama-model-section');
    if (key === 'ollama') {
        keySection.classList.remove('show');
        ollamaSection.classList.add('show');
        loadOllamaBundles();
    } else {
        keySection.classList.add('show');
        ollamaSection.classList.remove('show');
        const detected = detectedProviders[key];
        if (detected && detected.key) {
            document.getElementById('provider-key').value = detected.key;
        } else {
            document.getElementById('provider-key').value = '';
        }
        document.getElementById('provider-key-label').textContent = key.charAt(0).toUpperCase() + key.slice(1) + ' API Key';
        document.getElementById('provider-key-link').href = url;
    }
}

async function loadOllamaBundles() {
    if (ollamaBundles) { renderOllamaBundles(); return; }
    try {
        const resp = await fetch('/api/onboard/ollama-bundles');
        ollamaBundles = await resp.json();
        renderOllamaBundles();
    } catch(e) {}
}

function renderOllamaBundles() {
    if (!ollamaBundles) return;
    const hint = document.getElementById('ollama-ram-hint');
    hint.textContent = ollamaBundles.ram_gb.toFixed(0) + ' GB RAM detected';
    const container = document.getElementById('ollama-bundle-cards');
    const rec = ollamaBundles.recommended;
    state.ollamaMode = 'bundle';
    container.innerHTML = `
        <div class="card selected" onclick="selectOllamaMode(this, 'bundle')">
            <div class="icon">&#x1f4e6;</div>
            <div class="card-body">
                <h4>${rec.label} (Recommended)</h4>
                <p>${rec.desc}</p>
            </div>
        </div>
        <div class="card" onclick="selectOllamaMode(this, 'custom')">
            <div class="icon">&#x1f527;</div>
            <div class="card-body">
                <h4>Custom</h4>
                <p>Choose individual models</p>
            </div>
        </div>
        <div class="card" onclick="selectOllamaMode(this, 'skip')">
            <div class="icon">&#x23ed;</div>
            <div class="card-body">
                <h4>Skip</h4>
                <p>I'll manage models myself</p>
            </div>
        </div>
    `;
}

function selectOllamaMode(el, mode) {
    document.querySelectorAll('#ollama-bundle-cards .card').forEach(c => c.classList.remove('selected'));
    el.classList.add('selected');
    state.ollamaMode = mode;
}

async function validateProvider() {
    clearError('error-provider');
    state.apiKey = document.getElementById('provider-key').value.trim();
    if (!state.provider) {
        showError('error-provider', 'Please select a provider'); return;
    }
    const payload = {provider: state.provider, api_key: state.apiKey};
    if (state.provider === 'ollama' && ollamaBundles) {
        const rec = ollamaBundles.recommended;
        if (state.ollamaMode === 'bundle') {
            payload.ollama_model = rec.models[0];
            if (rec.models.length >= 3) payload.lightweight_model = rec.models[1];
        } else if (state.ollamaMode === 'custom') {
            payload.ollama_model = rec.models[0];
        }
    }
    try {
        const resp = await fetch('/api/onboard/validate/provider', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify(payload)
        });
        const data = await resp.json();
        if (data.success) { nextStep(); }
        else { showError('error-provider', data.errors.join('. ')); }
    } catch(e) { showError('error-provider', 'Connection error'); }
}

// ── Step 8: Security ─────────────────────────────────────────────
function toggleEncryption() {
    state.encryptEnabled = document.getElementById('encrypt-toggle').checked;
    document.getElementById('encryption-config').classList.toggle('show', state.encryptEnabled);
}

async function validateSecurity() {
    clearError('error-security');
    state.pin = document.getElementById('owner-pin').value.trim();
    state.passphrase = document.getElementById('encryption-passphrase').value.trim();

    // Validate PIN
    let resp = await fetch('/api/onboard/validate/owner_pin', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({pin: state.pin})
    });
    let data = await resp.json();
    if (!data.success) {
        showError('error-security', data.errors.join('. ')); return;
    }

    // Validate encryption
    resp = await fetch('/api/onboard/validate/encryption', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({enabled: state.encryptEnabled, passphrase: state.passphrase})
    });
    data = await resp.json();
    if (data.success) {
        if (data.data && data.data.passphrase) {
            state.passphrase = data.data.passphrase;
        }
        buildSummary();
        nextStep();
    } else {
        showError('error-security', data.errors.join('. '));
    }
}

// ── Step 9: Review ───────────────────────────────────────────────
function buildSummary() {
    const t = document.getElementById('summary-table');
    const providerLabels = {anthropic:'Anthropic Claude', openai:'OpenAI GPT', gemini:'Google Gemini', ollama:'Ollama (local)'};
    const speciesLabel = creatureData ? (creatureData.species.find(s => s.key === state.species) || {}).label || state.species : state.species;
    const rows = [
        ['Owner', state.ownerName || '(not set)'],
        ['Creature', state.creatureName],
        ['Species', speciesLabel],
        ['Provider', providerLabels[state.provider] || state.provider],
        ['Owner PIN', '\u2022'.repeat(state.pin.length) + ' (' + state.pin.length + ' digits)'],
        ['Encryption', state.encryptEnabled ? 'Enabled' : 'Off'],
    ];
    t.innerHTML = rows.map(r => `<tr><td>${r[0]}</td><td>${r[1]}</td></tr>`).join('');
}

// ── Step 10: Activation ──────────────────────────────────────────
async function activate() {
    showStep(10);
    const log = document.getElementById('activation-log');
    const fill = document.getElementById('activation-fill');
    log.innerHTML = '';

    function addLine(msg, cls) {
        const div = document.createElement('div');
        div.className = 'line' + (cls ? ' ' + cls : '');
        div.textContent = msg;
        log.appendChild(div);
        log.scrollTop = log.scrollHeight;
    }

    addLine('Starting activation...');
    fill.style.width = '10%';

    try {
        const evtSource = new EventSource('/api/onboard/activate/stream');
        let progress = 10;

        evtSource.onmessage = (event) => {
            const data = JSON.parse(event.data);
            if (data.message) {
                addLine(data.message, data.type === 'error' ? 'error' : 'ok');
            }
            if (data.progress) {
                progress = data.progress;
                fill.style.width = progress + '%';
            }
            if (data.done) {
                evtSource.close();
                fill.style.width = '100%';
                if (data.success) {
                    clearSavedState();
                    document.getElementById('activation-header').style.display = 'none';
                    document.getElementById('complete-section').style.display = 'block';
                    document.getElementById('complete-title').textContent = state.creatureName + ' is ready!';

                    // Store dashboard key
                    const dashKey = data.dashboard_key || '';
                    if (dashKey) {
                        sessionStorage.setItem('familiar_api_key', dashKey);
                    }

                    // Show credentials download section
                    const credSection = document.getElementById('credentials-section');
                    credSection.style.display = 'block';

                    // Wire up download button
                    const dlBtn = document.getElementById('download-credentials-btn');
                    dlBtn.onclick = () => {
                        const url = '/api/onboard/credentials.pdf?key=' + encodeURIComponent(dashKey);
                        const a = document.createElement('a');
                        a.href = url;
                        a.download = 'familiar-credentials.pdf';
                        a.click();
                        // Enable the Open Dashboard button after download
                        setTimeout(() => {
                            document.getElementById('download-done-msg').style.display = 'block';
                            const dashBtn = document.getElementById('open-dashboard-btn');
                            dashBtn.disabled = false;
                            dashBtn.style.opacity = '1';
                            dashBtn.style.cursor = 'pointer';
                        }, 1000);
                    };

                    // Wire up Open Dashboard button (stays disabled until download)
                    const dashBtn = document.getElementById('open-dashboard-btn');
                    dashBtn.onclick = () => {
                        // Queue /start so Chat triggers the onboarding flow
                        sessionStorage.setItem('familiar_handoff', '/start');
                        if (dashKey) {
                            window.location.href = '/?api_key=' + encodeURIComponent(dashKey) + '#chat';
                        } else {
                            window.location.href = '/#chat';
                        }
                    };

                    // Fetch and render checklist
                    fetch('/api/onboard/checklist')
                        .then(r => r.json())
                        .then(items => {
                            const rows = items.map((item, i) =>
                                `<tr><td>${i + 1}.</td><td><strong>${item.title}</strong><br><span style="color:var(--text-dim)">${item.detail}</span></td></tr>`
                            ).join('');
                            document.getElementById('complete-summary').innerHTML = rows;
                        }).catch(() => {
                            const providerLabels = {anthropic:'Anthropic Claude', openai:'OpenAI GPT', gemini:'Google Gemini', ollama:'Ollama (local)'};
                            const summaryRows = [
                                ['Provider', providerLabels[state.provider] || state.provider],
                                ['Creature', state.creatureName],
                            ];
                            document.getElementById('complete-summary').innerHTML =
                                summaryRows.map(r => `<tr><td>${r[0]}</td><td>${r[1]}</td></tr>`).join('');
                        });
                    document.getElementById('complete-subtitle').textContent = 'Next steps:';
                    document.getElementById('complete-instructions').textContent =
                        'Channels can be connected later via /connect in the dashboard.';
                } else {
                    addLine('Activation failed. Check errors above.', 'error');
                }
            }
        };

        evtSource.onerror = () => {
            evtSource.close();
            addLine('Connection lost. Try again.', 'error');
        };

    } catch(e) {
        addLine('Error: ' + e.message, 'error');
    }
}
</script>
</body>
</html>
