import { useState, useEffect, useCallback, useRef } from "react";
const PK = "jm32-p";
function usePS(k, d) {
const [v, sV] = useState(d);
const [ok, sO] = useState(false);
useEffect(() => {
let done = false;
(async () => {
try { if (window.storage) { const r = await window.storage.get(k); if (r?.value) sV(JSON.parse(r.value)); } } catch (e) {}
if (!done) { done = true; sO(true); }
})();
setTimeout(() => { if (!done) { done = true; sO(true); } }, 2000);
}, [k]);
const s = useCallback((n) => sV((p) => {
const r = typeof n === "function" ? n(p) : n;
try { if (window.storage) window.storage.set(k, JSON.stringify(r)).catch(() => {}); } catch (e) {}
return r;
}), [k]);
return [v, s, ok];
}
const DEMO = {
complete: true, name: "Kanita Schultz",
whyStatement: "I exist to make mission-driven organizations technologically fearless, so the people doing good can focus on their mission instead of fighting their systems.",
coreTruth: "Underneath the professional surface: you don't just fix systems \u2014 you remove the obstacles between good people and their mission. And the tension that drives you: the anger you feel when NGOs suffer from bad IT isn't about technology \u2014 it's about justice.",
keyQuotes: ["Someone should fix this \u2014 that someone is usually me", "I get angry when organizations doing good are held back by terrible technology", "The best moment is when nobody notices the migration happened. Invisible excellence.", "I would still show up to help these organizations even if nobody paid me"],
bigFive: { O: "medium", C: "high", E: "medium", A: "high", N: "low", summary: "Exceptionally organized \u2014 you create structure from chaos. You lead through empathy. Emotionally steady under pressure. Balanced openness \u2014 you appreciate new ideas but value practical outcomes." },
disc: { D: "medium", I: "medium", S: "high", C: "high", summary: "You build reliable systems and support others. Your work style blends thoughtfulness with deep care for people." },
clifton: {
top5: [
{ name: "Restorative", domain: "Executing", desc: "Loves solving problems. Finds what's broken and fixes it." },
{ name: "Responsibility", domain: "Executing", desc: "Takes psychological ownership of commitments." },
{ name: "Belief", domain: "Executing", desc: "Guided by enduring core values that provide direction." },
{ name: "Developer", domain: "Relationship", desc: "Recognizes and cultivates potential in others." },
{ name: "Strategic", domain: "Strategic", desc: "Creates alternative ways to proceed. Spots patterns." },
],
dominantDomain: "Executing",
summary: "Your top strengths cluster around Executing. You lead with Restorative and Responsibility \u2014 loves solving problems and finds what's broken and fixes it, and takes psychological ownership of commitments.",
},
ikigai: { love: "Fixing broken systems and seeing people thrive because of it", good_at: "IT transformation, bridging tech and people, building structure from nothing", world_needs: "Mission-driven organizations that can actually function at scale", paid_for: "IT leadership, transformation programs, ITSM, M365 expertise" },
goldenCircle: { why: "To make technology invisible so that purpose becomes unstoppable", how: "Building systems, coaching teams, and treating IT as a strategic enabler", what: "IT transformation, M365 migration, ITSM implementation, security governance" },
motivation: "Using technology to help mission-driven organizations actually function properly. Too many NGOs waste donor money on bad IT.",
idealRole: "Leading IT transformation at an international NGO. Hands-on enough for technical decisions, strategic enough to shape the roadmap. Small team, big impact.",
dealbreakers: "No fully on-site, no organizations without clear values, no roles without budget ownership",
guideSessions: { mate: { complete: true, timesCompleted: 1, answers: {}, qIndex: 6 }, sinek: { complete: true, timesCompleted: 1, answers: {}, qIndex: 6 } },
};
const EP = {
complete: false, name: "",
whyStatement: "", coreTruth: "", keyQuotes: [],
bigFive: null, disc: null, clifton: null, ikigai: null, goldenCircle: null,
motivation: "", idealRole: "", dealbreakers: "",
guideSessions: {},
};
// ══════════════════════════════════════════════
// GUIDES: 4 guides x 3 tiers x 6 questions = 72 questions
// ══════════════════════════════════════════════
const GUIDES = [
{
id: "mate", name: "The Healer", thinker: "Gabor Maté", color: "#10b981", emoji: "\u{1F33F}",
tagline: "What needs are you meeting through work?",
desc: "Explores the emotional needs driving your career.",
intro: [
"I'm glad you're here. Most people never ask why they really do what they do. Let's explore that together.",
"Welcome back. Last time we looked at the surface. Now I want to go underneath.",
"Third time. No safety net. I'm asking things that might be uncomfortable. That's where the truth lives.",
],
tiers: [
[
{ q: "When you think about Monday morning, what happens in your body? Not thoughts \u2014 your body.", key: "monday_body" },
{ q: "What does your work give you that nothing else does? Be honest \u2014 control, recognition, escape, purpose?", key: "work_gives" },
{ q: "Growing up, what did adults praise you for? How does that connect to what you do now?", key: "childhood" },
{ q: "If you stopped working for three months, what would you lose besides money?", key: "stop_working" },
{ q: "Is the career you're pursuing what YOU want, or what you learned to want?", key: "authentic" },
{ q: "What kind of work makes you deeply uncomfortable? That discomfort is information.", key: "discomfort" },
],
[
{ q: "You said what work gives you. Now flip it \u2014 what does work take FROM you?", key: "work_takes" },
{ q: "When did you last cry because of work? Not stress \u2014 meaning. Something that moved you.", key: "cried_at_work" },
{ q: "Who in your childhood does your boss or work environment remind you of? First instinct.", key: "boss_reminds" },
{ q: "What are you trying to prove through your work? And to whom? Are they even watching?", key: "proving" },
{ q: "If your 8-year-old self saw your career, would they be proud, confused, or sad?", key: "child_sees" },
{ q: "What would you do if you knew you were already enough, without any achievement?", key: "already_enough" },
],
[
{ q: "What wound are you healing through your work? Every healer has one.", key: "wound" },
{ q: "You've built a career around being useful. Who are you when you're NOT useful?", key: "not_useful" },
{ q: "What are you most afraid someone at work will discover about you?", key: "afraid_discover" },
{ q: "If your body could talk about your career, what would it say?", key: "body_speaks" },
{ q: "What are you running FROM? Not toward \u2014 from.", key: "running_from" },
{ q: "Complete this: 'If I stop achieving, I will...'", key: "stop_achieving" },
],
],
},
{
id: "perel", name: "The Provocateur", thinker: "Esther Perel", color: "#f43f5e", emoji: "\u{1F525}",
tagline: "Who are you when no one at work is watching?",
desc: "Challenges your work identity and the roles you play.",
intro: [
"Let's skip the LinkedIn version. I want the real person. Fair warning \u2014 I push.",
"Back for more? Good. Last time was the warm-up. Now we go where it's messy.",
"Third round. Let's talk about what you perform and what you hide.",
],
tiers: [
[
{ q: "Strip away title, salary, and what others think. What's left?", key: "no_title" },
{ q: "What mask do you wear at work? What would happen if you dropped it?", key: "mask" },
{ q: "Do you stay in your role because you want to, or because you're afraid of leaving?", key: "stay_or_fear" },
{ q: "In your team: caretaker, rebel, fixer, performer, or peacekeeper? Chosen or inherited?", key: "team_role" },
{ q: "When did work last genuinely excite you \u2014 intellectually, creatively, emotionally?", key: "excitement" },
{ q: "What professional ambition have you never said out loud?", key: "secret_ambition" },
],
[
{ q: "You described your mask. When did you first put it on? What taught you to perform?", key: "first_mask" },
{ q: "Who at work do you secretly envy? What do they have that you want?", key: "envy" },
{ q: "If your career were a relationship: in love, comfortable, codependent, or looking for the exit?", key: "career_relationship" },
{ q: "What's the most selfish career move you've wanted to make? The one that serves only you?", key: "selfish_move" },
{ q: "Power. Do you take it, share it, avoid it, or resent those who have it?", key: "power" },
{ q: "When someone disappoints you at work: fix it, withdraw, or fight?", key: "disappointment" },
],
[
{ q: "What would your work life look like if you stopped trying to be liked?", key: "stop_liked" },
{ q: "What if you're extraordinary at something you've never let yourself try?", key: "untried" },
{ q: "Are you building a career or an escape room? What would you escape from without work?", key: "escape_room" },
{ q: "Desire is different from ambition. Ambition is what you chase. Desire is what chases you. What is yours?", key: "desire" },
{ q: "One message to everyone you've ever worked with \u2014 fully honest, no consequences. What?", key: "one_message" },
{ q: "Your career has been choices. Which choice was actually a surrender?", key: "surrender" },
],
],
},
{
id: "sinek", name: "The Clarifier", thinker: "Simon Sinek", color: "#f59e0b", emoji: "\u{2B55}",
tagline: "People don't buy what you do. They buy why you do it.",
desc: "Finds your Why through stories and patterns.",
intro: [
"Your Why isn't invented \u2014 it's discovered in stories you've lived. I'll ask for stories, then find the thread.",
"We found threads last time. Now let's pull harder and see what unravels.",
"Third session. Your pattern should be undeniable now. Let's make it impossible to ignore.",
],
tiers: [
[
{ q: "A specific time at work when you felt most alive. Not biggest achievement \u2014 most alive.", key: "best_moment" },
{ q: "A time you helped someone and it stuck with you. Beyond the task itself.", key: "helped_someone" },
{ q: "When did you fight for something others didn't care about? Why couldn't you let go?", key: "fought_for" },
{ q: "Your closest friend explains to a stranger why people love working with you. What do they say?", key: "friend_says" },
{ q: "What would the world lose if you stopped doing what you do?", key: "world_loses" },
{ q: "What did you love as a kid that you still do today, in a different form?", key: "childhood_thread" },
],
[
{ q: "Your best moment \u2014 what made it different from hundreds of similar ones? The specific ingredient?", key: "ingredient" },
{ q: "Three people you've genuinely helped. What do they have in common?", key: "helped_pattern" },
{ q: "You fight for things others ignore. What does that cost you? Why do you keep paying?", key: "cost_of_fight" },
{ q: "When you're at your worst at work \u2014 checked out, frustrated \u2014 what's missing?", key: "whats_missing" },
{ q: "A project that failed but still felt meaningful. Why didn't the failure erase the meaning?", key: "meaningful_failure" },
{ q: "If you could bottle what you bring to a team, what would the label say?", key: "bottled" },
],
[
{ q: "A moment from before age 16 that still shapes how you work today.", key: "before_16" },
{ q: "You know what you contribute. What do you NEED in return? What can't you ask for?", key: "need_in_return" },
{ q: "If your contribution and your pain come from the same place \u2014 what is that place?", key: "same_place" },
{ q: "Unlimited resources, one year. Not a business \u2014 a legacy. What do you build?", key: "legacy" },
{ q: "Every Why has a shadow \u2014 the toxic version. What does yours look like?", key: "shadow" },
{ q: "Finish this: 'I exist to ___, so that ___.'", key: "why_sentence" },
],
],
},
{
id: "harari", name: "The Philosopher", thinker: "Yuval Noah Harari", color: "#8b5cf6", emoji: "\u{1F52E}",
tagline: "What story about yourself have you chosen to believe?",
desc: "Examines the narratives shaping your career.",
intro: [
"You didn't choose most beliefs shaping your career. They were handed to you. Let's find which are yours.",
"You've started questioning stories. Now let's question the questioner.",
"Final round. We've examined stories and beliefs. Now the storyteller \u2014 you.",
],
tiers: [
[
{ q: "The story you tell people about your career. Now \u2014 what's NOT in that story?", key: "story" },
{ q: "What belief about success did you absorb growing up and never question?", key: "belief" },
{ q: "Your title didn't exist 100 years ago. Why does it matter now?", key: "beyond_title" },
{ q: "An alien observes your career. What do they conclude you actually care about?", key: "alien_view" },
{ q: "What story do you WANT to be true about your work? Not what is \u2014 what you wish?", key: "desired_story" },
{ q: "You in 10 years looks back at now. What do they wish you'd done?", key: "future_self" },
],
[
{ q: "The story you tell others vs the story at 3am when you can't sleep. What's different?", key: "3am_story" },
{ q: "Your career mythology \u2014 the hero's journey version. What part is actually fiction?", key: "fiction" },
{ q: "If your entire industry vanished tomorrow \u2014 not income, your identity. What happens?", key: "identity_collapse" },
{ q: "Which belief about success do you KNOW is false but can't stop following?", key: "false_belief" },
{ q: "History is written by winners. Who lost in your career story? What did you sacrifice?", key: "who_lost" },
{ q: "One thing about work you could un-learn. What would it be?", key: "unlearn" },
],
[
{ q: "What if your entire career has been an answer to a question nobody actually asked?", key: "wrong_question" },
{ q: "Meritocracy, purpose, growth. What if none are real? What would you do then?", key: "nothing_real" },
{ q: "The most dangerous story is the one you can't see. What's yours?", key: "invisible_story" },
{ q: "Humans cooperate around fictions. What fiction is your career cooperating around?", key: "cooperation_fiction" },
{ q: "Write your career in third person. What does the reader see that the character can't?", key: "third_person" },
{ q: "After everything: Who are you, really? Not role, not story, not achievements. Who?", key: "who_really" },
],
],
},
];
// ══════════════════════════════════════════════
// GUIDE CHAT
// ══════════════════════════════════════════════
function GuideChat({ guide, profile, up, onBack }) {
const session = profile.guideSessions[guide.id] || { answers: {}, complete: false, qIndex: 0, timesCompleted: 0 };
const tier = Math.min(session.timesCompleted || 0, 2);
const questions = guide.tiers[tier];
const introText = guide.intro[tier];
const [answers, setAnswers] = useState(session.complete ? {} : (session.answers || {}));
const [qIndex, setQIndex] = useState(session.complete ? 0 : (session.qIndex || 0));
const [input, setInput] = useState("");
const [freshStart, setFreshStart] = useState(session.complete);
const [showIntro, setShowIntro] = useState(session.complete || (qIndex === 0 && Object.keys(answers).length === 0));
const endRef = useRef(null);
const save = (ans, qi, done) => {
const tc = done ? (session.timesCompleted || 0) + 1 : (session.timesCompleted || 0);
const merged = { ...(session.answers || {}), ...ans };
up({ guideSessions: { ...profile.guideSessions, [guide.id]: { answers: merged, qIndex: qi, complete: done || false, timesCompleted: tc } } });
};
useEffect(() => { endRef.current?.scrollIntoView({ behavior: "smooth" }); }, [qIndex, showIntro]);
const currentQ = questions[qIndex];
const isDone = qIndex >= questions.length;
const send = () => {
if (!input.trim()) return;
const t = input.trim();
setInput("");
if (!profile.name && qIndex === 0) {
const words = t.split(" ").filter((w) => w.length > 1);
if (words.length <= 3) up({ name: words.map((w) => w[0].toUpperCase() + w.slice(1).toLowerCase()).join(" ") });
}
const newAns = { ...answers, [currentQ.key]: t };
setAnswers(newAns);
const next = qIndex + 1;
setQIndex(next);
save(newAns, next, next >= questions.length);
};
const startChat = () => {
if (freshStart) {
setAnswers({});
setQIndex(0);
setFreshStart(false);
setShowIntro(false);
} else {
setShowIntro(false);
}
};
// Build conversation
const convo = [];
for (let i = 0; i < Math.min(qIndex, questions.length); i++) {
convo.push({ from: "bot", text: questions[i].q });
if (answers[questions[i].key]) convo.push({ from: "user", text: answers[questions[i].key] });
}
if (!isDone && !showIntro && !freshStart) convo.push({ from: "bot", text: questions[qIndex].q });
return (
<div style={{ display: "flex", flexDirection: "column", height: "calc(100vh - 140px)", minHeight: 400 }}>
<div style={{ display: "flex", alignItems: "center", gap: 10, padding: "12px 0", borderBottom: "1px solid #1e293b" }}>
<button onClick={onBack} style={btnS}>{"\u2190"} Back</button>
<span style={{ fontSize: 22 }}>{guide.emoji}</span>
<div>
<div style={{ fontSize: 15, fontWeight: 700, color: guide.color }}>{guide.name}</div>
<div style={{ fontSize: 11, color: "#64748b" }}>{guide.thinker}</div>
</div>
<div style={{ marginLeft: "auto", textAlign: "right" }}>
<div style={{ fontSize: 11, color: "#64748b" }}>Tier {tier + 1}/3</div>
<div style={{ fontSize: 11, color: "#64748b" }}>{Math.min(qIndex, questions.length)}/{questions.length}</div>
</div>
</div>
<div style={{ height: 3, background: "#1e293b" }}>
<div style={{ height: "100%", background: guide.color, width: (qIndex / questions.length * 100) + "%", transition: "width 0.3s" }} />
</div>
<div style={{ flex: 1, overflowY: "auto", padding: "12px 0", display: "flex", flexDirection: "column", gap: 10 }}>
{(showIntro || freshStart) && (
<div style={{ padding: "14px 16px", background: guide.color + "12", border: "1px solid " + guide.color + "25", borderRadius: 16, borderBottomLeftRadius: 4, maxWidth: "85%", fontSize: 14, lineHeight: 1.7, color: "#e2e8f0" }}>
{introText}
{tier > 0 && <div style={{ marginTop: 8, fontSize: 12, color: guide.color, fontWeight: 600 }}>Going deeper \u2014 tier {tier + 1}</div>}
</div>
)}
{!showIntro && !freshStart && convo.map((m, i) => (
<div key={i} style={{ display: "flex", justifyContent: m.from === "user" ? "flex-end" : "flex-start", animation: "fadeIn 0.3s ease" }}>
<div style={{
maxWidth: "85%", padding: "12px 16px", borderRadius: 16, fontSize: 14, lineHeight: 1.7, whiteSpace: "pre-wrap",
background: m.from === "user" ? "linear-gradient(135deg,#2563eb,#7c3aed)" : guide.color + "12",
color: m.from === "user" ? "#fff" : "#e2e8f0",
border: m.from === "bot" ? "1px solid " + guide.color + "20" : "none",
borderBottomRightRadius: m.from === "user" ? 4 : 16,
borderBottomLeftRadius: m.from === "bot" ? 4 : 16,
}}>{m.text}</div>
</div>
))}
{isDone && !showIntro && !freshStart && (
<div style={{ padding: "14px 16px", background: "#16a34a12", border: "1px solid #16a34a25", borderRadius: 16, borderBottomLeftRadius: 4, maxWidth: "85%", fontSize: 14, color: "#16a34a" }}>
{"\u2713"} Session complete! {tier < 2 ? "Come back for tier " + (tier + 2) + " to go even deeper." : "You've completed all tiers with this guide."} Go back to build your profile.
</div>
)}
<div ref={endRef} />
</div>
<div style={{ padding: "12px 0", borderTop: "1px solid #1e293b" }}>
{(showIntro || freshStart) ? (
<button onClick={startChat} style={{ width: "100%", padding: "14px", background: guide.color, color: "#fff", border: "none", borderRadius: 10, fontSize: 15, fontWeight: 600, cursor: "pointer", fontFamily: "inherit" }}>
{freshStart ? "Go deeper \u2014 tier " + (tier + 1) : "I'm ready"}
</button>
) : isDone ? (
<button onClick={onBack} style={{ width: "100%", padding: "14px", background: "#1e293b", color: "#94a3b8", border: "1px solid #334155", borderRadius: 10, fontSize: 14, cursor: "pointer", fontFamily: "inherit" }}>
{"\u2190"} Back to guides
</button>
) : (
<div style={{ display: "flex", gap: 8 }}>
<textarea value={input} onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); } }}
placeholder="Take your time. Be honest... (Shift+Enter for new line)"
rows={2}
style={{ ...inp, flex: 1, borderColor: guide.color + "30", resize: "none" }} />
<button onClick={send} disabled={!input.trim()}
style={{ padding: "10px 16px", background: input.trim() ? guide.color : "#1e293b", color: "#fff", border: "none", borderRadius: 10, cursor: input.trim() ? "pointer" : "default", fontSize: 16, alignSelf: "flex-end" }}>
{"\u27A4"}
</button>
</div>
)}
</div>
</div>
);
}
// ══════════════════════════════════════════════
// SYNTHESIS (client-side, no API)
// ══════════════════════════════════════════════
function synthesizeProfile(profile) {
const a = {};
GUIDES.forEach((g) => {
const s = profile.guideSessions[g.id];
if (s?.answers) Object.entries(s.answers).forEach(([k, v]) => { if (v) a[k] = v; });
});
const blob = Object.values(a).join(" ").toLowerCase();
const has = (words) => words.some((w) => blob.includes(w.toLowerCase()));
const pick = (...keys) => keys.map((k) => a[k]).filter(Boolean)[0] || "";
const pickAll = (...keys) => keys.map((k) => a[k]).filter(Boolean);
const shorten = (s, n) => s && s.length > n ? s.slice(0, n).replace(/[.,!?;:\s]+$/, "") + "..." : s;
// Why Statement
const worldLoses = pick("world_loses", "legacy");
const friendSays = pick("friend_says", "bottled");
const whySentence = pick("why_sentence");
let whyStatement = "";
if (whySentence && whySentence.length > 10) {
whyStatement = whySentence;
} else if (worldLoses && friendSays) {
whyStatement = "I exist to ensure " + shorten(worldLoses.toLowerCase(), 80) + ", and I do it by " + shorten(friendSays.toLowerCase(), 80) + ".";
} else if (worldLoses) {
whyStatement = "My purpose: " + shorten(worldLoses, 150);
}
// Core Truth
const deep = pickAll("wound", "running_from", "not_useful", "afraid_discover", "who_really", "invisible_story", "stop_achieving", "escape_room", "surrender");
let coreTruth = "";
if (deep.length >= 2) {
coreTruth = "Underneath the professional surface: " + shorten(deep[0], 100) + ". And the tension that drives you: " + shorten(deep[1], 100) + ".";
} else if (deep.length === 1) {
coreTruth = deep[0];
} else {
const mid = pickAll("authentic", "no_title", "future_self", "desired_story");
if (mid.length > 0) coreTruth = mid[0];
}
// Golden Circle
const goldenCircle = {
why: worldLoses || pick("work_gives", "desired_story", "wound"),
how: friendSays || pick("team_role", "bottled", "fought_for"),
what: pick("best_moment", "excitement", "childhood_thread", "legacy"),
};
// Ikigai
const ikigai = {
love: pick("excitement", "childhood_thread", "best_moment", "desire"),
good_at: pick("friend_says", "bottled", "fought_for"),
world_needs: pick("world_loses", "fought_for", "legacy"),
paid_for: pick("best_moment", "team_role", "ingredient"),
};
// Big Five
const bigFive = {
O: has(["creative", "curious", "explore", "new", "innovation", "imagine", "question", "wonder", "different"]) ? "high" : has(["practical", "routine", "stable", "traditional", "proven"]) ? "low" : "medium",
C: has(["organized", "plan", "structure", "detail", "discipline", "process", "thorough", "systematic"]) ? "high" : has(["spontaneous", "flexible", "improvise", "chaotic", "adaptive"]) ? "low" : "medium",
E: has(["team", "people", "social", "energy", "collaborate", "connect", "talk", "present"]) ? "high" : has(["alone", "quiet", "introvert", "independent", "solo", "deep work"]) ? "low" : "medium",
A: has(["help", "care", "support", "empathy", "kind", "harmony", "coach", "nurtur"]) ? "high" : has(["challenge", "compete", "debate", "push back", "direct", "confront"]) ? "low" : "medium",
N: has(["anxious", "stress", "worry", "overwhelm", "doubt", "fear", "pressure", "dread"]) ? "high" : has(["calm", "steady", "resilient", "confident", "relaxed", "grounded"]) ? "low" : "medium",
summary: [
has(["creative", "curious", "explore"]) ? "Highly open to experience \u2014 you question conventions." : has(["practical", "routine"]) ? "Pragmatic \u2014 you trust what's proven." : "Balanced openness.",
has(["organized", "plan", "structure"]) ? "Exceptionally organized \u2014 you create structure from chaos." : has(["spontaneous", "flexible"]) ? "Adaptive \u2014 you thrive in ambiguity." : "",
has(["team", "people", "social"]) ? "People energize you." : has(["alone", "quiet", "solo"]) ? "You recharge in solitude." : "",
has(["help", "care", "support"]) ? "You lead through empathy." : has(["challenge", "direct"]) ? "You lead through directness." : "",
has(["calm", "steady", "resilient"]) ? "Emotionally steady under pressure." : has(["anxious", "stress"]) ? "You feel deeply \u2014 that fuels both passion and stress." : "",
].filter(Boolean).join(" "),
};
// DISC
const disc = {
D: has(["lead", "decide", "control", "drive", "results", "charge", "initiative"]) ? "high" : "medium",
I: has(["inspire", "motivate", "persuade", "enthusiasm", "connect", "influence"]) ? "high" : has(["quiet", "reserved", "analytical"]) ? "low" : "medium",
S: has(["steady", "patient", "loyal", "consistent", "reliable", "calm", "support"]) ? "high" : has(["restless", "change", "impatient"]) ? "low" : "medium",
C: has(["quality", "accurate", "detail", "standard", "process", "thorough"]) ? "high" : "medium",
summary: has(["lead", "decide", "charge"]) ? "You take charge and drive outcomes."
: has(["inspire", "connect"]) ? "You inspire and connect people."
: has(["steady", "reliable", "support"]) ? "You build reliable systems and support others."
: has(["quality", "detail", "thorough"]) ? "You ensure quality and precision."
: "Balanced work style.",
};
// CliftonStrengths - 34 themes mapped to keywords from guide answers
const strengthThemes = [
// EXECUTING
{ name: "Achiever", domain: "Executing", kw: ["accomplish", "productive", "busy", "hard work", "stamina", "drive", "output", "deliver", "get things done"], desc: "Driven by constant need to achieve. Every day starts at zero." },
{ name: "Arranger", domain: "Executing", kw: ["organize", "arrange", "coordinate", "juggle", "flexible", "configure", "align", "move pieces"], desc: "Orchestrates complexity. Finds the best configuration." },
{ name: "Belief", domain: "Executing", kw: ["values", "purpose", "meaning", "mission", "principle", "ethical", "core", "stand for", "conviction"], desc: "Guided by enduring core values that provide direction." },
{ name: "Consistency", domain: "Executing", kw: ["fair", "equal", "consistent", "rules", "same", "balance", "predictable", "transparent"], desc: "Keenly aware of treating everyone equally." },
{ name: "Deliberative", domain: "Executing", kw: ["careful", "cautious", "risk", "anticipate", "serious", "private", "thorough", "think before"], desc: "Takes serious care in decisions. Anticipates obstacles." },
{ name: "Discipline", domain: "Executing", kw: ["routine", "structure", "order", "plan", "timeline", "organize", "systematic", "predictable"], desc: "Creates order and structure in the world." },
{ name: "Focus", domain: "Executing", kw: ["goal", "prioritize", "efficient", "direction", "filter", "single", "target", "concentrate"], desc: "Takes direction, follows through, makes corrections." },
{ name: "Responsibility", domain: "Executing", kw: ["commit", "reliable", "depend", "own it", "accountable", "promise", "follow through", "trust"], desc: "Takes psychological ownership of commitments." },
{ name: "Restorative", domain: "Executing", kw: ["fix", "solve", "problem", "repair", "restore", "diagnose", "improve", "broken", "what's wrong"], desc: "Loves solving problems. Finds what's broken and fixes it." },
// INFLUENCING
{ name: "Activator", domain: "Influencing", kw: ["start", "action", "impatient", "let's go", "make it happen", "initiate", "decide now", "momentum"], desc: "Makes things happen by turning thoughts into action." },
{ name: "Command", domain: "Influencing", kw: ["take charge", "lead", "confront", "direct", "decisive", "control", "clear", "no nonsense"], desc: "Has presence. Takes charge and is not intimidated." },
{ name: "Communication", domain: "Influencing", kw: ["story", "explain", "present", "articulate", "words", "narrative", "talk", "describe", "conversation"], desc: "Brings ideas to life through vivid storytelling." },
{ name: "Competition", domain: "Influencing", kw: ["win", "compete", "first", "best", "measure", "outperform", "benchmark", "race"], desc: "Measures progress against others' performance." },
{ name: "Maximizer", domain: "Influencing", kw: ["excellent", "best", "potential", "strength", "polish", "refine", "transform good to great"], desc: "Focuses on strengths to stimulate excellence." },
{ name: "Self-Assurance", domain: "Influencing", kw: ["confident", "trust myself", "certain", "inner compass", "faith in myself", "sure", "instinct"], desc: "Has an inner compass of confidence in own ability." },
{ name: "Significance", domain: "Influencing", kw: ["impact", "important", "recognized", "matter", "legacy", "remembered", "meaningful", "significance"], desc: "Wants to make a big impact and be recognized." },
// RELATIONSHIP BUILDING
{ name: "Adaptability", domain: "Relationship", kw: ["flexible", "go with flow", "adapt", "present moment", "respond", "adjust", "fluid"], desc: "Prefers to go with the flow and take things as they come." },
{ name: "Connectedness", domain: "Relationship", kw: ["connected", "everything linked", "bigger picture", "faith", "bridge", "universal", "we're all"], desc: "Believes everything happens for a reason." },
{ name: "Developer", domain: "Relationship", kw: ["grow", "develop", "potential", "coach", "mentor", "teach", "nurture", "help them become"], desc: "Recognizes and cultivates potential in others." },
{ name: "Empathy", domain: "Relationship", kw: ["feel", "sense", "understand emotion", "their shoes", "perspective", "compassion", "tune in"], desc: "Senses others' feelings by imagining their perspective." },
{ name: "Harmony", domain: "Relationship", kw: ["consensus", "agree", "common ground", "peace", "avoid conflict", "practical", "together"], desc: "Looks for areas of agreement and common ground." },
{ name: "Includer", domain: "Relationship", kw: ["include", "belong", "accept", "everyone", "outsider", "welcome", "no one left out"], desc: "Instinctively accepts others and makes them feel welcome." },
{ name: "Individualization", domain: "Relationship", kw: ["unique", "individual", "different", "each person", "tailor", "specific", "one size doesn't"], desc: "Intrigued by unique qualities of each person." },
{ name: "Relator", domain: "Relationship", kw: ["close", "deep relationship", "trust", "genuine", "authentic", "real", "inner circle", "intimate"], desc: "Derives meaning from close, deep relationships." },
// STRATEGIC THINKING
{ name: "Analytical", domain: "Strategic", kw: ["data", "evidence", "prove", "analyze", "logical", "objective", "fact", "pattern", "root cause"], desc: "Searches for reasons and causes. Thinks in data." },
{ name: "Context", domain: "Strategic", kw: ["history", "past", "background", "origin", "where it came from", "how we got here", "blueprint"], desc: "Looks back to understand the present." },
{ name: "Futuristic", domain: "Strategic", kw: ["future", "vision", "imagine", "possibility", "what could be", "dream", "tomorrow", "aspire"], desc: "Inspired by the future and what it could be." },
{ name: "Ideation", domain: "Strategic", kw: ["idea", "concept", "creative", "connection", "new angle", "innovation", "fascinated", "what if"], desc: "Fascinated by ideas and finds connections between phenomena." },
{ name: "Input", domain: "Strategic", kw: ["collect", "curious", "gather", "information", "archive", "read", "know", "accumulate"], desc: "Craves knowledge and collects information." },
{ name: "Intellection", domain: "Strategic", kw: ["think", "reflect", "introspect", "intellectual", "deep thought", "ponder", "philosophical", "mull"], desc: "Characterized by intellectual activity and introspection." },
{ name: "Learner", domain: "Strategic", kw: ["learn", "study", "grow", "course", "improve", "process of learning", "journey", "mastery"], desc: "Has a great desire to learn and continuously improve." },
{ name: "Strategic", domain: "Strategic", kw: ["pattern", "alternative", "option", "path", "scenario", "sort through", "find the way", "shortcut"], desc: "Creates alternative ways to proceed. Spots patterns." },
];
// Score each theme
const scored = strengthThemes.map((t) => {
let score = 0;
t.kw.forEach((kw) => {
const regex = new RegExp(kw.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), "gi");
const matches = blob.match(regex);
if (matches) score += matches.length;
});
return { ...t, score };
}).filter((t) => t.score > 0).sort((a, b) => b.score - a.score);
const topStrengths = scored.slice(0, 5);
const domainCounts = {};
topStrengths.forEach((s) => { domainCounts[s.domain] = (domainCounts[s.domain] || 0) + 1; });
const dominantDomain = Object.entries(domainCounts).sort((a, b) => b[1] - a[1])[0];
const clifton = {
top5: topStrengths.map((s) => ({ name: s.name, domain: s.domain, desc: s.desc })),
dominantDomain: dominantDomain ? dominantDomain[0] : "Balanced",
summary: topStrengths.length >= 3
? "Your top strengths cluster around " + (dominantDomain ? dominantDomain[0] : "multiple domains") + ". You lead with " + topStrengths[0].name + " and " + topStrengths[1].name + " \u2014 " + topStrengths[0].desc.toLowerCase().slice(0, -1) + ", and " + topStrengths[1].desc.toLowerCase()
: "Complete more sessions to reveal your strength pattern.",
};
// Key quotes
const quoteKeys = ["who_really", "authentic", "secret_ambition", "future_self", "world_loses", "desire", "wound", "one_message", "legacy", "why_sentence"];
const keyQuotes = quoteKeys.map((k) => a[k]).filter((v) => v && v.length > 10 && v.length < 300).slice(0, 5);
return {
complete: true,
whyStatement, coreTruth, keyQuotes,
goldenCircle: goldenCircle.why ? goldenCircle : null,
ikigai: ikigai.love ? ikigai : null,
bigFive, disc, clifton,
motivation: pick("work_gives", "desired_story", "authentic", "excitement"),
idealRole: pick("secret_ambition", "future_self", "desired_story", "legacy"),
dealbreakers: pick("discomfort", "stay_or_fear", "stop_liked"),
};
}
// ══════════════════════════════════════════════
// GUIDE PICKER
// ══════════════════════════════════════════════
function GuidePicker({ profile, up, onComplete }) {
const [active, setActive] = useState(null);
const done = GUIDES.filter((g) => profile.guideSessions[g.id]?.complete).length;
const totalSessions = GUIDES.reduce((sum, g) => sum + (profile.guideSessions[g.id]?.timesCompleted || 0), 0);
const build = () => {
const result = synthesizeProfile(profile);
up(result);
onComplete();
};
if (active) {
return <GuideChat guide={GUIDES.find((g) => g.id === active)} profile={profile} up={up} onBack={() => setActive(null)} />;
}
return (
<div style={{ padding: "16px 0" }}>
<div style={{ textAlign: "center", marginBottom: 24 }}>
<h2 style={{ fontSize: 22, fontWeight: 700, color: "#f1f5f9", margin: "0 0 8px" }}>Choose Your Guide</h2>
<p style={{ fontSize: 14, color: "#64748b", margin: 0, lineHeight: 1.6 }}>
Each guide sees you through a different lens.
{profile.name ? " Welcome, " + profile.name + "." : " Complete at least 2 to build your profile."}
</p>
</div>
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginBottom: 24 }}>
{GUIDES.map((g) => {
const s = profile.guideSessions[g.id];
const isDone = s?.complete;
const started = s?.qIndex > 0 && !isDone;
const tc = s?.timesCompleted || 0;
return (
<button key={g.id} onClick={() => setActive(g.id)} style={{
padding: 16, background: isDone ? g.color + "08" : "#111827",
border: "1.5px solid " + (isDone ? g.color + "35" : "#1e293b"),
borderRadius: 12, cursor: "pointer", textAlign: "left", fontFamily: "inherit",
}}>
<div style={{ display: "flex", justifyContent: "space-between", marginBottom: 8 }}>
<span style={{ fontSize: 26 }}>{g.emoji}</span>
{isDone && tc < 3 && <span style={{ fontSize: 10, fontWeight: 600, color: g.color, background: g.color + "18", padding: "2px 8px", borderRadius: 10 }}>Tier {tc + 1} ready</span>}
{tc >= 3 && <span style={{ fontSize: 10, fontWeight: 600, color: "#16a34a", background: "#16a34a18", padding: "2px 8px", borderRadius: 10 }}>Complete</span>}
{started && <span style={{ fontSize: 10, fontWeight: 600, color: g.color, background: g.color + "18", padding: "2px 8px", borderRadius: 10 }}>In progress</span>}
</div>
<div style={{ fontSize: 16, fontWeight: 700, color: g.color, marginBottom: 2 }}>{g.name}</div>
<div style={{ fontSize: 11, color: "#64748b", marginBottom: 6 }}>{g.thinker}</div>
<div style={{ fontSize: 13, color: "#94a3b8", fontStyle: "italic", lineHeight: 1.5 }}>"{g.tagline}"</div>
{tc > 0 && <div style={{ marginTop: 6 }}>
{[0, 1, 2].map((t) => <span key={t} style={{ display: "inline-block", width: 8, height: 8, borderRadius: 4, marginRight: 4, background: t < tc ? g.color : "#1e293b" }} />)}
</div>}
</button>
);
})}
</div>
<div style={{ textAlign: "center" }}>
{done >= 2 ? (
<button onClick={build} style={{ padding: "14px 32px", background: "linear-gradient(135deg,#f43f5e,#8b5cf6)", color: "#fff", border: "none", borderRadius: 12, fontSize: 16, fontWeight: 600, cursor: "pointer", fontFamily: "inherit" }}>
{"\u26A1"} Build My Profile {totalSessions > 2 ? "(from " + totalSessions + " sessions)" : ""}
</button>
) : (
<p style={{ fontSize: 13, color: "#475569" }}>Complete at least 2 guide sessions ({done}/2)</p>
)}
{!profile.complete && (
<button onClick={() => up({ ...DEMO })} style={{ ...btnS, marginTop: 16, color: "#475569", fontSize: 11 }}>
Load demo profile to preview
</button>
)}
</div>
</div>
);
}
// ══════════════════════════════════════════════
// PROFILE VIEW
// ══════════════════════════════════════════════
function ProfileView({ profile: p, up }) {
const Box = ({ title, accent, children }) => (
<div style={{ background: "#111827", border: "1.5px solid " + (accent || "#1e293b") + "25", borderRadius: 12, padding: 16, marginBottom: 10, borderLeft: accent ? "3px solid " + accent + "40" : "none" }}>
{title && <h3 style={{ fontSize: 14, fontWeight: 700, color: accent || "#f1f5f9", margin: "0 0 10px" }}>{title}</h3>}
{children}
</div>
);
const Bar = ({ label, level, color }) => (
<div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }}>
<span style={{ fontSize: 12, color: "#94a3b8", width: 120 }}>{label}</span>
<div style={{ flex: 1, height: 8, background: "#1e293b", borderRadius: 4 }}>
<div style={{ height: "100%", borderRadius: 4, background: color, width: level === "high" ? "90%" : level === "medium" ? "55%" : "25%", transition: "width 0.5s" }} />
</div>
<span style={{ fontSize: 11, color: "#64748b", width: 50, textAlign: "right" }}>{level}</span>
</div>
);
const totalSessions = GUIDES.reduce((sum, g) => sum + (p.guideSessions[g.id]?.timesCompleted || 0), 0);
return (
<div style={{ padding: "16px 0" }}>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 20 }}>
<div>
<h2 style={{ fontSize: 22, fontWeight: 700, color: "#f1f5f9", margin: 0 }}>{p.name || "Your"} Profile</h2>
<p style={{ fontSize: 12, color: "#64748b", margin: "4px 0 0" }}>Built from {totalSessions} coaching session{totalSessions !== 1 ? "s" : ""}</p>
</div>
<div style={{ display: "flex", gap: 6 }}>
<button onClick={() => up({ complete: false })} style={btnS}>More sessions</button>
<button onClick={() => up({ ...EP })} style={{ ...btnS, color: "#64748b" }}>Reset</button>
</div>
</div>
{p.whyStatement && (
<Box accent="#f43f5e" title={"\u26A1 Your Why"}>
<div style={{ fontSize: 16, color: "#f1f5f9", lineHeight: 1.7, fontWeight: 500 }}>"{p.whyStatement}"</div>
</Box>
)}
{p.coreTruth && (
<Box accent="#8b5cf6" title={"\u{1F52E} Core Truth"}>
<div style={{ fontSize: 14, color: "#cbd5e1", lineHeight: 1.7 }}>{p.coreTruth}</div>
</Box>
)}
{p.goldenCircle?.why && (
<Box title="Golden Circle">
<div style={{ display: "flex", gap: 8 }}>
{[["WHY", p.goldenCircle.why, "#f59e0b"], ["HOW", p.goldenCircle.how, "#3b82f6"], ["WHAT", p.goldenCircle.what, "#64748b"]].map(([l, v, c]) => (
<div key={l} style={{ flex: 1, padding: 12, background: "#0a0f1a", borderRadius: 10, borderTop: "3px solid " + c }}>
<div style={{ fontSize: 11, fontWeight: 700, color: c, marginBottom: 6 }}>{l}</div>
<div style={{ fontSize: 13, color: "#cbd5e1", lineHeight: 1.6 }}>{v}</div>
</div>
))}
</div>
</Box>
)}
{p.bigFive && (
<Box title="Big Five Personality (OCEAN)">
<Bar label="Openness" level={p.bigFive.O} color="#8b5cf6" />
<Bar label="Conscientiousness" level={p.bigFive.C} color="#3b82f6" />
<Bar label="Extraversion" level={p.bigFive.E} color="#f59e0b" />
<Bar label="Agreeableness" level={p.bigFive.A} color="#10b981" />
<Bar label="Neuroticism" level={p.bigFive.N} color="#f43f5e" />
{p.bigFive.summary && <div style={{ fontSize: 13, color: "#94a3b8", marginTop: 10, lineHeight: 1.6 }}>{p.bigFive.summary}</div>}
</Box>
)}
{p.disc && (
<Box title="DISC Work Style">
<Bar label="Dominance" level={p.disc.D} color="#dc2626" />
<Bar label="Influence" level={p.disc.I} color="#f59e0b" />
<Bar label="Steadiness" level={p.disc.S} color="#10b981" />
<Bar label="Conscientiousness" level={p.disc.C} color="#3b82f6" />
{p.disc.summary && <div style={{ fontSize: 13, color: "#94a3b8", marginTop: 10, lineHeight: 1.6 }}>{p.disc.summary}</div>}
</Box>
)}
{p.clifton?.top5?.length > 0 && (
<Box accent="#f59e0b" title={"\u{1F3AF} Top Strengths (CliftonStrengths-inspired)"}>
<div style={{ fontSize: 12, color: "#64748b", marginBottom: 10 }}>Dominant domain: <span style={{ color: "#f59e0b", fontWeight: 600 }}>{p.clifton.dominantDomain}</span></div>
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
{p.clifton.top5.map((s, i) => {
const dc = { Executing: "#3b82f6", Influencing: "#f43f5e", Relationship: "#10b981", Strategic: "#8b5cf6" };
const color = dc[s.domain] || "#64748b";
return (
<div key={i} style={{ display: "flex", gap: 10, alignItems: "flex-start", padding: "10px 12px", background: "#0a0f1a", borderRadius: 10, borderLeft: "3px solid " + color }}>
<span style={{ fontSize: 18, fontWeight: 700, color: color, fontFamily: "monospace", minWidth: 24 }}>{i + 1}</span>
<div style={{ flex: 1 }}>
<div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 2 }}>
<span style={{ fontSize: 14, fontWeight: 700, color: "#f1f5f9" }}>{s.name}</span>
<span style={{ fontSize: 10, fontWeight: 600, color: color, background: color + "18", padding: "1px 8px", borderRadius: 10 }}>{s.domain}</span>
</div>
<div style={{ fontSize: 12, color: "#94a3b8", lineHeight: 1.5 }}>{s.desc}</div>
</div>
</div>
);
})}
</div>
{p.clifton.summary && <div style={{ fontSize: 13, color: "#94a3b8", marginTop: 12, lineHeight: 1.6, paddingTop: 10, borderTop: "1px solid #1e293b" }}>{p.clifton.summary}</div>}
<div style={{ fontSize: 11, color: "#475569", marginTop: 8, fontStyle: "italic" }}>Based on patterns in your guide conversations. For the official assessment, visit gallup.com/cliftonstrengths.</div>
</Box>
)}
{p.ikigai?.love && (
<Box title="Ikigai">
{[
["\u2764\uFE0F", "What you love", p.ikigai.love],
["\u{1F4AA}", "What you're good at", p.ikigai.good_at],
["\u{1F30D}", "What the world needs", p.ikigai.world_needs],
["\u{1F4B0}", "What you can be paid for", p.ikigai.paid_for],
].map(([e, l, v]) => v ? (
<div key={l} style={{ display: "flex", gap: 10, marginBottom: 10, alignItems: "flex-start" }}>
<span style={{ fontSize: 20 }}>{e}</span>
<div>
<div style={{ fontSize: 11, fontWeight: 600, color: "#64748b", marginBottom: 2 }}>{l}</div>
<div style={{ fontSize: 13, color: "#cbd5e1", lineHeight: 1.5 }}>{v}</div>
</div>
</div>
) : null)}
</Box>
)}
{p.motivation && (
<Box title="What Drives You">
<div style={{ fontSize: 13, color: "#cbd5e1", lineHeight: 1.6 }}>{p.motivation}</div>
</Box>
)}
{p.idealRole && (
<Box title="What You're Looking For">
<div style={{ fontSize: 13, color: "#cbd5e1", lineHeight: 1.6 }}>{p.idealRole}</div>
</Box>
)}
{p.keyQuotes?.length > 0 && (
<Box accent="#f59e0b" title="In Your Own Words">
{p.keyQuotes.map((q, i) => (
<div key={i} style={{ fontSize: 14, color: "#e2e8f0", fontStyle: "italic", marginBottom: 10, paddingLeft: 14, borderLeft: "2px solid #f59e0b30", lineHeight: 1.6 }}>
"{q}"
</div>
))}
</Box>
)}
</div>
);
}
// ══════════════════════════════════════════════
// MAIN APP
// ══════════════════════════════════════════════
export default function App() {
const [p, setP, pOk] = usePS(PK, { ...EP });
const up = (f) => setP((prev) => ({ ...prev, ...f }));
if (!pOk) {
return <div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100vh", fontFamily: "'IBM Plex Sans',sans-serif", background: "#0a0f1a", color: "#64748b" }}>Loading...</div>;
}
return (
<div style={{ fontFamily: "'IBM Plex Sans',sans-serif", background: "#0a0f1a", color: "#e2e8f0", minHeight: "100vh", maxWidth: 860, margin: "0 auto", padding: "0 16px 40px" }}>
<style>{`
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&display=swap');
@keyframes fadeIn { from { opacity: 0; transform: translateY(6px) } to { opacity: 1; transform: translateY(0) } }
input, textarea, select { font-family: 'IBM Plex Sans', sans-serif; }
::selection { background: #3b82f650; }
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-thumb { background: #334155; border-radius: 3px; }
`}</style>
<header style={{ padding: "28px 0 20px", textAlign: "center" }}>
<div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 10, marginBottom: 4 }}>
<span style={{ fontSize: 26, fontWeight: 700, color: "#f8fafc", letterSpacing: "-0.5px" }}>KnowYourself</span>
<span style={{ fontSize: 10, fontWeight: 700, background: "linear-gradient(135deg,#f43f5e,#8b5cf6)", color: "#fff", padding: "3px 10px", borderRadius: 6, textTransform: "uppercase", letterSpacing: "1px" }}>AI</span>
</div>
<p style={{ fontSize: 14, color: "#64748b", margin: 0 }}>Discover who you really are. Then find work that fits.</p>
</header>
{!p.complete
? <GuidePicker profile={p} up={up} onComplete={() => {}} />
: <ProfileView profile={p} up={up} />
}
</div>
);
}
const inp = { padding: "10px 14px", background: "#111827", border: "1.5px solid #1e293b", borderRadius: 10, color: "#e2e8f0", fontSize: 14, outline: "none", boxSizing: "border-box", fontFamily: "'IBM Plex Sans',sans-serif", lineHeight: 1.6 };
const btnS = { padding: "6px 12px", background: "#1e293b", color: "#94a3b8", border: "1px solid #334155", borderRadius: 6, fontSize: 12, fontWeight: 600, cursor: "pointer", fontFamily: "'IBM Plex Sans',sans-serif" };