The Dutch Mentor
Listening…
Enter to send · Shift+Enter for newline · Free · 5 questions/day

CV Review with The Dutch Mentor

Upload your résumé, paste the job description. Get a comprehensive 7-section report — alignment audit, specific rewrites, interview prep, STAR stories, questions to ask. Free: 1 report ever.

PDF/DOCX is parsed locally in your browser — only the extracted text is sent.

🌟 Your WHY

Start here. The clearer the mentor knows you, the more precise the coaching gets. Your WHY, your brand, and your CV become context the mentor uses on every reply — across Chat, CV Review, and Email Helper.

A great WHY is concrete and personal — not "to help leaders succeed" but "because I watched my mom drown in middle management when nobody helped her, and I'm not letting that happen to anyone else on my watch."
Brand = Natural Strengths + Leadership Attributes + Expertise. The mentor will help you triangulate.
Parsed in your browser. Only the extracted text is stored locally and sent as context with each request — never to anyone but the AI mentor.
📊 Profile completeness: No data yet — start with your WHY above.

Accountability Tracker

Track the commitments that actually matter. Tap each cell — green (on track), yellow (at risk), red (off track). Two yellows or reds in the current period triggers a countermeasure. Pulled straight from your Rhythmic Operations + Performance pillars.

Email Helper — drafts in your voice

Pro feature. Paste an incoming email + a few notes about how you want to respond. The mentor drafts a reply in your voice, learning from samples you share over time.

* * It adds: * - A floating cobalt circle button in the bottom-right corner * - Click → slides up a chat panel with the Dutch Mentor avatar * - Wires the avatar to the shared dutch-mentor-ai worker * - Runs client-side crisis detection BEFORE any API call * - Persists conversation per-app in localStorage (key per host) * * Customize via window.DM_MENTOR_CONFIG before this script loads: * * * * Safe to load on any page. Will not collide with existing UI * because all CSS is scoped to .dm-mentor-* classes. * * VERSION: 0.1 * ============================================================= */ (function () { "use strict"; // ----------------------------------------------------------- // Configuration (with sensible defaults) // ----------------------------------------------------------- const cfg = Object.assign({ appName: detectAppName(), endpoint: "https://dutch-mentor-ai.pages.dev/chat", greeting: "Hey — I'm Walter's AI mentor. Tell me what's on your mind. I'm here to think with you, not at you.", buttonLabel: "Talk to Walter", autoOpen: false, timeoutMs: 30000, storageKey: "dm-mentor-chat-" + (location.hostname || "default"), }, window.DM_MENTOR_CONFIG || {}); function detectAppName() { const h = (location.hostname || "").toLowerCase(); if (h.includes("dutchmentor.pages.dev") || h.includes("smart")) return "smart"; if (h.includes("aimscholar")) return "aimscholar"; if (h.includes("companion")) return "companion"; return "unknown"; } // ----------------------------------------------------------- // Crisis detection — runs BEFORE any API call. // Same pattern as the Companion's spine. NEVER edit without // a clinical advisor's read. // ----------------------------------------------------------- const CRISIS_PATTERNS = [ /\b(?:kill|hurt|harm)\s+(?:my)?self\b/i, /\bsuicide|suicidal\b/i, /\bend\s+(?:it|my\s+life|things)\b/i, /\bdon'?t\s+want\s+to\s+(?:live|be\s+here|exist)\b/i, /\bwant\s+to\s+die\b/i, /\bcan'?t\s+go\s+on\b/i, /\bgoing\s+to\s+(?:kill|hurt)\b/i, /\bself[\s-]?harm\b/i, /\bcutting\s+myself\b/i, /\boverdose\b/i, /\bjump\s+off\b/i, /\b(?:has|have)\s+a\s+gun\b/i, /\babuse[ds]?\s+me\b/i, /\b(?:rape|raped|assault(?:ed)?|attacked)\s+me\b/i, ]; function detectCrisis(text) { return CRISIS_PATTERNS.some(rx => rx.test(text)); } // ----------------------------------------------------------- // Crisis modal — same content as Companion. Opens directly, // no API call. // ----------------------------------------------------------- function showCrisisModal() { const modal = document.createElement("div"); modal.className = "dm-mentor-crisis-modal"; modal.innerHTML = ` `; document.body.appendChild(modal); modal.querySelector(".dm-mentor-crisis-close").addEventListener("click", () => modal.remove()); } // ----------------------------------------------------------- // Chat state (persists in localStorage, scoped per host) // ----------------------------------------------------------- let chat = loadChat(); function loadChat() { try { const raw = localStorage.getItem(cfg.storageKey); if (raw) return JSON.parse(raw); } catch (e) {} return [{ role: "me", text: cfg.greeting, t: Date.now() }]; } function saveChat() { try { localStorage.setItem(cfg.storageKey, JSON.stringify(chat.slice(-30))); } catch (e) {} } function pushMsg(role, text) { chat.push({ role, text, t: Date.now() }); saveChat(); renderChat(); } // ----------------------------------------------------------- // AI call — sends conversation history to the worker. // Trusts that the user message is already in `chat` (caller pushes it first). // ----------------------------------------------------------- async function mentorReplyAI(userText) { if (detectCrisis(userText)) { showCrisisModal(); return ""; } const history = chat .filter(m => m.role === "you" || m.role === "me") .map(m => ({ role: m.role === "you" ? "user" : "assistant", content: m.text })); if (history.length === 0 || history[history.length - 1].role !== "user") { history.push({ role: "user", content: userText }); } setAvatarState("thinking"); const ctrl = new AbortController(); const timeout = setTimeout(() => ctrl.abort(), cfg.timeoutMs); try { const resp = await fetch(cfg.endpoint, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ messages: history, memoryEnabled: true, appName: cfg.appName }), signal: ctrl.signal, }); clearTimeout(timeout); if (!resp.ok) throw new Error("upstream_" + resp.status); const data = await resp.json(); if (!data.reply) throw new Error("empty_reply"); return data.reply; } catch (e) { clearTimeout(timeout); console.warn("Mentor AI unreachable:", e.message); return "I had trouble reaching the mentor service. Try again in a moment, or check your connection."; } } // ----------------------------------------------------------- // Render the floating button + chat panel // ----------------------------------------------------------- function injectStyles() { if (document.getElementById("dm-mentor-styles")) return; const css = document.createElement("style"); css.id = "dm-mentor-styles"; css.textContent = ` .dm-mentor-fab { position: fixed; right: 22px; bottom: 22px; z-index: 99998; width: 60px; height: 60px; border-radius: 50%; background: #0836DB; color: #fff; border: 3px solid #FFB536; cursor: pointer; box-shadow: 0 8px 24px rgba(8,54,219,0.35); display: grid; place-items: center; font-size: 28px; transition: transform 0.15s ease, box-shadow 0.15s ease; } .dm-mentor-fab:hover { transform: scale(1.06); box-shadow: 0 12px 32px rgba(8,54,219,0.45); } .dm-mentor-fab svg { width: 36px; height: 36px; } .dm-mentor-fab.is-open { display: none; } .dm-mentor-panel { position: fixed; right: 22px; bottom: 22px; z-index: 99999; width: 380px; max-width: calc(100vw - 24px); height: 560px; max-height: calc(100vh - 80px); background: #fff; border-radius: 16px; overflow: hidden; box-shadow: 0 24px 60px rgba(0,0,0,0.28); display: none; flex-direction: column; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Ubuntu, sans-serif; color: #0A1428; } .dm-mentor-panel.is-open { display: flex; } .dm-mentor-panel.is-mobile { right: 12px; left: 12px; bottom: 12px; width: auto; } .dm-mentor-header { background: linear-gradient(135deg, #0836DB 0%, #062AA8 100%); color: #fff; padding: 14px 16px; display: flex; align-items: center; gap: 12px; } .dm-mentor-header-title { flex: 1; font-weight: 600; font-size: 15px; letter-spacing: 0.01em; } .dm-mentor-header-title small { display: block; font-weight: 400; font-size: 11px; opacity: 0.8; letter-spacing: 0.04em; } .dm-mentor-close { background: rgba(255,255,255,0.15); border: 0; color: #fff; width: 32px; height: 32px; border-radius: 50%; cursor: pointer; font-size: 20px; line-height: 1; } .dm-mentor-close:hover { background: rgba(255,255,255,0.25); } .dm-mentor-avatar { width: 36px; height: 36px; border-radius: 50%; flex-shrink: 0; background: #062AA8; border: 2px solid #FFB536; display: grid; place-items: center; overflow: hidden; } .dm-mentor-avatar svg { width: 100%; height: 100%; } .dm-mentor-avatar.is-thinking { animation: dm-pulse 1.2s ease-in-out infinite; } @keyframes dm-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.08); } } .dm-mentor-body { flex: 1; overflow-y: auto; padding: 16px; background: #F4F6FB; display: flex; flex-direction: column; gap: 10px; } .dm-mentor-bubble { max-width: 85%; padding: 10px 14px; border-radius: 16px; font-size: 14px; line-height: 1.5; white-space: pre-wrap; } .dm-mentor-bubble.you { align-self: flex-end; background: #0836DB; color: #fff; border-bottom-right-radius: 4px; } .dm-mentor-bubble.me { align-self: flex-start; background: #fff; color: #0A1428; border-bottom-left-radius: 4px; border: 1px solid #E2E1DC; } .dm-mentor-bubble.thinking { align-self: flex-start; background: #fff; border: 1px solid #E2E1DC; border-bottom-left-radius: 4px; color: #6b6b6b; font-style: italic; } .dm-mentor-thinking-dots::after { content: ''; animation: dm-dots 1.4s steps(4,end) infinite; } @keyframes dm-dots { 0%,20% { content: ''; } 40% { content: '.'; } 60% { content: '..'; } 80%,100% { content: '...'; } } .dm-mentor-footer { border-top: 1px solid #E2E1DC; padding: 10px 12px; background: #fff; display: flex; gap: 8px; align-items: flex-end; } .dm-mentor-input { flex: 1; border: 1px solid #E2E1DC; border-radius: 12px; padding: 10px 12px; font-size: 14px; font-family: inherit; resize: none; outline: none; min-height: 40px; max-height: 120px; line-height: 1.4; } .dm-mentor-input:focus { border-color: #0836DB; } .dm-mentor-send { background: #0836DB; color: #fff; border: 0; border-radius: 12px; padding: 10px 16px; font-weight: 600; font-size: 14px; cursor: pointer; white-space: nowrap; } .dm-mentor-send:hover { background: #062AA8; } .dm-mentor-send:disabled { opacity: 0.5; cursor: not-allowed; } .dm-mentor-meta { font-size: 10px; color: #6b6b6b; text-align: center; padding: 6px 12px 8px; background: #fff; border-top: 1px solid #F0EFEA; } .dm-mentor-meta a { color: #0836DB; text-decoration: none; } .dm-mentor-meta a:hover { text-decoration: underline; } /* Crisis modal */ .dm-mentor-crisis-modal { position: fixed; inset: 0; z-index: 100000; background: rgba(10,20,40,0.7); backdrop-filter: blur(4px); display: grid; place-items: center; padding: 20px; } .dm-mentor-crisis-card { background: #fff; border-radius: 16px; padding: 28px 24px; max-width: 420px; width: 100%; box-shadow: 0 24px 60px rgba(0,0,0,0.4); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; color: #0A1428; } .dm-mentor-crisis-card h2 { font-size: 22px; margin-bottom: 12px; color: #0A1428; } .dm-mentor-crisis-card p { font-size: 15px; line-height: 1.5; margin-bottom: 16px; color: #4a4a4a; } .dm-mentor-crisis-actions { display: flex; flex-direction: column; gap: 10px; margin: 16px 0; } .dm-mentor-crisis-btn { display: block; padding: 14px 18px; border-radius: 12px; font-weight: 600; text-decoration: none; text-align: center; font-size: 16px; } .dm-mentor-crisis-call { background: #0836DB; color: #fff; } .dm-mentor-crisis-text { background: #FFB536; color: #0A1428; } .dm-mentor-crisis-aux { font-size: 13px; color: #6b6b6b; margin: 12px 0 16px; } .dm-mentor-crisis-close { background: transparent; border: 1px solid #E2E1DC; color: #4a4a4a; padding: 10px 14px; border-radius: 10px; font-size: 14px; cursor: pointer; width: 100%; } @media (max-width: 480px) { .dm-mentor-fab { right: 14px; bottom: 14px; width: 54px; height: 54px; } .dm-mentor-panel.is-open { right: 12px; left: 12px; bottom: 12px; width: auto; height: 70vh; } } `; document.head.appendChild(css); } // Cobalt avatar SVG — simplified version of the Companion's avatar const AVATAR_SVG = ` `; let panelEl, fabEl, bodyEl, inputEl, sendEl, avatarEl; function buildUI() { injectStyles(); // Floating button fabEl = document.createElement("button"); fabEl.className = "dm-mentor-fab"; fabEl.title = cfg.buttonLabel; fabEl.setAttribute("aria-label", cfg.buttonLabel); fabEl.innerHTML = AVATAR_SVG; fabEl.addEventListener("click", openPanel); document.body.appendChild(fabEl); // Slide-up panel panelEl = document.createElement("div"); panelEl.className = "dm-mentor-panel"; panelEl.innerHTML = `
${AVATAR_SVG}
The Dutch Mentor AI Walter's voice — not a real person
AI mentor · Crisis? 988 · Text HOME to 741741
`; document.body.appendChild(panelEl); bodyEl = panelEl.querySelector(".dm-mentor-body"); inputEl = panelEl.querySelector(".dm-mentor-input"); sendEl = panelEl.querySelector(".dm-mentor-send"); avatarEl = panelEl.querySelector(".dm-mentor-avatar"); panelEl.querySelector(".dm-mentor-close").addEventListener("click", closePanel); sendEl.addEventListener("click", handleSend); inputEl.addEventListener("keydown", (e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); handleSend(); } }); inputEl.addEventListener("input", () => { inputEl.style.height = "auto"; inputEl.style.height = Math.min(120, inputEl.scrollHeight) + "px"; }); renderChat(); if (cfg.autoOpen) openPanel(); } function renderChat() { if (!bodyEl) return; bodyEl.innerHTML = ""; chat.forEach(m => { const b = document.createElement("div"); b.className = "dm-mentor-bubble " + (m.role === "you" ? "you" : "me"); b.textContent = m.text; bodyEl.appendChild(b); }); bodyEl.scrollTop = bodyEl.scrollHeight; } function setAvatarState(state) { if (!avatarEl) return; avatarEl.classList.remove("is-thinking", "is-speaking"); if (state && state !== "idle") avatarEl.classList.add("is-" + state); } function openPanel() { panelEl.classList.add("is-open"); fabEl.classList.add("is-open"); setTimeout(() => inputEl && inputEl.focus(), 100); } function closePanel() { panelEl.classList.remove("is-open"); fabEl.classList.remove("is-open"); } async function handleSend() { const text = inputEl.value.trim(); if (!text) return; inputEl.value = ""; inputEl.style.height = "auto"; if (detectCrisis(text)) { showCrisisModal(); return; } pushMsg("you", text); sendEl.disabled = true; // Show a thinking placeholder bubble const thinking = document.createElement("div"); thinking.className = "dm-mentor-bubble thinking"; thinking.innerHTML = 'thinking'; bodyEl.appendChild(thinking); bodyEl.scrollTop = bodyEl.scrollHeight; try { const reply = await mentorReplyAI(text); thinking.remove(); if (reply) { setAvatarState("speaking"); pushMsg("me", reply); setTimeout(() => setAvatarState("idle"), Math.min(5000, 1000 + reply.length * 12)); } } catch (e) { thinking.remove(); pushMsg("me", "I had trouble reaching the mentor service. Try again in a moment."); } finally { sendEl.disabled = false; setAvatarState("idle"); } } // ----------------------------------------------------------- // Boot // ----------------------------------------------------------- if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", buildUI); } else { buildUI(); } // Expose minimal API on window window.DutchMentorModule = { open: openPanel, close: closePanel, clear: () => { chat = [{ role: "me", text: cfg.greeting, t: Date.now() }]; saveChat(); renderChat(); }, version: "0.1", }; })();