<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>All About My Child</title>
<!-- Meta Pixel Code -->
<script>
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', '1630656715380377');
fbq('track', 'PageView');
</script>
<noscript><img height="1" width="1" style="display:none"
src="https://www.facebook.com/tr?id=1630656715380377&ev=PageView&noscript=1"
/></noscript>
<!-- End Meta Pixel Code -->
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@600;700&family=Jost:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--cream: #FAF3E9;
--cream-dark: #F0E4D3;
--sage: #8A9A82;
--sage-dark: #5A6B54;
--terracotta: #C97B5F;
--terracotta-dark: #A85C42;
--ink: #3A362F;
--ink-soft: rgba(58,54,47,0.65);
}
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--cream);
color: var(--ink);
font-family: 'Jost', sans-serif;
display: flex;
justify-content: center;
min-height: 100vh;
}
#app {
width: 100%;
max-width: 640px;
padding: 24px;
display: flex;
flex-direction: column;
min-height: 100vh;
}
h1 {
font-family: 'Cormorant Garamond', serif;
font-weight: 700;
color: var(--terracotta-dark);
font-size: 30px;
margin: 8px 0 10px;
text-align: center;
}
.intro {
text-align: center;
color: var(--ink-soft);
font-size: 15px;
line-height: 1.5;
margin: 0 0 14px;
}
#consent-gate {
background: var(--cream-dark);
border: 1px solid rgba(90,107,84,0.3);
border-radius: 14px;
padding: 22px 20px;
margin-bottom: 8px;
}
#consent-gate h2 {
font-family: 'Cormorant Garamond', serif;
font-weight: 700;
color: var(--terracotta-dark);
font-size: 19px;
margin: 0 0 10px;
text-align: center;
}
#consent-gate p {
font-size: 14.5px;
line-height: 1.55;
color: var(--ink);
margin: 0 0 10px;
}
#consent-gate ul {
margin: 0 0 14px;
padding-left: 20px;
font-size: 14.5px;
line-height: 1.5;
}
#consent-gate li { margin-bottom: 4px; }
.consent-check {
display: flex;
align-items: flex-start;
gap: 10px;
background: var(--cream);
border-radius: 10px;
padding: 12px 14px;
margin-bottom: 14px;
}
.consent-check input {
margin-top: 3px;
width: 18px;
height: 18px;
flex-shrink: 0;
accent-color: var(--terracotta);
}
.consent-check label {
font-size: 14px;
line-height: 1.45;
color: var(--ink);
}
#consent-continue {
width: 100%;
background: var(--terracotta);
color: #fff;
border: none;
border-radius: 10px;
padding: 13px;
font-family: 'Jost', sans-serif;
font-weight: 500;
font-size: 15px;
cursor: pointer;
}
#consent-continue:disabled {
background: rgba(201,123,95,0.4);
cursor: not-allowed;
}
#consent-continue:not(:disabled):hover { background: var(--terracotta-dark); }
.site-footer {
text-align: center;
padding-top: 14px;
margin-top: auto;
}
.site-footer a {
color: var(--ink-soft);
font-size: 12.5px;
text-decoration: underline;
}
#chat {
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 12px;
padding-bottom: 16px;
}
.msg {
padding: 12px 16px;
border-radius: 14px;
max-width: 85%;
line-height: 1.4;
white-space: pre-wrap;
}
.bot {
background: var(--sage);
color: #fff;
align-self: flex-start;
border-bottom-left-radius: 4px;
}
.user {
background: var(--terracotta);
color: #fff;
align-self: flex-end;
border-bottom-right-radius: 4px;
}
.choice-buttons {
display: flex;
gap: 10px;
margin-top: 8px;
}
.choice-btn {
flex: 1;
background: var(--terracotta);
color: #fff;
border: none;
border-radius: 10px;
padding: 12px;
font-family: 'Jost', sans-serif;
font-weight: 500;
cursor: pointer;
}
.choice-btn:hover { background: var(--terracotta-dark); }
#input-row {
display: flex;
gap: 8px;
padding-top: 8px;
border-top: 1px solid rgba(58,54,47,0.15);
}
#input-row input {
flex: 1;
padding: 12px 14px;
border-radius: 10px;
border: 1px solid rgba(90,107,84,0.3);
background: var(--cream-dark);
color: var(--ink);
font-family: 'Jost', sans-serif;
font-size: 15px;
}
#input-row input::placeholder { color: var(--ink-soft); }
#input-row button {
background: var(--terracotta);
color: #fff;
border: none;
border-radius: 10px;
padding: 0 18px;
font-weight: 500;
cursor: pointer;
}
#input-row button:hover { background: var(--terracotta-dark); }
#loading {
align-self: flex-start;
color: var(--ink-soft);
font-style: italic;
padding: 4px 16px;
}
#error-banner {
background: #B8443A;
color: #fff;
padding: 10px 14px;
border-radius: 10px;
margin-bottom: 10px;
display: none;
}
#doc-result {
display: none;
flex-direction: column;
flex: 1;
}
.doc-actions {
display: flex;
gap: 10px;
margin-bottom: 12px;
}
.doc-actions button {
flex: 1;
border: none;
border-radius: 10px;
padding: 12px;
font-family: 'Jost', sans-serif;
font-weight: 500;
font-size: 14.5px;
cursor: pointer;
}
#print-btn {
background: var(--terracotta);
color: #fff;
}
#print-btn:hover { background: var(--terracotta-dark); }
#start-over-btn {
background: var(--cream-dark);
color: var(--ink);
border: 1px solid rgba(58,54,47,0.2) !important;
}
.doc-hint {
text-align: center;
font-size: 12.5px;
color: var(--ink-soft);
margin: 0 0 12px;
}
#doc-frame-wrap {
flex: 1;
overflow: auto;
border-radius: 12px;
background: #ddd;
-webkit-overflow-scrolling: touch;
}
#doc-frame {
display: block;
width: 850px;
min-height: 400px;
border: none;
}
</style>
</head>
<body>
<div id="app">
<h1>All About My Child</h1>
<p class="intro">A short, guided conversation that turns what you know about your child into a one-page reference — for a new teacher, aide, or caregiver meeting them for the first time.</p>
<div id="consent-gate">
<h2>Before we start</h2>
<p>This chat asks about your child, and what you share may include health-related details — medical needs, sensory needs, behavior, medications, and similar. Here's how that's handled:</p>
<ul>
<li>Your messages are sent to Anthropic's Claude API to run this chat and build your document.</li>
<li>Nothing is stored on our end — your conversation lives only in this browser tab and disappears when you close or refresh the page.</li>
<li>It is not used to train Anthropic's AI models, and is automatically deleted from their systems within 30 days.</li>
<li>We don't sell it, share it with advertisers, or use it for anything besides building your document.</li>
</ul>
<div class="consent-check">
<input type="checkbox" id="consent-checkbox" />
<label for="consent-checkbox">I understand and agree to how my information is handled, as described here and in the full <a href="/privacy-policy.html" target="_blank">Privacy Policy</a>.</label>
</div>
<button id="consent-continue" disabled>Continue</button>
</div>
<div id="chat-wrap" style="display:none;">
<div id="error-banner"></div>
<div id="chat"></div>
<div id="input-row" style="display:none;">
<input type="text" id="user-input" placeholder="Type your answer..." />
<button id="send-btn">Send</button>
</div>
</div>
<div id="doc-result">
<div class="doc-actions">
<button id="print-btn">Print / Save as PDF</button>
<button id="start-over-btn">Start Over</button>
</div>
<p class="doc-hint">In the print window, choose "Save as PDF" as the destination to save a copy.</p>
<div id="doc-frame-wrap">
<iframe id="doc-frame" title="Your child's reference document"></iframe>
</div>
</div>
<div class="site-footer"><a href="/privacy-policy.html" target="_blank">Privacy Policy</a></div>
</div>
<script>
// TODO: this should point at your deployed Worker URL.
const WORKER_URL = "https://twilight-fire-243b.cbscodingsuccess.workers.dev/";
const chatEl = document.getElementById('chat');
const chatWrap = document.getElementById('chat-wrap');
const inputRow = document.getElementById('input-row');
const inputEl = document.getElementById('user-input');
const sendBtn = document.getElementById('send-btn');
const errorBanner = document.getElementById('error-banner');
const consentGate = document.getElementById('consent-gate');
const consentCheckbox = document.getElementById('consent-checkbox');
const consentContinue = document.getElementById('consent-continue');
const docResult = document.getElementById('doc-result');
const docFrame = document.getElementById('doc-frame');
const docFrameWrap = document.getElementById('doc-frame-wrap');
const printBtn = document.getElementById('print-btn');
const startOverBtn = document.getElementById('start-over-btn');
let messages = [];
let waitingForModeChoice = true;
function addMessage(role, text) {
const div = document.createElement('div');
div.className = 'msg ' + (role === 'user' ? 'user' : 'bot');
div.textContent = text;
chatEl.appendChild(div);
chatEl.scrollTop = chatEl.scrollHeight;
}
function showLoading() {
const div = document.createElement('div');
div.id = 'loading';
div.textContent = 'Thinking...';
chatEl.appendChild(div);
chatEl.scrollTop = chatEl.scrollHeight;
}
function hideLoading() {
const el = document.getElementById('loading');
if (el) el.remove();
}
function showError(text) {
errorBanner.textContent = text;
errorBanner.style.display = 'block';
}
function hideError() {
errorBanner.style.display = 'none';
}
async function callWorker() {
showLoading();
hideError();
try {
const res = await fetch(WORKER_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messages })
});
const data = await res.json();
hideLoading();
if (!res.ok) {
showError(data.error || 'Something went wrong. Please try again.');
return;
}
const reply = data.reply || '';
messages.push({ role: 'assistant', content: reply });
const markerIndex = reply.indexOf('READY_FOR_DOCUMENT:');
if (markerIndex !== -1) {
addMessage('bot', "Thank you — I have everything I need. Building your document now, one moment...");
inputRow.style.display = 'none';
const summaryText = reply.slice(markerIndex + 'READY_FOR_DOCUMENT:'.length).trim();
generateDocument(summaryText);
} else {
addMessage('bot', reply);
}
} catch (err) {
hideLoading();
showError('Could not reach the server. Check your connection and try again.');
}
}
async function generateDocument(summaryText) {
showLoading();
hideError();
try {
const res = await fetch(WORKER_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'generate_document', summary: summaryText })
});
const data = await res.json();
hideLoading();
if (!res.ok) {
showError(data.error || 'Could not build the document. Please try again.');
addRetryButton(summaryText);
return;
}
showDocument(data.html);
} catch (err) {
hideLoading();
showError('Could not reach the server. Check your connection and try again.');
addRetryButton(summaryText);
}
}
function addRetryButton(summaryText) {
const btn = document.createElement('button');
btn.className = 'choice-btn';
btn.textContent = 'Try building the document again';
btn.style.marginTop = '4px';
btn.onclick = () => {
btn.remove();
generateDocument(summaryText);
};
chatEl.appendChild(btn);
chatEl.scrollTop = chatEl.scrollHeight;
}
function showDocument(html) {
chatWrap.querySelectorAll('#chat, #input-row, #error-banner').forEach(el => {
if (el.id !== 'error-banner') el.style.display = 'none';
});
docResult.style.display = 'flex';
docFrame.srcdoc = html;
docFrame.onload = () => {
try {
const doc = docFrame.contentWindow.document;
docFrame.style.height = doc.documentElement.scrollHeight + 'px';
} catch (e) { /* ignore */ }
fitDocFrame();
};
}
function fitDocFrame() {
const wrapWidth = docFrameWrap.clientWidth;
const nativeWidth = 850;
if (wrapWidth < nativeWidth) {
const scale = wrapWidth / nativeWidth;
docFrame.style.transform = `scale(${scale})`;
docFrame.style.transformOrigin = 'top left';
docFrameWrap.style.height = (docFrame.offsetHeight * scale) + 'px';
} else {
docFrame.style.transform = 'none';
docFrameWrap.style.height = docFrame.offsetHeight + 'px';
}
}
window.addEventListener('resize', fitDocFrame);
printBtn.addEventListener('click', () => {
docFrame.contentWindow.focus();
docFrame.contentWindow.print();
});
startOverBtn.addEventListener('click', () => {
location.reload();
});
function sendUserMessage(text) {
if (!text.trim()) return;
messages.push({ role: 'user', content: text });
addMessage('user', text);
inputEl.value = '';
callWorker();
}
sendBtn.addEventListener('click', () => sendUserMessage(inputEl.value));
inputEl.addEventListener('keydown', (e) => {
if (e.key === 'Enter') sendUserMessage(inputEl.value);
});
// Kick off the conversation with the opening choice message.
function start() {
addMessage('bot', "Let's build your child's document. You can do this two ways:");
const choiceDiv = document.createElement('div');
choiceDiv.className = 'choice-buttons';
const btnA = document.createElement('button');
btnA.className = 'choice-btn';
btnA.textContent = 'A) Brain dump';
btnA.onclick = () => chooseMode('A) Brain dump — I want to just tell you everything about my child freely.');
const btnB = document.createElement('button');
btnB.className = 'choice-btn';
btnB.textContent = 'B) Guided questions';
btnB.onclick = () => chooseMode('B) Guided questions — please ask me one question at a time.');
choiceDiv.appendChild(btnA);
choiceDiv.appendChild(btnB);
chatEl.appendChild(choiceDiv);
}
function chooseMode(choiceText) {
// Remove the choice buttons once picked
document.querySelectorAll('.choice-buttons').forEach(el => el.remove());
addMessage('user', choiceText);
messages.push({ role: 'user', content: choiceText });
inputRow.style.display = 'flex';
callWorker();
}
consentCheckbox.addEventListener('change', () => {
consentContinue.disabled = !consentCheckbox.checked;
});
consentContinue.addEventListener('click', () => {
if (!consentCheckbox.checked) return;
consentGate.style.display = 'none';
chatWrap.style.display = 'block';
start();
});
</script>
</body>
</html>