// SCN2A Australia — "Add your family" (Join) page.
//
// ONE flow, TWO independent actions:
//   · The Family Map (public, opt-in, fully skippable) — a pin with first name,
//     state and general area only. Every map submission is reviewed by the team
//     before a pin appears.
//   · The Family Registry (private, never published) — contact and clinical
//     details that help the org connect families with support, resources and
//     research. Ends in a required consent checkbox linking to /privacy.
//
// Steps: 0 About you (required) -> 1 Family Map (optional, skippable) ->
//        2 Family Registry (skippable; consent required to submit) -> 3 Done.
//
// Submission POSTs one canonical JSON payload to /api/join (see api/join.js).
// If the network call fails, the payload is parked in localStorage under
// JOIN_PENDING_KEY and the confirmation still shows, with a gentle note.
//
// Consent model enforced client-side (api/join.js re-checks server-side):
//   · Map data is only included in the payload if the map step wasn't skipped.
//   · Registry data is only included if the registry step was completed, and
//     completing it requires registryConsent.
//   · If someone skips the map (or declines map consent) AND skips the
//     registry, nothing is sent anywhere — the confirmation says so.

const JOIN_ENDPOINT = '/api/join';
const JOIN_PENDING_KEY = 'scn2a-join-pending';
const JOIN_SUPPORT_EMAIL = 'support@scn2aaustralia.org';

const JOIN_STATES = ['NSW', 'VIC', 'QLD', 'SA', 'WA', 'TAS', 'ACT', 'NT', 'Outside Australia'];
const JOIN_RELATIONSHIPS = ['Parent/Guardian', 'Self (adult with SCN2A)', 'Other family member', 'Other'];
const JOIN_TOPICS = ['Diagnosis and early days', 'Seizures and medication', 'Therapies and interventions', 'Schooling and NDIS', 'Daily life and routines'];
const JOIN_VARIANT_TYPES = ['Gain of Function', 'Loss of Function', 'Mixed', 'Unsure'];
const JOIN_SEIZURES = ['Yes', 'No', 'Unsure'];
const JOIN_RESEARCH = ['Yes', 'No', 'Maybe / tell me more'];
const JOIN_VOLUNTEER = ['Yes', 'Not at this stage', 'Tell me more!'];
const JOIN_ACTIVITIES = ['Resources', 'Peer Connection', 'Conferences', 'Family Meet Ups'];

const JOIN_EMAIL_RE = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;

// ---- Shared field styling (mirrors PinPreviewForm on the Family Map page) ----
const joinFieldStyle = (error) => ({
  width: '100%', boxSizing: 'border-box', padding: '10px 12px',
  border: `1px solid ${error ? '#8E3B5E' : '#D5CFBF'}`, borderRadius: 5,
  fontFamily: 'inherit', fontSize: 14, color: '#0D1B2A', background: '#fff',
});
const JOIN_LABEL_STYLE = { display: 'block', fontSize: 11, fontWeight: 600, color: '#0D1B2A', marginBottom: 6 };
const JOIN_HINT_STYLE = { fontSize: 11.5, color: '#6B6659', lineHeight: 1.55, margin: '6px 0 0' };
const JOIN_ERROR_STYLE = { fontSize: 12.5, color: '#8E3B5E', marginTop: 6, lineHeight: 1.4 };

// One labelled field: label (+ optional tag), the control, then hint and error.
function JoinField({ id, label, optional, hint, error, children, style }) {
  return (
    <div style={{ marginBottom: 18, ...style }}>
      <label htmlFor={id} style={JOIN_LABEL_STYLE}>
        {label}{optional && <span style={{ fontWeight: 500, color: '#6B6659' }}> (optional)</span>}
      </label>
      {children}
      {hint && <p style={JOIN_HINT_STYLE}>{hint}</p>}
      {error && <div role="alert" style={JOIN_ERROR_STYLE}>{error}</div>}
    </div>
  );
}

// A small eyebrow divider used to group fields inside the registry step.
function JoinGroupLabel({ children }) {
  return (
    <div className="eyebrow" style={{ color: '#6B6659', margin: '26px 0 14px', paddingTop: 18, borderTop: '1px solid #E8E3DA' }}>
      {children}
    </div>
  );
}

// A framed consent checkbox. `tone` shifts the frame: 'map' (soft blue) or
// 'registry' (sand). When `error` is set the frame turns mulberry.
function JoinConsent({ id, checked, onChange, tone, error, children }) {
  const bg = tone === 'map' ? '#E5EDF8' : '#EFEAE1';
  return (
    <div style={{ background: bg, border: `1px solid ${error ? '#8E3B5E' : '#D5CFBF'}`, borderRadius: 5, padding: '14px 16px', marginTop: 6 }}>
      <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>
        <input id={id} type="checkbox" checked={checked} onChange={onChange}
          style={{ marginTop: 2, width: 17, height: 17, flexShrink: 0, accentColor: '#1E3A6E', cursor: 'pointer' }} />
        <label htmlFor={id} style={{ fontSize: 13, lineHeight: 1.55, color: '#0D1B2A', cursor: 'pointer' }}>
          {children}
        </label>
      </div>
      {error && <div role="alert" style={{ ...JOIN_ERROR_STYLE, marginLeft: 27 }}>{error}</div>}
    </div>
  );
}

// ---- Progress indicator: four dots, done / current / upcoming ----
function JoinProgress({ step }) {
  const labels = ['About you', 'Family Map', 'Registry', 'Done'];
  return (
    <ol aria-label={`Step ${Math.min(step, 3) + 1} of 4`} style={{ listStyle: 'none', display: 'flex', alignItems: 'flex-start', margin: '0 0 28px', padding: 0 }}>
      {labels.map((label, i) => {
        const done = i < step;
        const current = i === step;
        return (
          <li key={label} aria-current={current ? 'step' : undefined}
            style={{ flex: i < labels.length - 1 ? 1 : '0 0 auto', display: 'flex', alignItems: 'flex-start' }}>
            <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: 72 }}>
              <span aria-hidden="true" style={{
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                width: 30, height: 30, borderRadius: 999, fontSize: 13, fontWeight: 800,
                background: done || current ? '#1E3A6E' : '#fff',
                color: done || current ? '#fff' : '#6B6659',
                border: `2px solid ${done || current ? '#1E3A6E' : '#D5CFBF'}`,
                transition: 'background 200ms, border-color 200ms',
              }}>{done ? '✓' : i + 1}</span>
              <span style={{ marginTop: 7, fontSize: 11, fontWeight: current ? 700 : 600, color: current ? '#0D1B2A' : '#6B6659', letterSpacing: '0.02em', textAlign: 'center' }}>
                {label}{i === 1 && <span style={{ display: 'block', fontWeight: 500, color: '#6B6659' }}>optional</span>}
              </span>
            </div>
            {i < labels.length - 1 && (
              <span aria-hidden="true" style={{ flex: 1, height: 2, background: i < step ? '#1E3A6E' : '#D5CFBF', margin: '14px -14px 0', borderRadius: 2, transition: 'background 200ms' }} />
            )}
          </li>
        );
      })}
    </ol>
  );
}

// ---- The page ----
function JoinPage({ navigate }) {
  const [step, setStep] = React.useState(0);

  // Step 0 — about you (always required)
  const [about, setAbout] = React.useState({ firstName: '', relationship: '', state: '' });
  // Step 1 — family map (optional)
  const [map, setMap] = React.useState({ suburb: '', personFirstName: '', personAge: '', topics: [], blurb: '', openToMessages: false, mapConsent: false });
  const [mapSkipped, setMapSkipped] = React.useState(false);
  const [softWarn, setSoftWarn] = React.useState(false);
  // Step 2 — registry (private; consent required to submit)
  const [reg, setReg] = React.useState({
    email: '', lastName: '', phone: '', personDob: '', personLastName: '',
    streetAddress: '', postcode: '', country: 'Australia', variant: '', variantType: '',
    seizures: '', seizuresAgeStarted: '', researchContact: '', volunteer: '', activities: [],
    registryConsent: false,
  });

  const [errors, setErrors] = React.useState({});
  const [submitting, setSubmitting] = React.useState(false);
  // outcome: { joinedMap, joinedRegistry, offline, nothing } — drives Step 3.
  const [outcome, setOutcome] = React.useState(null);
  const [website, setWebsite] = React.useState(''); // honeypot — humans never see it

  const setA = (patch) => { setAbout(a => ({ ...a, ...patch })); setErrors({}); };
  const setM = (patch) => { setMap(m => ({ ...m, ...patch })); setErrors({}); };
  const setR = (patch) => { setReg(r => ({ ...r, ...patch })); setErrors({}); };
  const toggleIn = (list, value) => (list.includes(value) ? list.filter(v => v !== value) : [...list, value]);

  const scrollToFlow = () => {
    const el = document.getElementById('join-flow');
    if (!el) return;
    const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    el.scrollIntoView({ behavior: reduce ? 'auto' : 'smooth', block: 'start' });
  };
  const goTo = (n) => { setStep(n); setErrors({}); scrollToFlow(); };

  const mapHasData = () =>
    !!(map.suburb.trim() || map.personFirstName.trim() || String(map.personAge).trim() || map.topics.length || map.blurb.trim() || map.openToMessages);
  const didJoinMap = !mapSkipped && map.mapConsent;

  // ---- Step validation ----
  const validateAbout = () => {
    const e = {};
    if (!about.firstName.trim()) e.firstName = 'Please tell us your first name.';
    if (!about.relationship) e.relationship = 'Please choose the option closest to you.';
    if (!about.state) e.state = 'Please choose your state or territory.';
    return e;
  };
  const validateMap = () => {
    const e = {};
    const age = String(map.personAge).trim();
    if (age && (!/^\d{1,3}$/.test(age) || Number(age) > 120)) e.personAge = 'Please enter an age in years, e.g. 6.';
    return e;
  };
  const validateRegistry = () => {
    const e = {};
    if (!reg.email.trim()) e.email = 'Please enter your email address.';
    else if (!JOIN_EMAIL_RE.test(reg.email.trim())) e.email = 'That email address does not look right, please check it.';
    if (!reg.lastName.trim()) e.lastName = 'Please enter your last name.';
    if (!reg.personLastName.trim()) e.personLastName = 'Please enter their last name.';
    if (!reg.personDob) e.personDob = 'Please enter their date of birth.';
    else if (new Date(reg.personDob) > new Date()) e.personDob = 'Please check the date of birth.';
    if (!reg.country.trim()) e.country = 'Please enter your country.';
    if (!reg.researchContact) e.researchContact = 'Please choose an option, "Maybe / tell me more" is fine.';
    if (!reg.registryConsent) e.registryConsent = 'To join the registry we need this consent. If you would rather not, use "Skip for now" below.';
    return e;
  };

  // ---- Step transitions ----
  const continueFromAbout = () => {
    const e = validateAbout();
    if (Object.keys(e).length) { setErrors(e); return; }
    goTo(1);
  };
  const continueFromMap = () => {
    const e = validateMap();
    if (Object.keys(e).length) { setErrors(e); return; }
    // Soft warning: fields filled but the map consent box left unticked.
    // Shown once; a second Continue moves on regardless.
    if (mapHasData() && !map.mapConsent && !softWarn) { setSoftWarn(true); return; }
    setMapSkipped(false); setSoftWarn(false); goTo(2);
  };
  const skipMap = () => { setMapSkipped(true); setSoftWarn(false); goTo(2); };

  // ---- Payload + submission ----
  // Canonical shape expected by /api/join and the n8n intake workflow.
  const buildPayload = (includeRegistry) => {
    const useMap = !mapSkipped;
    return {
      firstName: about.firstName.trim().slice(0, 80),
      lastName: includeRegistry ? reg.lastName.trim().slice(0, 80) : '',
      email: includeRegistry ? reg.email.trim().slice(0, 160) : '',
      phone: includeRegistry ? reg.phone.trim().slice(0, 40) : '',
      relationship: about.relationship,
      state: about.state,
      suburb: useMap ? map.suburb.trim().slice(0, 80) : '',
      streetAddress: includeRegistry ? reg.streetAddress.trim().slice(0, 200) : '',
      postcode: includeRegistry ? reg.postcode.trim().slice(0, 12) : '',
      country: includeRegistry ? reg.country.trim().slice(0, 80) : '',
      personFirstName: useMap ? map.personFirstName.trim().slice(0, 80) : '',
      personLastName: includeRegistry ? reg.personLastName.trim().slice(0, 80) : '',
      personDob: includeRegistry ? reg.personDob : '',
      personAge: useMap ? String(map.personAge).trim().slice(0, 10) : '',
      variant: includeRegistry ? reg.variant.trim().slice(0, 120) : '',
      variantType: includeRegistry ? reg.variantType : '',
      seizures: includeRegistry ? reg.seizures : '',
      seizuresAgeStarted: includeRegistry && reg.seizures === 'Yes' ? reg.seizuresAgeStarted.trim().slice(0, 80) : '',
      researchContact: includeRegistry ? reg.researchContact : '',
      volunteer: includeRegistry ? reg.volunteer : '',
      activities: includeRegistry ? reg.activities : [],
      topics: useMap ? map.topics : [],
      blurb: useMap ? map.blurb.trim().slice(0, 200) : '',
      openToMessages: useMap ? !!map.openToMessages : false,
      mapConsent: useMap ? !!map.mapConsent : false,
      registryConsent: !!includeRegistry,
      joinedMap: didJoinMap,
      source: 'website-join-form',
      website, // honeypot — always '' for humans
    };
  };

  const submitAll = async (includeRegistry) => {
    const payload = buildPayload(includeRegistry);
    const base = { joinedMap: didJoinMap, joinedRegistry: includeRegistry, nothing: false };
    setSubmitting(true);
    try {
      const res = await fetch(JOIN_ENDPOINT, {
        method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload),
      });
      if (!res.ok) throw new Error('join-failed');
      setOutcome({ ...base, offline: false });
    } catch (err) {
      // Network failure (or local static preview): park the submission in this
      // browser so nothing is lost, and still show the confirmation.
      try {
        localStorage.setItem(JOIN_PENDING_KEY, JSON.stringify({ ...payload, savedAt: Date.now() }));
      } catch (e2) { /* ignore storage errors */ }
      setOutcome({ ...base, offline: true });
    } finally {
      setSubmitting(false);
      setStep(3);
      scrollToFlow();
    }
  };

  const submitRegistry = () => {
    const e = validateRegistry();
    if (Object.keys(e).length) { setErrors(e); return; }
    submitAll(true);
  };
  const skipRegistry = () => {
    if (didJoinMap) { submitAll(false); return; }
    // Nothing was consented to — nothing is sent anywhere.
    setOutcome({ joinedMap: false, joinedRegistry: false, offline: false, nothing: true });
    setStep(3);
    scrollToFlow();
  };

  // ---- Shared step chrome ----
  const stepHeader = (stepNo, title, intro) => (
    <div style={{ marginBottom: 22 }}>
      <div className="eyebrow" style={{ color: '#4A7EC7', marginBottom: 8 }}>{stepNo}</div>
      <h2 className="h2-mobile" style={{ fontSize: 24, fontWeight: 700, letterSpacing: '-0.01em', margin: '0 0 8px', color: '#0D1B2A' }}>{title}</h2>
      {intro && <p style={{ fontSize: 14.5, lineHeight: 1.6, color: '#6B6659', margin: 0 }}>{intro}</p>}
    </div>
  );

  const navRow = (children) => (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 10, justifyContent: 'space-between', alignItems: 'center', marginTop: 26, paddingTop: 20, borderTop: '1px solid #E8E3DA' }}>
      {children}
    </div>
  );

  // ---- Step bodies ----
  const renderAbout = () => (
    <div className="fmap-rise" key="s0">
      {stepHeader('Step 1 of 3 · About you', "Let's start with you.", 'Just three quick things so we know who we are talking to. Everything after this is your choice.')}
      <JoinField id="join-first-name" label="Your first name" hint="This is the name we will use when we talk with you." error={errors.firstName}>
        <input id="join-first-name" type="text" value={about.firstName} maxLength={80} autoComplete="given-name"
          onChange={(e) => setA({ firstName: e.target.value })} placeholder="e.g. Sam" style={joinFieldStyle(errors.firstName)} />
      </JoinField>
      <JoinField id="join-relationship" label="Your relationship to the person with SCN2A" error={errors.relationship}>
        <select id="join-relationship" value={about.relationship} onChange={(e) => setA({ relationship: e.target.value })} style={joinFieldStyle(errors.relationship)}>
          <option value="">Choose…</option>
          {JOIN_RELATIONSHIPS.map(r => <option key={r} value={r}>{r}</option>)}
        </select>
      </JoinField>
      <JoinField id="join-state" label="State or territory" error={errors.state}>
        <select id="join-state" value={about.state} onChange={(e) => setA({ state: e.target.value })} style={joinFieldStyle(errors.state)}>
          <option value="">Choose…</option>
          {JOIN_STATES.map(s => <option key={s} value={s}>{s}</option>)}
        </select>
      </JoinField>
      {navRow(
        <>
          <span style={{ fontSize: 12, color: '#6B6659' }}>Nothing is sent until the end.</span>
          <Button variant="navy" onClick={continueFromAbout}>Continue →</Button>
        </>
      )}
    </div>
  );

  const renderMap = () => (
    <div className="fmap-rise" key="s1">
      {stepHeader('Step 2 of 3 · The Family Map · Optional', 'Add your pin to the Family Map.',
        'This step is completely optional. If the map is not for you, skip straight past it, the rest of the form works exactly the same.')}
      <div style={{ display: 'flex', gap: 10, padding: '12px 14px', background: '#E5EDF8', borderRadius: 5, marginBottom: 20 }}>
        <span aria-hidden="true" style={{ color: '#4A7EC7', fontWeight: 700, fontSize: 14, flexShrink: 0 }}>✓</span>
        <p style={{ fontSize: 12.5, lineHeight: 1.55, color: '#0D1B2A', margin: 0 }}>
          The map only ever shows a first name, a state and a general area. Pins are placed approximately by our team, never at an address, and nothing appears until you tick the consent box at the bottom of this step.
        </p>
      </div>

      <JoinField id="join-suburb" label="Suburb or general area" optional error={errors.suburb}
        hint="Used to place your pin approximately, e.g. somewhere near your suburb. Your exact address is never shown or asked for here.">
        <input id="join-suburb" type="text" value={map.suburb} maxLength={80}
          onChange={(e) => setM({ suburb: e.target.value })} placeholder="e.g. Fremantle" style={joinFieldStyle(errors.suburb)} />
      </JoinField>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }} className="stack-mobile">
        <JoinField id="join-person-first-name" label="First name of the person with SCN2A" optional error={errors.personFirstName}>
          <input id="join-person-first-name" type="text" value={map.personFirstName} maxLength={80}
            onChange={(e) => setM({ personFirstName: e.target.value })} placeholder="e.g. Alex" style={joinFieldStyle(errors.personFirstName)} />
        </JoinField>
        <JoinField id="join-person-age" label="Their age" optional hint="Shown as an age only, e.g. age 6." error={errors.personAge}>
          <input id="join-person-age" type="number" inputMode="numeric" min="0" max="120" value={map.personAge}
            onChange={(e) => setM({ personAge: e.target.value })} placeholder="e.g. 6" style={joinFieldStyle(errors.personAge)} />
        </JoinField>
      </div>

      <fieldset style={{ border: 'none', padding: 0, margin: '0 0 18px' }}>
        <legend style={{ ...JOIN_LABEL_STYLE, padding: 0 }}>Topics you are happy to talk about <span style={{ fontWeight: 500, color: '#6B6659' }}>(choose any)</span></legend>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginTop: 4 }}>
          {JOIN_TOPICS.map(t => (
            <label key={t} style={{ display: 'flex', alignItems: 'flex-start', gap: 9, fontSize: 13, lineHeight: 1.5, color: '#0D1B2A', cursor: 'pointer' }}>
              <input type="checkbox" checked={map.topics.includes(t)} onChange={() => setM({ topics: toggleIn(map.topics, t) })}
                style={{ marginTop: 2, width: 15, height: 15, flexShrink: 0, accentColor: '#1E3A6E' }} />
              {t}
            </label>
          ))}
        </div>
      </fieldset>

      <JoinField id="join-blurb" label="A short note for your card" optional error={errors.blurb}
        hint={`A sentence or two other families will see on your card. ${200 - map.blurb.length} characters left.`}>
        <textarea id="join-blurb" value={map.blurb} maxLength={200} rows="3"
          onChange={(e) => setM({ blurb: e.target.value })}
          placeholder="e.g. Diagnosed at 14 months. Happy to share what we've learned about the NDIS…"
          style={{ ...joinFieldStyle(errors.blurb), lineHeight: 1.5, resize: 'vertical' }} />
      </JoinField>

      <label style={{ display: 'flex', alignItems: 'flex-start', gap: 9, margin: '0 0 6px', cursor: 'pointer' }}>
        <input type="checkbox" checked={map.openToMessages} onChange={(e) => setM({ openToMessages: e.target.checked })}
          style={{ marginTop: 2, width: 16, height: 16, flexShrink: 0, accentColor: '#1E3A6E' }} />
        <span style={{ fontSize: 13, lineHeight: 1.55, color: '#0D1B2A' }}>
          <strong>I am open to private messages from other families.</strong>
          <span style={{ display: 'block', fontSize: 12, color: '#6B6659', marginTop: 3 }}>
            Messages are relayed by the SCN2A Australia team. Your contact details are never shared automatically, you decide if and how you reply.
          </span>
        </span>
      </label>

      <JoinConsent id="join-map-consent" tone="map" checked={map.mapConsent}
        onChange={(e) => { setM({ mapConsent: e.target.checked }); setSoftWarn(false); }}>
        <strong>Show our family (first name, state and general area only) on the SCN2A Family Map,</strong> and pass on private messages if I have opted in above.
      </JoinConsent>

      {softWarn && (
        <div role="status" style={{ display: 'flex', gap: 10, background: '#EFEAE1', border: '1px solid #D5CFBF', borderLeft: '3px solid #8E3B5E', borderRadius: 5, padding: '12px 14px', marginTop: 12 }}>
          <p style={{ fontSize: 12.5, lineHeight: 1.55, color: '#0D1B2A', margin: 0 }}>
            Just checking, you have filled in some map details but not ticked the box above, so <strong>nothing will appear on the map</strong>. Your answers still come to us privately with the rest of this form. Tick the box if you would like a pin, or choose Continue again to move on as you are.
          </p>
        </div>
      )}

      {navRow(
        <>
          <Button variant="outline" onClick={() => goTo(0)}>← Back</Button>
          <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
            <Button variant="outline" onClick={skipMap}>Skip this step</Button>
            <Button variant="navy" onClick={continueFromMap}>Continue →</Button>
          </div>
        </>
      )}
    </div>
  );

  const renderRegistry = () => (
    <div className="fmap-rise" key="s2">
      {stepHeader('Step 3 of 3 · The Family Registry', 'Join the Family Registry.',
        'This part is private and never published. It helps us connect you with support, resources and research.')}

      <JoinGroupLabel>Your contact details</JoinGroupLabel>
      <JoinField id="join-email" label="Your email" error={errors.email} hint="Where your welcome email and anything you opt into will arrive.">
        <input id="join-email" type="email" value={reg.email} maxLength={160} autoComplete="email"
          onChange={(e) => setR({ email: e.target.value })} placeholder="you@example.com" style={joinFieldStyle(errors.email)} />
      </JoinField>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }} className="stack-mobile">
        <JoinField id="join-last-name" label="Your last name (primary contact)" error={errors.lastName}>
          <input id="join-last-name" type="text" value={reg.lastName} maxLength={80} autoComplete="family-name"
            onChange={(e) => setR({ lastName: e.target.value })} placeholder="e.g. Nguyen" style={joinFieldStyle(errors.lastName)} />
        </JoinField>
        <JoinField id="join-phone" label="Phone" optional error={errors.phone}>
          <input id="join-phone" type="tel" value={reg.phone} maxLength={40} autoComplete="tel"
            onChange={(e) => setR({ phone: e.target.value })} placeholder="e.g. 0400 000 000" style={joinFieldStyle(errors.phone)} />
        </JoinField>
      </div>

      <JoinGroupLabel>The person with SCN2A</JoinGroupLabel>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }} className="stack-mobile">
        <JoinField id="join-person-last-name" label="Their last name" error={errors.personLastName}>
          <input id="join-person-last-name" type="text" value={reg.personLastName} maxLength={80}
            onChange={(e) => setR({ personLastName: e.target.value })} style={joinFieldStyle(errors.personLastName)} />
        </JoinField>
        <JoinField id="join-person-dob" label="Their date of birth" error={errors.personDob}
          hint="Kept private. Only ever shown publicly as an age, and only if you joined the map.">
          <input id="join-person-dob" type="date" value={reg.personDob} max={new Date().toISOString().slice(0, 10)}
            onChange={(e) => setR({ personDob: e.target.value })} style={joinFieldStyle(errors.personDob)} />
        </JoinField>
      </div>

      <JoinGroupLabel>Address</JoinGroupLabel>
      <JoinField id="join-street" label="Street address" optional error={errors.streetAddress}
        hint="Helps us post resources and invite you to things nearby. Never shown anywhere.">
        <input id="join-street" type="text" value={reg.streetAddress} maxLength={200} autoComplete="street-address"
          onChange={(e) => setR({ streetAddress: e.target.value })} style={joinFieldStyle(errors.streetAddress)} />
      </JoinField>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 2fr', gap: 14 }} className="stack-mobile">
        <JoinField id="join-postcode" label="Postcode" optional error={errors.postcode}>
          <input id="join-postcode" type="text" inputMode="numeric" value={reg.postcode} maxLength={12} autoComplete="postal-code"
            onChange={(e) => setR({ postcode: e.target.value })} style={joinFieldStyle(errors.postcode)} />
        </JoinField>
        <JoinField id="join-country" label="Country" error={errors.country}>
          <input id="join-country" type="text" value={reg.country} maxLength={80} autoComplete="country-name"
            onChange={(e) => setR({ country: e.target.value })} style={joinFieldStyle(errors.country)} />
        </JoinField>
      </div>

      <JoinGroupLabel>SCN2A details</JoinGroupLabel>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }} className="stack-mobile">
        <JoinField id="join-variant" label="SCN2A variant" optional error={errors.variant}>
          <input id="join-variant" type="text" value={reg.variant} maxLength={120}
            onChange={(e) => setR({ variant: e.target.value })} placeholder="e.g. p.Arg853Gln, Unsure is fine" style={joinFieldStyle(errors.variant)} />
        </JoinField>
        <JoinField id="join-variant-type" label="Variant type" optional error={errors.variantType}>
          <select id="join-variant-type" value={reg.variantType} onChange={(e) => setR({ variantType: e.target.value })} style={joinFieldStyle(errors.variantType)}>
            <option value="">Choose…</option>
            {JOIN_VARIANT_TYPES.map(v => <option key={v} value={v}>{v}</option>)}
          </select>
        </JoinField>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }} className="stack-mobile">
        <JoinField id="join-seizures" label="Does your child have seizures?" optional error={errors.seizures}>
          <select id="join-seizures" value={reg.seizures} onChange={(e) => setR({ seizures: e.target.value, seizuresAgeStarted: e.target.value === 'Yes' ? reg.seizuresAgeStarted : '' })} style={joinFieldStyle(errors.seizures)}>
            <option value="">Choose…</option>
            {JOIN_SEIZURES.map(v => <option key={v} value={v}>{v}</option>)}
          </select>
        </JoinField>
        {reg.seizures === 'Yes' && (
          <JoinField id="join-seizures-age" label="Age seizures started" optional error={errors.seizuresAgeStarted}>
            <input id="join-seizures-age" type="text" value={reg.seizuresAgeStarted} maxLength={80}
              onChange={(e) => setR({ seizuresAgeStarted: e.target.value })} placeholder="e.g. 3 months" style={joinFieldStyle(errors.seizuresAgeStarted)} />
          </JoinField>
        )}
      </div>

      <JoinGroupLabel>Research and getting involved</JoinGroupLabel>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }} className="stack-mobile">
        <JoinField id="join-research" label="Willing to be contacted about research opportunities?" error={errors.researchContact}>
          <select id="join-research" value={reg.researchContact} onChange={(e) => setR({ researchContact: e.target.value })} style={joinFieldStyle(errors.researchContact)}>
            <option value="">Choose…</option>
            {JOIN_RESEARCH.map(v => <option key={v} value={v}>{v}</option>)}
          </select>
        </JoinField>
        <JoinField id="join-volunteer" label="Willing to volunteer?" optional error={errors.volunteer}>
          <select id="join-volunteer" value={reg.volunteer} onChange={(e) => setR({ volunteer: e.target.value })} style={joinFieldStyle(errors.volunteer)}>
            <option value="">Choose…</option>
            {JOIN_VOLUNTEER.map(v => <option key={v} value={v}>{v}</option>)}
          </select>
        </JoinField>
      </div>
      <fieldset style={{ border: 'none', padding: 0, margin: '0 0 6px' }}>
        <legend style={{ ...JOIN_LABEL_STYLE, padding: 0 }}>Activities you would like to see offered <span style={{ fontWeight: 500, color: '#6B6659' }}>(choose any)</span></legend>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px 20px', marginTop: 4 }}>
          {JOIN_ACTIVITIES.map(a => (
            <label key={a} style={{ display: 'flex', alignItems: 'center', gap: 7, fontSize: 13, color: '#0D1B2A', cursor: 'pointer' }}>
              <input type="checkbox" checked={reg.activities.includes(a)} onChange={() => setR({ activities: toggleIn(reg.activities, a) })}
                style={{ width: 15, height: 15, accentColor: '#1E3A6E' }} />
              {a}
            </label>
          ))}
        </div>
      </fieldset>

      <div style={{ marginTop: 20 }}>
        <JoinConsent id="join-registry-consent" tone="registry" checked={reg.registryConsent} error={errors.registryConsent}
          onChange={(e) => setR({ registryConsent: e.target.checked })}>
          <strong>I consent to SCN2A Australia storing and using my information as described in the{' '}
            <a href="/privacy" onClick={(e) => { e.preventDefault(); e.stopPropagation(); navigate('privacy'); }}
              style={{ color: '#1E3A6E', textDecoration: 'underline', textUnderlineOffset: 2 }}>Privacy Policy</a>,</strong>{' '}
          including referring me to relevant research studies if I have opted in above.
        </JoinConsent>
      </div>

      {navRow(
        <>
          <Button variant="outline" onClick={() => goTo(1)}>← Back</Button>
          <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'center' }}>
            <Button variant="outline" onClick={skipRegistry}>{didJoinMap ? 'Skip, just add our pin' : 'Skip for now'}</Button>
            <Button variant="mulberry" onClick={submitRegistry}>{submitting ? 'Sending…' : (didJoinMap ? 'Join the map and registry →' : 'Join the registry →')}</Button>
          </div>
        </>
      )}
      <p style={{ fontSize: 11.5, color: '#6B6659', lineHeight: 1.55, margin: '14px 0 0', textAlign: 'right' }}>
        You can update or remove your details at any time by emailing {JOIN_SUPPORT_EMAIL}.
      </p>
    </div>
  );

  const renderDone = () => {
    const o = outcome || { joinedMap: false, joinedRegistry: false, offline: false, nothing: true };
    const title = o.nothing
      ? 'No worries, nothing has been saved.'
      : (o.joinedMap && o.joinedRegistry) ? "Thank you, you're part of SCN2A Australia."
      : o.joinedRegistry ? "Thank you, you're on the Family Registry."
      : 'Thank you, your family map pin is on its way.';
    const rows = [];
    if (o.joinedMap) rows.push({ icon: 'map-pin', head: 'Your pin is with our team', body: 'Our team reviews every map submission before your pin appears. Once approved, your family shows as a first name, state and general area only.' });
    if (o.joinedRegistry) rows.push({ icon: 'shield-check', head: "You're on the private Family Registry", body: 'Never published. We use it to connect you with support, resources and, only if you opted in, research opportunities.' });
    if (o.joinedRegistry) rows.push({ icon: 'mail', head: 'Check your inbox, a welcome email is on its way', body: 'It covers what happens next and how to reach us. If it has not arrived within a day or two, check your spam folder or email us.' });
    return (
      <div className="fmap-rise" key="s3" style={{ textAlign: 'center' }}>
        <div aria-hidden="true" style={{ width: 56, height: 56, borderRadius: 999, background: o.nothing ? '#E8E3DA' : '#E5EDF8', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 18px', color: o.nothing ? '#6B6659' : '#4A7EC7', fontSize: 26, fontWeight: 700 }}>
          {o.nothing ? '·' : '✓'}
        </div>
        <h2 className="h2-mobile" style={{ fontSize: 25, fontWeight: 700, letterSpacing: '-0.01em', margin: '0 0 10px', color: '#0D1B2A' }}>{title}</h2>
        {o.nothing ? (
          <p style={{ fontSize: 14.5, lineHeight: 1.65, color: '#0D1B2A', margin: '0 auto 24px', maxWidth: '46ch' }}>
            You chose not to join the map or the registry this time, so nothing was sent to us. The door is always open, come back whenever it feels right, or say hello at {JOIN_SUPPORT_EMAIL}.
          </p>
        ) : (
          <p style={{ fontSize: 14.5, lineHeight: 1.65, color: '#0D1B2A', margin: '0 auto 24px', maxWidth: '48ch' }}>
            {o.joinedMap && o.joinedRegistry
              ? 'Your family is joining the map and the registry. Here is what happens next.'
              : o.joinedRegistry
                ? 'Your details are safely with us, privately. Here is what happens next.'
                : 'Your map details are with our team. You can join the private registry any time, it helps us support you better.'}
          </p>
        )}
        {rows.length > 0 && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10, textAlign: 'left', margin: '0 auto 24px', maxWidth: 520 }}>
            {rows.map(r => (
              <div key={r.head} style={{ display: 'flex', gap: 14, background: '#F7F4F0', border: '1px solid #E8E3DA', borderRadius: 5, padding: '14px 16px' }}>
                <span style={{ flexShrink: 0, color: '#1E3A6E' }}><i data-lucide={r.icon} style={{ width: 20, height: 20 }} /></span>
                <div>
                  <div style={{ fontSize: 14, fontWeight: 700, color: '#0D1B2A', marginBottom: 3 }}>{r.head}</div>
                  <div style={{ fontSize: 13, lineHeight: 1.55, color: '#6B6659' }}>{r.body}</div>
                </div>
              </div>
            ))}
          </div>
        )}
        {o.offline && !o.nothing && (
          <div role="status" style={{ background: '#EFEAE1', border: '1px solid #D5CFBF', borderLeft: '3px solid #4A7EC7', borderRadius: 5, padding: '12px 16px', margin: '0 auto 24px', maxWidth: 520, textAlign: 'left' }}>
            <p style={{ fontSize: 12.5, lineHeight: 1.55, color: '#0D1B2A', margin: 0 }}>
              A small hiccup, we could not reach our server just now, so your answers are saved safely in this browser and we may follow up. If you do not hear from us within a week, please email {JOIN_SUPPORT_EMAIL} and we will sort it out together.
            </p>
          </div>
        )}
        <div style={{ display: 'flex', gap: 10, justifyContent: 'center', flexWrap: 'wrap' }}>
          <Button variant="navy" onClick={() => navigate('family-map')}>See the Family Map</Button>
          <Button variant="outline" onClick={() => navigate('home')}>Back to home</Button>
        </div>
      </div>
    );
  };

  return (
    <div data-screen-label="Add your family">

      {/* ============ HERO (navy feature band) ============ */}
      <section style={{ background: '#1E3A6E', color: '#F7F4F0', position: 'relative', overflow: 'hidden' }}>
        <div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 6, background: '#8E3B5E', zIndex: 2 }} />
        <NetworkMotif opacity={0.12} stroke="#fff" dense />
        <div className="container" style={{ position: 'relative', zIndex: 2, padding: '72px 32px 64px' }}>
          <div style={{ display: 'grid', gridTemplateColumns: '1.25fr 1fr', gap: 56, alignItems: 'center' }} className="stack-mobile">
            <div>
              <div className="eyebrow" style={{ color: '#4A7EC7', marginBottom: 20 }}>Join SCN2A Australia · Add your family</div>
              <h1 className="hero-h1-mobile-lg" style={{ fontSize: 'clamp(34px, 4.2vw, 44px)', fontWeight: 800, letterSpacing: '-0.02em', lineHeight: 1.12, margin: '0 0 20px', color: '#fff', textWrap: 'balance' }}>
                One short form. You choose what is shared.
              </h1>
              <p className="hero-sub-mobile" style={{ fontSize: 17, lineHeight: 1.6, color: '#B9C2D6', maxWidth: '54ch', margin: '0 0 28px' }}>
                This form covers two things at once. A pin on the public Family Map, which is optional and opt-in, and our private Family Registry, which is never published. Do both, or just one, it is entirely up to you.
              </p>
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 12, alignItems: 'center' }}>
                <Button size="lg" variant="mulberry" onClick={scrollToFlow}>Start, it takes about five minutes →</Button>
                <Button size="lg" variant="outlineLight" onClick={() => navigate('family-map')}>See the map first</Button>
              </div>
              <p style={{ fontSize: 12, color: '#B9C2D6', margin: '18px 0 0', opacity: 0.85 }}>Private, secure, and reviewed by our team. You can change or remove your details at any time.</p>
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
              {[
                { icon: 'map-pin', title: 'The Family Map', body: 'Public and opt-in. First name, state and general area only, never an address.' },
                { icon: 'lock', title: 'The Family Registry', body: 'Private and never published. It helps us connect you with support, resources and research.' },
                { icon: 'sliders-horizontal', title: 'Your call, always', body: 'Skip the map, skip the registry, or do both. Every share is a choice you make.' },
              ].map(c => (
                <div key={c.title} style={{ display: 'flex', gap: 16, background: '#18305B', border: '1px solid #26467F', borderRadius: 8, padding: '18px 22px' }}>
                  <span style={{ flexShrink: 0, color: '#4A7EC7' }}><i data-lucide={c.icon} style={{ width: 22, height: 22 }} /></span>
                  <div>
                    <h3 style={{ fontSize: 15, fontWeight: 700, margin: '0 0 4px', color: '#fff' }}>{c.title}</h3>
                    <p style={{ fontSize: 13, lineHeight: 1.5, color: '#B9C2D6', margin: 0 }}>{c.body}</p>
                  </div>
                </div>
              ))}
            </div>
          </div>
        </div>
      </section>

      <Breadcrumbs items={[{ label: 'Support', to: 'support' }, { label: 'Add your family' }]} navigate={navigate} />

      {/* ============ THE FLOW ============ */}
      <section id="join-flow" style={{ background: '#F7F4F0', padding: '56px 0 72px', scrollMarginTop: 90 }}>
        <div className="container" style={{ maxWidth: 820 }}>
          <JoinProgress step={step} />
          <div style={{ background: '#fff', border: '1px solid #D5CFBF', borderTop: '3px solid #4A7EC7', borderRadius: 8, padding: '36px 40px', boxShadow: '0 1px 2px rgba(13,27,42,0.06), 0 1px 3px rgba(13,27,42,0.08)' }} className="pad-mobile">
            {/* Honeypot — hidden from humans and screen readers; bots fill it. */}
            <div aria-hidden="true" style={{ position: 'absolute', left: '-9999px', height: 0, overflow: 'hidden' }}>
              <label htmlFor="join-website">Website</label>
              <input id="join-website" type="text" name="website" tabIndex={-1} autoComplete="off"
                value={website} onChange={(e) => setWebsite(e.target.value)} />
            </div>
            {step === 0 && renderAbout()}
            {step === 1 && renderMap()}
            {step === 2 && renderRegistry()}
            {step === 3 && renderDone()}
          </div>
          {step < 3 && (
            <p style={{ fontSize: 12, color: '#6B6659', lineHeight: 1.6, margin: '16px 4px 0', textAlign: 'center' }}>
              Questions before you share anything? Read our{' '}
              <a href="/privacy" onClick={(e) => { e.preventDefault(); navigate('privacy'); }} style={{ color: '#1E3A6E' }}>Privacy Policy</a>
              {' '}or email {JOIN_SUPPORT_EMAIL}.
            </p>
          )}
        </div>
      </section>

      {/* ============ HOW YOUR INFORMATION IS HANDLED (sandstone band) ============ */}
      <section style={{ background: '#E8E3DA', padding: '64px 0' }}>
        <div className="container">
          <div className="eyebrow" style={{ color: '#6B6659', marginBottom: 12 }}>How your information is handled</div>
          <h2 className="h2-mobile" style={{ fontSize: 28, fontWeight: 700, letterSpacing: '-0.01em', margin: '0 0 36px', maxWidth: '26ch', color: '#0D1B2A' }}>Careful by design, and always reversible.</h2>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 20 }} className="stack-mobile-2">
            {[
              { n: '01', title: 'Map pins are reviewed', body: 'Nothing appears on the Family Map automatically. Our team reviews every submission and places pins approximately, so no family can be identified by location.' },
              { n: '02', title: 'The registry stays private', body: 'Registry details are never published anywhere. We use them to connect you with support, resources and, only if you opt in, research opportunities.' },
              { n: '03', title: 'Change your mind any time', body: `Ask us to update your details, hide your pin, or remove everything, and we will. One email to ${JOIN_SUPPORT_EMAIL} is all it takes.` },
            ].map(s => (
              <div key={s.n} style={{ background: '#fff', border: '1px solid #D5CFBF', borderRadius: 8, padding: 28, borderTop: '3px solid #4A7EC7' }}>
                <div style={{ fontSize: 13, fontWeight: 800, color: '#4A7EC7', marginBottom: 14 }}>{s.n}</div>
                <h3 style={{ fontSize: 17, fontWeight: 700, margin: '0 0 10px', color: '#0D1B2A' }}>{s.title}</h3>
                <p style={{ fontSize: 14.5, lineHeight: 1.6, color: '#0D1B2A', margin: 0 }}>{s.body}</p>
              </div>
            ))}
          </div>
        </div>
      </section>
    </div>
  );
}

Object.assign(window, { JoinPage });
