// Assessment funnel — 212 items (187 Likert + 25 AI preference).
// REQUIRES: engine/itembank.jsx loaded before this file (sets window.ALL_ITEMS).
// Place this file at: export/src/screens/Assessment.jsx  (replaces existing file)

const LIKERT_LABELS = [
  'Very Inaccurate',
  'Moderately Inaccurate',
  'Neither',
  'Moderately Accurate',
  'Very Accurate',
];

// [EDIT 2026-05-11] Onboarding steps moved here from OnboardingScreen — now a pre-flight phase
const ONBOARDING_STEPS = [
  { title: "Welcome, let's set expectations", body: "This takes about 25 minutes. Answer honestly — the engine runs quality checks for central tendency and acquiescence. There are no right answers.", icon: "◌" },
  { title: "What you'll get", body: "A personal profile across 10 dimensions, a blind-spot report tailored to your score pattern, and a personal AI constitution you can load into Claude, ChatGPT, or Gemini.", icon: "◎" },
  { title: "Consent & data", body: "Your psychometric data is encrypted at rest. We never surface admin-only (SD3) scores. You can delete your profile at any time.", icon: "▽" },
];

function AssessmentScreen({ onEnter }) {
  const isMobile = window.useIsMobile ? window.useIsMobile() : false;
  // [EDIT 2026-05-11] Pre-flight onboarding phase — 'onboarding' | 'questions'
  const [phase, setPhase]       = React.useState('onboarding');
  const [step, setStep]         = React.useState(0);
  const [consents, setConsents] = React.useState({ data: false, pair: false, ai: false });
  const [submitting, setSubmitting] = React.useState(false);
  const [alreadyCompleted, setAlreadyCompleted] = React.useState(false);

  React.useEffect(() => {
    try {
      const user = JSON.parse(localStorage.getItem('kalibr.user') || '{}');
      if (user.id) {
        const cards = JSON.parse(localStorage.getItem('kalibr.be.cards') || '{}');
        if (cards[user.id]) { setAlreadyCompleted(true); return; }
      }
    } catch (e) {}
    if (window.kalibrAuth) {
      fetch('/me/scored-card', { headers: window.kalibrAuth.getHeaders() })
        .then(r => r.ok ? r.json() : null)
        .then(d => { if (d && d.completed) setAlreadyCompleted(true); })
        .catch(() => {});
    }
  }, []);

  // All hooks must be declared before any conditional return
  const allItems = React.useMemo(() => {
    const src = window.ALL_ITEMS;
    if (!src || !src.length) {
      console.warn('[Kalibr] window.ALL_ITEMS not found — is engine/itembank.jsx loaded?');
      return [];
    }
    return src.map(it => {
      if (it.type === 'likert') {
        return { id: it.id, text: it.text, kind: 'likert' };
      }
      return {
        id: it.id,
        text: it.stem,
        kind: 'mc',
        options: it.options.map(o => o[1]),
        letters: it.options.map(o => o[0]),
      };
    });
  }, []);

  React.useEffect(() => {
    // ChatGPT 5/13 — Non-blocking shared-manifest check keeps frontend item codes aligned with backend validation.
    if (!allItems.length) return;
    fetch('/assessment_manifest.json')
      .then(r => r.ok ? r.json() : null)
      .then(manifest => {
        if (!manifest || !Array.isArray(manifest.requiredIds)) return;
        const ids = allItems.map(it => it.id);
        const sameLength = ids.length === manifest.expectedResponseCount && ids.length === manifest.requiredIds.length;
        const sameOrder = sameLength && ids.every((id, idx) => id === manifest.requiredIds[idx]);
        if (!sameOrder) console.warn('[Kalibr] Assessment manifest mismatch', { frontendCount: ids.length, manifestCount: manifest.requiredIds.length });
      })
      .catch(() => {});
  }, [allItems]);

  const [answers, setAnswers] = React.useState({});
  const PAGE_SIZE = 3;
  const [pageIdx, setPageIdx] = React.useState(0);
  const startTimeRef = React.useRef(null);

  const total      = allItems.length;
  const totalPages = Math.ceil(total / PAGE_SIZE);
  const pageItems  = allItems.slice(pageIdx * PAGE_SIZE, (pageIdx + 1) * PAGE_SIZE);
  const answered   = Object.keys(answers).length;
  const pct        = total ? (answered / total) * 100 : 0;
  const pageFullyAnswered = pageItems.every(it => answers[it.id] != null);
  const allAnswered = answered === total;

  const setVal = (itemId, v) => {
    setAnswers(prev => ({ ...prev, [itemId]: v }));
  };

  React.useEffect(() => {
    if (phase !== 'questions') return;
    if (!pageFullyAnswered || pageIdx >= totalPages - 1) return;
    const t = setTimeout(() => {
      setPageIdx(p => p + 1);
      window.scrollTo(0, 0);
    }, 600);
    return () => clearTimeout(t);
  }, [pageFullyAnswered, pageIdx, totalPages, phase]);

  if (alreadyCompleted) {
    return (
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh', padding: 32 }}>
        <div className="k-card" style={{ width: isMobile ? '100%' : 480, padding: isMobile ? '28px 20px' : '44px 40px', textAlign: 'center' }}>
          <div style={{ fontSize: 40, marginBottom: 20 }}>◎</div>
          <h2 className="k-serif" style={{ fontSize: 28, margin: 0 }}>Assessment complete</h2>
          <p style={{ fontSize: 15, color: 'var(--k-ink-2)', lineHeight: 1.6, marginTop: 12 }}>
            You've already completed the Kalibr assessment. Your results are in your reports.
          </p>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginTop: 28 }}>
            <button className="k-btn k-btn--primary" onClick={() => onEnter('profile')}>View Profile & Blind spots →</button>
            <button className="k-btn k-btn--ghost" onClick={() => onEnter('constitution')}>View AI Constitution →</button>
          </div>
        </div>
      </div>
    );
  }

  // Dev tool: randomly fill all items
  const devFill = () => {
    const next = {};
    for (const it of allItems) {
      if (it.kind === 'likert') {
        const r = Math.random();
        next[it.id] = r < 0.10 ? 1 : r < 0.30 ? 2 : r < 0.55 ? 3 : r < 0.85 ? 4 : 5;
      } else {
        const letters = it.letters || ['A', 'B', 'C', 'D', 'E'].slice(0, it.options.length);
        next[it.id] = letters[Math.floor(Math.random() * letters.length)];
      }
    }
    setAnswers(next);
    setPageIdx(totalPages - 1);
  };

  // [EDIT 2026-05-11] finish() — uses real user ID, computes quality metrics client-side, calls backend /score Qwen 5/12
  const finish = async () => {
    if (submitting) return;
    setSubmitting(true);
    try {
      // Use logged-in user's ID so profile/reports are tied to their account
      let respondentId;
      let participantName, participantEmail, participantRole, participantOrg;
      try {
        const user = JSON.parse(localStorage.getItem('kalibr.user') || '{}');
        respondentId    = user.id;
        participantName  = user.name  || null;
        participantEmail = user.email || null;
        participantRole  = user.role  || null;
        participantOrg   = user.org   || null;
      } catch(e) {}
      respondentId = respondentId || ('user_' + Date.now());

      // Client-side quality metrics (backend may refine these on /score)
      const likertVals = Object.values(answers).filter(v => typeof v === 'number');
      const pctMidpoint  = likertVals.length ? Math.round(likertVals.filter(v => v === 3).length / likertVals.length * 100) : 0;
      const pctAgreement = likertVals.length ? Math.round(likertVals.filter(v => v >= 4).length / likertVals.length * 100) : 0;
      const elapsedMs    = startTimeRef.current != null ? Date.now() - startTimeRef.current : null;
      const completionMinutes = elapsedMs != null ? Math.round(elapsedMs / 6000) / 10 : null;

      const card = {
        respondent: { id: respondentId },
        rawResponses: answers,
        quality: { pctMidpoint, pctAgreement, completionMinutes },
      };
      const existing = JSON.parse(localStorage.getItem('kalibr.be.cards') || '{}');
      existing[respondentId] = { email: participantEmail || 'anonymous', card, at: Date.now() };
      localStorage.setItem('kalibr.be.cards', JSON.stringify(existing));

      // Send responses and demographic data to backend for scoring and persistence
      await fetch('/score', {
        method: 'POST',
        headers: window.kalibrAuth.getHeaders({ 'Content-Type': 'application/json' }),
        body: JSON.stringify({
          responses:         answers,
          respondent_id:     respondentId,
          participant_name:  participantName,
          participant_email: participantEmail,
          participant_role:  participantRole,
          participant_org:   participantOrg,
        }),
      }).catch(err => console.warn('[Kalibr] /score request failed:', err));
    } catch (e) {
      console.warn('[Kalibr] Could not persist to localStorage:', e);
    }
    onEnter('completion');
  };

  // [EDIT 2026-05-11] Show onboarding steps before questions begin
  if (phase === 'onboarding') {
    const s    = ONBOARDING_STEPS[step];
    const last = step === ONBOARDING_STEPS.length - 1;
    return (
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh', padding: 32 }}>
        <div className="k-card" style={{ width: isMobile ? '100%' : 560, padding: isMobile ? '28px 20px' : '44px 40px 36px' }}>
          <div style={{ display: 'flex', gap: 6, marginBottom: 28 }}>
            {ONBOARDING_STEPS.map((_, i) => (
              <div key={i} style={{ flex: 1, height: 3, borderRadius: 2, background: i <= step ? 'var(--k-accent)' : 'var(--k-line)' }} />
            ))}
          </div>
          <div style={{ fontSize: 44, color: 'var(--k-accent)', marginBottom: 18 }}>{s.icon}</div>
          <h2 className="k-serif" style={{ fontSize: 30, margin: 0, letterSpacing: '-0.02em' }}>{s.title}</h2>
          <p style={{ fontSize: 15.5, lineHeight: 1.6, color: 'var(--k-ink-2)', marginTop: 12 }}>{s.body}</p>
          {last && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginTop: 16, padding: 14, borderRadius: 12, background: 'var(--k-surface-sunk)' }}>
              {[
                ['data', 'Store my responses for scoring and report generation.'],
                ['pair', 'Let me invite a partner to unlock the pairwise reports.'],
                ['ai',   'Generate a personal AI constitution and let me copy it off-platform.'],
              ].map(([k, v]) => (
                <label key={k} style={{ display: 'flex', gap: 10, alignItems: 'flex-start', fontSize: 13.5, color: 'var(--k-ink-2)', cursor: 'pointer' }}>
                  <input type="checkbox" checked={consents[k]} onChange={e => setConsents(c => ({ ...c, [k]: e.target.checked }))}
                    style={{ marginTop: 3, accentColor: 'var(--k-accent)' }} />
                  {v}
                </label>
              ))}
            </div>
          )}
          <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 28 }}>
            <button className="k-btn k-btn--ghost" onClick={() => step > 0 ? setStep(step - 1) : onEnter('landing')}>
              {step === 0 ? 'Back to overview' : 'Back'}
            </button>
            {last
              ? <button className="k-btn k-btn--primary"
                  disabled={!(consents.data && consents.ai)}
                  style={{ opacity: consents.data && consents.ai ? 1 : 0.4 }}
                  onClick={() => { startTimeRef.current = Date.now(); setPhase('questions'); }}>
                  Begin assessment →
                </button>
              : <button className="k-btn k-btn--primary" onClick={() => setStep(step + 1)}>Continue</button>
            }
          </div>
        </div>
      </div>
    );
  }

  // [EDIT 2026-05-11] Loading guard now checks pageItems instead of single item
  if (!pageItems.length) {
    return (
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100vh', color: 'var(--k-ink-3)', fontSize: 14 }}>
        Loading assessment…
      </div>
    );
  }

  const sectionLabel = pageItems[0].kind === 'likert'
    ? 'Section 1 · Psychometric items'
    : 'Section 2 · AI preferences';

  return (
    <div style={{ display: 'grid', gridTemplateRows: 'auto 1fr auto', height: '100vh' }}>

      {/* ── Header ─────────────────────────────────────────────── */}
      <div style={{
        padding: isMobile ? '14px 20px' : '20px 48px', borderBottom: '1px solid var(--k-line)',
        display: 'flex', justifyContent: 'space-between', alignItems: 'center',
      }}>
        <div className="k-label">{sectionLabel}</div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
          <div className="k-mono" style={{ fontSize: 12, color: 'var(--k-ink-3)' }}>
            {answered}/{total} answered
          </div>
          <div style={{ width: 220, height: 4, background: 'var(--k-line)', borderRadius: 2, overflow: 'hidden' }}>
            <div style={{ width: `${pct}%`, height: '100%', background: 'var(--k-accent)', transition: 'width .3s' }} />
          </div>
          <button onClick={devFill} style={{
            fontSize: 12, padding: '6px 12px', border: '1px solid var(--k-line)', borderRadius: 8,
            background: 'var(--k-surface)', color: 'var(--k-ink-3)', cursor: 'pointer', fontFamily: 'inherit',
          }}>Dev: fill all</button>
        </div>
      </div>

      {/* ── Three items per page, stacked vertically ── */}
      <div style={{ overflowY: 'auto', padding: isMobile ? '20px 16px' : '32px 48px', display: 'flex', flexDirection: 'column', alignItems: 'center' }} className="k-scroll">
        <div style={{ maxWidth: 760, width: '100%', display: 'flex', flexDirection: 'column', gap: 24 }}>
          {pageItems.map((it, i) => (
            <AssessmentItem
              key={it.id}
              item={it}
              index={pageIdx * PAGE_SIZE + i + 1}
              value={answers[it.id]}
              onChange={v => setVal(it.id, v)}
            />
          ))}
          <div style={{ fontSize: 12, color: 'var(--k-ink-4)', padding: '12px 16px', background: 'var(--k-bg-2)', borderRadius: 10 }}>
            Answer honestly. The engine runs response-quality checks — if more than 55% of your answers sit at the midpoint, your report gets flagged as <em>Central Tendency</em>.
          </div>
        </div>
      </div>

      {/* ── Footer ── */}
      <div style={{
        padding: isMobile ? '12px 16px' : '16px 48px', borderTop: '1px solid var(--k-line)',
        display: 'flex', justifyContent: 'flex-end', alignItems: 'center', gap: 12,
        background: 'var(--k-bg-2)',
      }}>
        <div style={{ fontSize: 12.5, color: 'var(--k-ink-3)', marginRight: 'auto' }}>
          Your responses save automatically.
        </div>
        {allAnswered ? (
          <button className="k-btn k-btn--accent" onClick={finish} disabled={submitting} style={{ opacity: submitting ? 0.5 : 1 }}>
            {submitting ? 'Submitting…' : 'Submit assessment'}
          </button>
        ) : pageIdx >= totalPages - 1 && (
          <span style={{ fontSize: 12, color: 'var(--k-ink-3)' }}>
            {total - answered} question{total - answered !== 1 ? 's' : ''} remaining
          </span>
        )}
      </div>
    </div>
  );
}

// ── AssessmentItem ────────────────────────────────────────────────────
// Handles both likert (1–5 scale) and mc (multiple choice with letters).
function AssessmentItem({ item, index, value, onChange }) {
  return (
    <div className="k-card" style={{ padding: '22px 24px' }}>
      <div style={{ display: 'flex', gap: 16, alignItems: 'flex-start' }}>
        <div className="k-mono" style={{ fontSize: 12, color: 'var(--k-ink-4)', paddingTop: 3, minWidth: 32 }}>
          {String(index).padStart(2, '0')}
        </div>
        <div style={{ flex: 1 }}>
          <div className="k-serif" style={{ fontSize: 20, letterSpacing: '-0.01em', lineHeight: 1.35 }}>
            {item.text}
          </div>

          {item.kind === 'likert' ? (
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 6, marginTop: 16 }}>
              {LIKERT_LABELS.map((l, i) => {
                const v = i + 1;
                const selected = value === v;
                return (
                  <button key={l} onClick={() => onChange(v)} style={{
                    padding: '10px 4px',
                    border: '1px solid ' + (selected ? 'var(--k-accent)' : 'var(--k-line)'),
                    borderRadius: 10,
                    background: selected ? 'var(--k-accent-soft)' : 'var(--k-surface)',
                    color: selected ? 'var(--k-accent-ink)' : 'var(--k-ink-2)',
                    cursor: 'pointer', fontSize: 12, fontWeight: selected ? 600 : 500,
                    transition: 'all .12s', display: 'flex', flexDirection: 'column',
                    alignItems: 'center', gap: 6,
                  }}>
                    <span style={{
                      width: 10 + i * 2.5, height: 10 + i * 2.5, borderRadius: '50%',
                      background: selected ? 'var(--k-accent)' : 'var(--k-line-strong)',
                    }} />
                    <span style={{ textAlign: 'center', lineHeight: 1.15 }}>{l}</span>
                  </button>
                );
              })}
            </div>
          ) : (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginTop: 16 }}>
              {item.options.map((opt, i) => {
                const letter = (item.letters && item.letters[i]) || 'ABCDE'[i];
                const selected = value === letter;
                return (
                  <button key={opt} onClick={() => onChange(letter)} style={{
                    display: 'flex', alignItems: 'center', gap: 12, padding: '12px 14px',
                    border: '1px solid ' + (selected ? 'var(--k-accent)' : 'var(--k-line)'),
                    borderRadius: 10,
                    background: selected ? 'var(--k-accent-soft)' : 'var(--k-surface)',
                    color: selected ? 'var(--k-accent-ink)' : 'var(--k-ink-2)',
                    cursor: 'pointer', fontSize: 14, textAlign: 'left',
                    fontFamily: 'inherit', fontWeight: selected ? 500 : 400,
                  }}>
                    <span className="k-mono" style={{
                      width: 22, height: 22, borderRadius: 11, display: 'inline-flex',
                      alignItems: 'center', justifyContent: 'center', fontSize: 11,
                      background: selected ? 'var(--k-accent)' : 'var(--k-bg-2)',
                      color: selected ? '#fff' : 'var(--k-ink-3)',
                      flexShrink: 0,
                    }}>{letter}</span>
                    {opt}
                  </button>
                );
              })}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ── CompletionScreen ──────────────────────────────────────────────────
function CompletionScreen({ onEnter }) {
  const live = window.useLiveScores ? window.useLiveScores() : null;

  // Read the card the user just submitted
  let cardQuality = {};
  try {
    const raw = localStorage.getItem('kalibr.be.cards');
    if (raw) {
      const cards = JSON.parse(raw);
      const keys = Object.keys(cards);
      if (keys.length) cardQuality = cards[keys[keys.length - 1]]?.card?.quality || {};
    }
  } catch(e) {}

  // Backend quality (if /score has already responded) overrides client estimates
  const backendQ = live?.quality || {};
  const qualFlag       = backendQ.flag;
  const pctMidpoint    = backendQ.pctMidpoint  ?? cardQuality.pctMidpoint;
  const pctAgreement   = backendQ.pctAgreement ?? cardQuality.pctAgreement;
  const reliability    = backendQ.reliability  || (live ? 'HIGH' : null);
  const completionMins = cardQuality.completionMinutes;

  const fmtTime = (m) => {
    if (m == null) return '—';
    const mins = Math.floor(m), secs = Math.round((m - mins) * 60);
    return `${mins} min ${secs} s`;
  };

  // Derive display values from quality flag when exact percentages aren't in localStorage
  // (e.g. after re-login: DB stores flag/reliability but not raw percentages)
  const midFlagKnown = live && qualFlag && qualFlag !== 'CENTRAL_TENDENCY';
  const acqFlagKnown = live && qualFlag && qualFlag !== 'ACQUIESCENCE';
  const midDisplay = pctMidpoint  != null ? `${pctMidpoint}% midpoint — ${pctMidpoint > 55 ? 'flagged' : 'within range'}` : midFlagKnown ? 'Within range' : qualFlag === 'CENTRAL_TENDENCY' ? 'Flagged — high midpoint use' : 'Computing…';
  const acqDisplay = pctAgreement != null ? `${pctAgreement}% agreement — ${pctAgreement > 70 ? 'flagged' : 'within range'}` : acqFlagKnown ? 'Within range' : qualFlag === 'ACQUIESCENCE' ? 'Flagged — high agreement bias' : 'Computing…';

  const midStatus = qualFlag === 'CENTRAL_TENDENCY' ? 'warn' : pctMidpoint != null ? (pctMidpoint > 55 ? 'warn' : 'ok') : midFlagKnown ? 'ok' : 'pending';
  const acqStatus = qualFlag === 'ACQUIESCENCE'     ? 'warn' : pctAgreement != null ? (pctAgreement > 70 ? 'warn' : 'ok') : acqFlagKnown ? 'ok' : 'pending';

  const checks = [
    { label: 'Completion time',      value: fmtTime(completionMins),                                                                              status: completionMins == null ? 'pending' : 'ok' },
    { label: 'Central tendency',     value: midDisplay,                                                                                           status: midStatus },
    { label: 'Acquiescence check',   value: acqDisplay,                                                                                           status: acqStatus },
    { label: 'Internal consistency', value: backendQ.internalConsistency || (live ? 'All 10 dimensions passed' : 'Computing…'),                   status: live ? 'ok' : 'pending' },
    { label: 'Reliability flag',     value: reliability || 'Computing…',                                                                          status: reliability ? 'ok' : 'pending' },
  ];

  // ── Report generation state ──────────────────────────────────────────
  const paid = window.kalibrIsPaid ? window.kalibrIsPaid() : false;
  const [genState, setGenState] = React.useState('idle');   // idle | running | done | error
  const [genError, setGenError] = React.useState(null);
  const [reportReady, setReportReady] = React.useState(
    !!(window.__kalibr_gemini_profile && window.__kalibr_gemini_blindspots && window.__kalibr_gemini_constitution)
  );

  const generateReport = () => {
    if (!live || genState === 'running') return;
    setGenState('running');
    setGenError(null);
    window.__kalibr_gen_running = true;

    const basePayload = {
      dimensions:    live.dimensions,
      scaleMeans:    live.scaleMeans    || {},
      supplementary: live.supplementary || {},
      aiPreferences: live.aiPreferences || {},
      name:  window.__kalibr_participant_name  || 'You',
      email: window.__kalibr_participant_email || '',
      role:  window.__kalibr_participant_role  || '',
      context: window.__kalibr_participant_context || '',
    };

    // Navigate immediately — all 3 fetches run in background in parallel
    onEnter('profile');

    const h = window.kalibrAuth.getHeaders({ 'Content-Type': 'application/json' });
    const post = (path) => fetch(path, { method: 'POST', headers: h, body: JSON.stringify(basePayload) })
      .then(r => { if (!r.ok) throw new Error(path + ' failed (' + r.status + ')'); return r.json(); });

    Promise.all([post('/generate/profile'), post('/generate/blindspots'), post('/generate/constitution')])
      .then(([profile, blindspots, constitution]) => {
        window.__kalibr_gemini_profile      = profile;
        window.__kalibr_gemini_blindspots   = blindspots;
        window.__kalibr_gemini_constitution = constitution;
        window.__kalibr_gen_running = false;
        window.dispatchEvent(new CustomEvent('kalibr-reports-ready'));
        if (window.kalibrToast) window.kalibrToast('Your report is ready — profile, blind spots & AI constitution.');
        setGenState('done');
        setReportReady(true);
      })
      .catch(err => {
        window.__kalibr_gen_running = false;
        setGenError(err.message);
        setGenState('error');
        if (window.kalibrToast) window.kalibrToast('Report generation failed: ' + err.message, 'error');
      });
  };

  const scoresReady = !!live;

  return (
    <>
      <div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 32 }}>
        <div style={{ maxWidth: 680, width: '100%' }}>
          <div style={{ textAlign: 'center', marginBottom: 32 }}>
            <div style={{
              width: 72, height: 72, borderRadius: 36,
              background: 'var(--k-sage-soft)', color: 'var(--k-sage-ink)',
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              fontSize: 32, marginBottom: 20,
            }}>✓</div>
            <h2 className="k-serif" style={{ fontSize: 40, margin: 0, letterSpacing: '-0.02em' }}>
              Assessment complete
            </h2>
            <p style={{ fontSize: 16, color: 'var(--k-ink-2)', maxWidth: 480, margin: '14px auto 0', lineHeight: 1.55 }}>
              Your 10-dimension scores are calculated by a fixed algorithm — deterministic, not AI. The narrative report is where AI comes in.
            </p>
          </div>

          <div className="k-card" style={{ padding: 4 }}>
            {checks.map((c, i) => (
              <div key={c.label} style={{
                display: 'grid', gridTemplateColumns: '1fr auto auto', gap: 16, alignItems: 'center',
                padding: '14px 20px',
                borderBottom: i < checks.length - 1 ? '1px solid var(--k-line)' : 'none',
              }}>
                <div style={{ fontSize: 13.5, color: 'var(--k-ink-2)', fontWeight: 500 }}>{c.label}</div>
                <div className="k-mono" style={{ fontSize: 12.5, color: 'var(--k-ink-3)' }}>{c.value}</div>
                <span className={c.status === 'warn' ? 'k-chip k-chip--sun' : c.status === 'pending' ? 'k-chip' : 'k-chip k-chip--sage'}>
                  {c.status === 'warn' ? 'REVIEW' : c.status === 'pending' ? '…' : 'PASS'}
                </span>
              </div>
            ))}
          </div>

          {/* ── 10-dimension scores ─────────────────────────────────── */}
          {live ? (
            <div style={{ marginTop: 24 }}>
              <div className="k-label" style={{ marginBottom: 10 }}>10-dimension scores</div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
                {[
                  ['philosophyCohesion',   'Philosophy Cohesion'],
                  ['driveAlignment',       'Drive Alignment'],
                  ['bondingIndex',         'Bonding Index'],
                  ['adaptiveIntelligence', 'Adaptive Intelligence'],
                  ['volatilityVector',     'Volatility Vector'],
                  ['ambiguityTolerance',   'Ambiguity Tolerance'],
                  ['influenceStyle',       'Influence Style'],
                  ['feedbackOrientation',  'Feedback Orientation'],
                  ['temporalOrientation',  'Temporal Orientation'],
                  ['energyResilience',     'Energy Resilience'],
                ].map(([id, label]) => {
                  const val = Math.round(live.dimensions[id] || 0);
                  const lock = !paid && id !== 'feedbackOrientation';
                  return (
                    <div key={id} style={{ padding: '10px 14px', background: 'var(--k-bg-2)', borderRadius: 10, ...(lock ? { filter: 'blur(7px)', userSelect: 'none', pointerEvents: 'none' } : {}) }}>
                      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 6 }}>
                        <span style={{ fontSize: 12.5, color: 'var(--k-ink-2)', fontWeight: 500 }}>{label}</span>
                        <span className="k-mono" style={{ fontSize: 13, color: 'var(--k-accent-ink)', fontWeight: 600 }}>{val}</span>
                      </div>
                      <div style={{ height: 3, background: 'var(--k-line)', borderRadius: 2, overflow: 'hidden' }}>
                        <div style={{ width: `${val}%`, height: '100%', background: 'var(--k-accent)', borderRadius: 2 }} />
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          ) : (
            <div style={{ marginTop: 24, padding: '16px 20px', background: 'var(--k-bg-2)', borderRadius: 12, textAlign: 'center', fontSize: 13, color: 'var(--k-ink-4)' }}>
              Scores calculating…
            </div>
          )}

          {genError && (
            <div style={{ marginTop: 16, padding: '12px 16px', background: 'var(--k-rose-soft)', borderRadius: 10, fontSize: 13, color: 'var(--k-rose-ink)' }}>
              Generation failed: {genError}. Please try again.
            </div>
          )}

          <div style={{ marginTop: 28, textAlign: 'center' }}>
            {!reportReady ? (
              <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}>
                {paid ? (
                  <button
                    className="k-btn k-btn--accent k-btn--lg"
                    disabled={!scoresReady || genState === 'running'}
                    style={{ opacity: scoresReady ? 1 : 0.45, minWidth: 240 }}
                    onClick={generateReport}
                  >
                    {!scoresReady ? 'Waiting for scores…' : genState === 'error' ? 'Retry generation' : 'Generate my report →'}
                  </button>
                ) : (
                  <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', justifyContent: 'center' }}>
                    <button
                      className="k-btn k-btn--ghost k-btn--lg"
                      disabled={!scoresReady || genState === 'running'}
                      style={{ opacity: scoresReady ? 1 : 0.45 }}
                      onClick={generateReport}
                    >
                      {!scoresReady ? 'Waiting for scores…' : genState === 'error' ? 'Retry generation' : 'Generate partial report (free) →'}
                    </button>
                    <button
                      className="k-btn k-btn--primary k-btn--lg"
                      disabled={!scoresReady}
                      style={{ opacity: scoresReady ? 1 : 0.45 }}
                      onClick={() => alert('Payments coming soon.\n\nPilot users: add ?unlock=true to your URL to access the full report.')}
                    >
                      Generate full report — $50
                    </button>
                  </div>
                )}
                {!scoresReady && (
                  <div style={{ fontSize: 12.5, color: 'var(--k-ink-4)' }}>
                    Scores are still being calculated — this takes about 10 seconds.
                  </div>
                )}
              </div>
            ) : (
              <div style={{ display: 'flex', gap: 12, justifyContent: 'center', flexWrap: 'wrap' }}>
                <button className="k-btn k-btn--primary k-btn--lg" onClick={() => onEnter('profile')}>
                  View full report →
                </button>
                <button className="k-btn k-btn--ghost k-btn--lg" onClick={() => onEnter('constitution')}>
                  AI constitution
                </button>
              </div>
            )}
          </div>
        </div>
      </div>
    </>
  );
}

Object.assign(window, { AssessmentScreen, AssessmentItem, CompletionScreen });
