/* global React */ /* ============================================================ Diagnostic pages - AuditLandingPage /audit - AuditFlowPage /audit/flow - AuditResultsPage /audit/results ============================================================ */ const { useState: aState, useEffect: aEffect, useRef: aRef, useMemo: aMemo } = React; /* ============================================================ Persistence — answers stored in sessionStorage ============================================================ */ const STORE_KEY = 'tdp.audit.v1'; function loadAudit() { try { const raw = sessionStorage.getItem(STORE_KEY); return raw ? JSON.parse(raw) : { answers: {}, contact: null, completedAt: null }; } catch { return { answers: {}, contact: null, completedAt: null }; } } function saveAudit(state) { try { sessionStorage.setItem(STORE_KEY, JSON.stringify(state)); } catch {} } /* ============================================================ AUDIT LANDING — the threshold page ============================================================ */ function AuditLandingPage() { return (
{/* atmospheric backdrop slot */}
Executive Authority Signal Audit

A short, deliberate reading of where authority leaks.

Ten questions. Roughly seven minutes. The audit asks how you respond under pressure that has consequence.

There is no score. The result is a private reading. One dominant signature of how your authority leaks under load, one secondary, and a short observation on each. The full distribution of all four signatures appears as supporting context, never as a ranking.

Not what this is
    Not a personality test. Not therapy or journaling. Not a leadership self-assessment. Not optimised for a score.
What this is

A structured, psychologically intelligent reading of authority under consequence.

Each question presents a moment senior leaders actually live in. A board pushing back. A decision being revisited. A room that does not give. There is no right answer. The audit reads the pattern across your answers, not any one of them.

); } function Spec({ k, l }) { return (
{k}
{l}
); } function NegItem({ children }) { return (
  • {children}
  • ); } /* The visual blueprint panel — gives the landing some atmospheric weight without showing the questions */ function BlueprintPanel() { return (
    The territory
    and five more
    ); } /* ============================================================ AUDIT FLOW — one question per screen ============================================================ */ function AuditFlowPage() { const router = useRouter(); const tw = useTw(); const pacing = pacingMs(tw.diagnosticPacing || 'measured'); const [state, setState] = aState(() => loadAudit()); // Name-gate — required before Q1 so the audit can address them // personally and so Daniel has a first name attached to any // submitted reading. const firstName = (state.contact && state.contact.firstName) || ''; const [showNameGate, setShowNameGate] = aState(() => !firstName); const [pendingName, setPendingName] = aState(''); // If audit is already complete, route to results so the user doesn't // land on Q10 again on revisit. aEffect(() => { if (state.completedAt && Object.keys(state.answers).length === AUDIT_QUESTIONS.length) { router.go('/audit/results'); } // intentionally only run on mount // eslint-disable-next-line }, []); const submitName = (e) => { e.preventDefault(); const name = pendingName.trim(); if (!name) return; const next = { ...state, contact: { ...(state.contact || {}), firstName: name }, }; setState(next); saveAudit(next); setShowNameGate(false); }; const [step, setStep] = aState(() => { const a = loadAudit(); // resume at first unanswered question const answered = Object.keys(a.answers).length; return Math.min(answered, AUDIT_QUESTIONS.length - 1); }); const [transitioning, setTransitioning] = aState(false); const [direction, setDirection] = aState(1); // persist state on every change aEffect(() => { saveAudit(state); }, [state]); const total = AUDIT_QUESTIONS.length; const q = AUDIT_QUESTIONS[step]; if (showNameGate) { return (
    Before we begin

    What should I call you?

    Your first name is enough. The audit reads better when it speaks to you, not at you.

    Held in confidence. No newsletter. Nothing shared.

    setPendingName(e.target.value)} placeholder="Daniel" />
    ≈ 7 minutes · 10 questions
    ); } const selected = state.answers[q.id] || null; const answer = (sig) => { if (transitioning) return; const next = { ...state, answers: { ...state.answers, [q.id]: sig } }; setState(next); setTimeout(() => { setTransitioning(true); setDirection(1); setTimeout(() => { if (step < total - 1) { setStep(step + 1); } else { // Done — go to results const done = { ...next, completedAt: Date.now() }; saveAudit(done); router.go('/audit/results'); return; } setTransitioning(false); }, pacing.transition); }, pacing.beforeAdvance); }; const goBack = () => { if (step === 0 || transitioning) return; setDirection(-1); setTransitioning(true); setTimeout(() => { setStep(step - 1); setTransitioning(false); }, pacing.transition * 0.7); }; return (
    {/* progress header */}
    Question {step + 1} of {total}. {q.eyebrow}. {q.prompt}
    {/* question */}
    {/* footer bar */}
    {step > 0 && ( )}
    Held in confidence
    ); } function ProgressRail({ step, total }) { return (
    The Audit
    {Array.from({ length: total }).map((_, i) => (
    ))}
    {String(step + 1).padStart(2, '0')} / {String(total).padStart(2, '0')}
    ); } function QuestionCard({ q, selected, onSelect, transitioning, direction, step, total }) { const tw = useTw(); const pacing = pacingMs(tw.diagnosticPacing || 'measured'); return (
    0 ? -12 : 12}px)` : 'translateY(0)', transition: `opacity ${pacing.transition * 0.7}ms var(--ease-out), transform ${pacing.transition * 0.7}ms var(--ease-out)`, }}>
    {String(step + 1).padStart(2, '0')} / {String(total).padStart(2, '0')}
    {q.eyebrow}

    Choose the response closest to the truth, not closest to the answer you would prefer.

    {q.prompt}

    {q.options.map((opt, i) => ( onSelect(opt.sig)} hint={selected === opt.sig} /> ))}
    ); } function OptionRow({ idx, label, selected, onClick }) { const [hover, setHover] = aState(false); return ( ); } /* ============================================================ AUDIT RESULTS ============================================================ */ function AuditResultsPage() { const router = useRouter(); const [state, setState] = aState(() => loadAudit()); const [email, setEmail] = aState(''); const [lastName, setLastName] = aState(''); const [unlocking, setUnlocking] = aState(false); const [unlockError, setUnlockError] = aState(null); const loadedAt = aMemo(() => Date.now(), []); const firstName = (state.contact && state.contact.firstName) || ''; const unlocked = !!(state.contact && state.contact.email); const answered = Object.keys(state.answers).length; const result = aMemo(() => answered === AUDIT_QUESTIONS.length ? scoreAudit(state.answers) : null, [state.answers]); const unlockReading = (e) => { e.preventDefault(); if (!email.trim() || !lastName.trim() || !result) return; setUnlocking(true); setUnlockError(null); fetch('/api/audit-lead.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ firstName, lastName: lastName.trim(), email: email.trim(), primary: result.primary, secondary: result.secondary, total: result.total, primaryCount: result.counts ? result.counts[result.primary] : null, website: '', ts: loadedAt, }), }) .then((res) => res.json()) .then((data) => { setUnlocking(false); // Unlock locally regardless of email-send outcome — the reading // is already computed client-side and shouldn't be blocked by // a transient email issue. Log server errors silently. const next = { ...state, contact: { ...(state.contact || {}), lastName: lastName.trim(), email: email.trim(), capturedAt: Date.now(), }, }; setState(next); saveAudit(next); if (!data || !data.ok) { setUnlockError('Your reading is unlocked below. We had trouble emailing a copy — you can also save this page.'); } }) .catch(() => { setUnlocking(false); const next = { ...state, contact: { ...(state.contact || {}), lastName: lastName.trim(), email: email.trim(), capturedAt: Date.now(), }, }; setState(next); saveAudit(next); setUnlockError('Your reading is unlocked below. We had trouble emailing a copy — you can also save this page.'); }); }; // Animate reveal const [revealStage, setRevealStage] = aState(0); aEffect(() => { if (!result) return; const t1 = setTimeout(() => setRevealStage(1), 200); const t2 = setTimeout(() => setRevealStage(2), 800); const t3 = setTimeout(() => setRevealStage(3), 1600); return () => { clearTimeout(t1); clearTimeout(t2); clearTimeout(t3); }; }, [result]); if (!result) { return (
    The audit

    The audit has not been completed.

    The reading is generated only after all ten responses. Begin the audit, or resume where you left off.

    ); } const primary = SIGNATURES[result.primary]; const secondary = result.secondary ? SIGNATURES[result.secondary] : null; if (!unlocked) { return (
    The reading is ready

    {firstName ? `${firstName}, your reading is ready.` : 'Your reading is ready.'}

    Your full name and email open the reading on this page — and send a copy to your inbox, so it stays with you.

    Held in confidence. No newsletter. The reading is for you.

    setLastName(e.target.value)} /> setEmail(e.target.value)} />
    Reveal · send
    {unlockError && (

    {unlockError}

    )}
    ); } return (
    {/* Hero — the signature */}
    = 1 ? 1 : 0, transform: revealStage >= 1 ? 'translateY(0)' : 'translateY(12px)', transition: 'all 800ms var(--ease-out)', }}> The reading
    = 2 ? 1 : 0, transform: revealStage >= 2 ? 'translateY(0)' : 'translateY(16px)', transition: 'all 1100ms var(--ease-out)', }}>

    {primary.name}

    {primary.short}

    {/* Distribution bars */}
    = 3 ? 1 : 0, transform: revealStage >= 3 ? 'translateY(0)' : 'translateY(20px)', transition: 'all 1100ms var(--ease-out)', }}>
    {/* Imagery Moment — Reflection */}
    = 2 ? 1 : 0, transform: revealStage >= 2 ? 'translateY(0)' : 'translateY(20px)', transition: 'all 1200ms var(--ease-out)', }}>

    The reading reflects a pattern. Not a fixed identity. A habit of authority under pressure.

    The next step is a discovery conversation to explore what shifts are possible.

    {/* Reading */}
    Reading

    What the pattern says.

      {primary.reading.map((line, i) => (
    1. {String(i + 1).padStart(2, '0')}

      {line}

    2. ))}
    Pattern
    {primary.pattern}
    Cost
    {primary.cost}
    Work
    {primary.work}
    {/* Secondary */} {secondary && (
    Secondary signature

    {secondary.name}

    {secondary.short} It does not run the room as your primary signature does. It surfaces in moments of greater consequence.

    )}
    {/* Conversion */}
    The next step

    A reading is useful. A conversation is structural.

    If the reading recognises something, that is the indication that the underlying structure is correctable. A short, private conversation is the next deliberate step.

    A copy is in your inbox

    The reading you are looking at has been sent to {(state.contact && state.contact.email) || 'your email'}. It will keep, and it will not be sent to anyone else.

    When you are ready, the next step is a private conversation. No timeline pressure.

    ); } function SignatureBars({ counts, primary, secondary }) { const labels = { E: 'Explanation', C: 'Composure', D: 'Deflection', R: 'Recovery' }; const total = Object.values(counts).reduce((a, b) => a + b, 0) || 1; const max = Math.max(...Object.values(counts), 1); const order = Object.entries(counts).sort((a, b) => b[1] - a[1]); return (
    {order.map(([sig, n]) => { const isPrimary = sig === primary; const isSecondary = sig === secondary; return (
    {isPrimary ? 'Primary · ' : isSecondary ? 'Secondary · ' : ''}{labels[sig]}
    {n} / {total}
    ); })}
    ); } Object.assign(window, { AuditLandingPage, AuditFlowPage, AuditResultsPage });