/* ===== Booking — dual flow: family (5 steps) + student (mission feed) ===== */

const bookingTiers = [
  { id: 'compagnie', label: "Compagnie & répit", gross: 29.50, credit: 11.80, net: 17.70, tone: 'soft' },
  { id: 'aide',      label: "Aide à la vie quotidienne",          gross: 39.08, credit: 15.63, net: 23.45, tone: 'featured' },
  { id: 'pab',       label: "Aide personnelle avec PAB", gross: 44.83, credit: 17.93, net: 26.90, tone: 'accent' },
];

const frequencies = [
  { id: 'ponctuel', t: 'Ponctuel', d: "Une seule intervention" },
  { id: 'hebdo',    t: 'Chaque semaine', d: "Même jour, même créneau" },
  { id: 'sur_mes',  t: 'Sur mesure', d: "Planning à composer" },
];

const days = ['L','Ma','Me','J','V','S','D'];
const slots = ['08:00','10:00','12:00','14:00','16:00','18:00','20:00'];

/** Frais uniques à la première réservation (dossier / sécurité). À transmettre à Stripe avec le total des services (ex. `line_items` ou montant agrégé). */
const FRAIS_ADHESION_DOSSIER = 45;

/**
 * Notifications par courriel — branchées sur la fonction serverless /api/send-email
 * (envoi réel via Resend en production). Fallback mailto: en cas d'échec réseau
 * ou en local quand le serveur statique (npm run dev) ne sert pas /api/*.
 */
const BOOKING_NOTIFICATION_EMAIL = 'contact@maisonsereine.ca';
const STUDENT_ADMIN_EMAIL = 'contact@maisonsereine.ca';
const SEND_EMAIL_ENDPOINT = '/api/send-email';
/** Portail Sterling Backcheck (Canada) — à ajuster si l'URL d'onduisme change. */
const STERLING_BACKCHECK_PORTAL_URL = 'https://www.sterlingbackcheck.ca/';

const STUDENT_INSTITUTIONS = [
  { id: 'ulaval', label: 'Université Laval' },
  { id: 'csfoy', label: 'Cégep de Sainte-Foy' },
  { id: 'limoilou', label: 'Cégep Limoilou' },
  { id: 'garneau', label: 'Cégep Garneau' },
  { id: 'autre', label: 'Autre' },
];

/** Appel JSON minimal à /api/send-email. Retourne `{ ok: true }` ou lève une erreur. */
async function postEmailNotification(payload) {
  const res = await fetch(SEND_EMAIL_ENDPOINT, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  });
  if (!res.ok) {
    let detail = '';
    try { detail = (await res.json()).error || ''; } catch (_) { /* noop */ }
    throw new Error('send-email ' + res.status + (detail ? ' — ' + detail : ''));
  }
  return res.json().catch(() => ({ ok: true }));
}

/** Fallback : ouvre le client mail de l'utilisateur si l'envoi serveur a échoué. */
function openMailtoFallback({ to, subject, body, cc }) {
  const qs = [
    'subject=' + encodeURIComponent(subject || ''),
    'body=' + encodeURIComponent(body || ''),
  ];
  if (cc) qs.push('cc=' + encodeURIComponent(cc));
  const link = document.createElement('a');
  link.href = 'mailto:' + to + '?' + qs.join('&');
  link.setAttribute('rel', 'noopener noreferrer');
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
}

function buildBookingPayload({ data, tierLabel, grossPremierMoisAvecAdhesion, grossMonthly, frequencyLabel, consentLoi25 }) {
  const clientName = (data.contact && data.contact.name) ? String(data.contact.name).trim() : '';
  const phone = (data.contact && data.contact.phone) ? String(data.contact.phone).trim() : '';
  const contactEmail = (data.contact && data.contact.email) ? String(data.contact.email).trim() : '';
  const beneficiary = (data.for || '').trim();
  const daysList = data.daysOn && data.daysOn.length ? data.daysOn.map((i) => days[i]) : [];
  return {
    type: 'booking',
    website: '',
    consentLoi25: consentLoi25 === true,
    consentAt: new Date().toISOString(),
    client: { name: clientName, phone, email: contactEmail },
    beneficiary,
    tierLabel: tierLabel || '',
    frequencyLabel: frequencyLabel || '',
    days: daysList,
    slot: data.slot || '',
    grossPremierMoisAvecAdhesion,
    grossMonthly,
    fraisAdhesion: FRAIS_ADHESION_DOSSIER,
  };
}

/**
 * Envoie la notification de réservation famille.
 * Retourne un statut structuré au composant appelant pour qu'il décide de l'UX.
 *   - { ok: true,  mode: 'server' }   → courriel envoyé via Resend, on peut afficher la page de succès
 *   - { ok: true,  mode: 'mailto' }   → le serveur a échoué mais le client mail a été ouvert (fallback)
 *   - { ok: false, mode: 'none', error } → impossible (en pratique très rare)
 */
async function handleEmailNotification(args) {
  const payload = buildBookingPayload(args);
  if (!payload.consentLoi25) {
    return { ok: false, mode: 'none', error: new Error('Consentement Loi 25 requis.') };
  }
  try {
    await postEmailNotification(payload);
    return { ok: true, mode: 'server' };
  } catch (err) {
    if (typeof console !== 'undefined') console.warn('[booking] Envoi serveur indisponible, fallback mailto:', err);
    const lines = [
      'Bonjour,',
      '',
      'Une nouvelle demande de réservation a été soumise via le site Maison Sereine.',
      '',
      `Nom du client : ${payload.client.name || '—'}`,
      payload.beneficiary ? `Personne accompagnée : ${payload.beneficiary}` : null,
      `Type de soin : ${payload.tierLabel || '—'}`,
      `Jours : ${payload.days.length ? payload.days.join(' · ') : '—'} · Créneau : ${payload.slot || '—'} · Fréquence : ${payload.frequencyLabel || '—'}`,
      `Téléphone : ${payload.client.phone || '—'}`,
      payload.client.email ? `Courriel : ${payload.client.email}` : null,
      '',
      `1re période (services + frais de dossier ${FRAIS_ADHESION_DOSSIER} $) : ${payload.grossPremierMoisAvecAdhesion} $`,
      `Mensualité services : ${payload.grossMonthly} $`,
    ].filter(Boolean).join('\n');
    try {
      openMailtoFallback({
        to: BOOKING_NOTIFICATION_EMAIL,
        subject: `Nouvelle Réservation Maison Sereine - ${payload.client.name || 'Client'}`,
        body: lines,
      });
      return { ok: true, mode: 'mailto' };
    } catch (mailErr) {
      return { ok: false, mode: 'none', error: mailErr };
    }
  }
}

/** Idem côté étudiant : retourne le statut sans rediriger ni rien afficher. */
async function notifyStudentAdmin({ nom, prenom, email, phone, institution, consentLoi25 }) {
  const payload = {
    type: 'student_signup',
    website: '',
    consentLoi25: consentLoi25 === true,
    consentAt: new Date().toISOString(),
    nom: (nom || '').trim(),
    prenom: (prenom || '').trim(),
    email: (email || '').trim(),
    phone: (phone || '').trim(),
    institution: (institution || '').trim(),
  };
  try {
    await postEmailNotification(payload);
    return { ok: true, mode: 'server' };
  } catch (err) {
    if (typeof console !== 'undefined') console.warn('[student] Envoi serveur indisponible, fallback mailto:', err);
    const displayName = [payload.prenom, payload.nom].filter(Boolean).join(' ').trim() || payload.nom || 'Étudiant';
    const body = [
      `Nouvelle candidature étudiante.`,
      ``,
      `Prénom : ${payload.prenom || '—'}`,
      `Nom : ${payload.nom || '—'}`,
      `Courriel : ${payload.email || '—'}`,
      `Téléphone : ${payload.phone || '—'}`,
      `Institution : ${payload.institution || '—'}`,
      ``,
      `Étape 1 complétée — redirection vers Sterling Backcheck à effectuer.`,
    ].join('\n');
    try {
      openMailtoFallback({
        to: STUDENT_ADMIN_EMAIL,
        cc: payload.email,
        subject: `Nouvelle demande d'inscription Étudiant - ${displayName}`,
        body,
      });
      return { ok: true, mode: 'mailto' };
    } catch (mailErr) {
      return { ok: false, mode: 'none', error: mailErr };
    }
  }
}

/**
 * Écran de confirmation après une soumission réussie.
 * Réutilisable famille / étudiant. Le composant parent décide du contexte et des actions.
 *
 * Props attendues :
 *   - kind: 'family' | 'student'
 *   - mode: 'server' | 'mailto'
 *   - summaryItems: [{ label, value }]
 *   - primaryAction: { label, onClick } (bouton principal)
 *   - secondaryAction?: { label, onClick } (optionnel)
 *   - notice?: string (message supplémentaire — ex. consigne Sterling)
 */
const BookingSuccessScreen = ({
  kind,
  mode,
  summaryItems = [],
  primaryAction,
  secondaryAction,
  notice,
}) => {
  const title = kind === 'student'
    ? "Demande d'inscription reçue"
    : "Demande reçue";
  const subline = kind === 'student'
    ? "Notre équipe a bien reçu votre dossier. Vous serez recontacté(e) dès la fin de la vérification Sterling Backcheck."
    : "Notre équipe vous recontacte sous 24 h ouvrables pour confirmer votre première visite.";

  return (
    <section style={{ padding: '48px 0 80px' }}>
      <div className="container" style={{ maxWidth: 720 }}>
        <div
          style={{
            background: 'var(--surface)',
            border: '1px solid var(--line)',
            borderRadius: 20,
            padding: '40px 36px',
            boxShadow: '0 8px 30px rgba(15,28,22,0.06)',
            textAlign: 'center',
          }}
        >
          <div
            aria-hidden="true"
            style={{
              width: 72, height: 72, borderRadius: '50%',
              background: 'var(--primary-soft)',
              color: 'var(--primary)',
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              marginBottom: 20,
            }}
          >
            <Icon name="check" size={36} />
          </div>

          <div className="eyebrow" style={{ color: 'var(--ink-3)', marginBottom: 8 }}>
            Confirmation
          </div>
          <h2 style={{ fontFamily: 'var(--font-display)', fontSize: 30, lineHeight: 1.2, margin: '0 0 14px' }}>
            {title}
          </h2>
          <p style={{ color: 'var(--ink-2)', fontSize: 16, lineHeight: 1.55, margin: '0 auto', maxWidth: 540 }}>
            {subline}
          </p>

          {notice ? (
            <div
              style={{
                marginTop: 22,
                padding: '14px 16px',
                borderRadius: 12,
                background: 'var(--accent-soft)',
                border: '1px solid var(--line)',
                color: 'var(--ink)',
                fontSize: 14,
                lineHeight: 1.55,
                textAlign: 'left',
              }}
            >
              <strong style={{ display: 'block', marginBottom: 4 }}>À retenir</strong>
              {notice}
            </div>
          ) : null}

          {summaryItems.length > 0 ? (
            <div
              style={{
                marginTop: 28,
                padding: '20px 22px',
                borderRadius: 12,
                background: 'var(--bg-2)',
                border: '1px solid var(--line)',
                textAlign: 'left',
              }}
            >
              <div className="eyebrow" style={{ color: 'var(--ink-3)', marginBottom: 10 }}>
                Récapitulatif
              </div>
              <dl style={{ margin: 0, display: 'grid', gridTemplateColumns: '160px 1fr', rowGap: 8, columnGap: 14, fontSize: 14 }}>
                {summaryItems.map((item, idx) => (
                  <React.Fragment key={idx}>
                    <dt style={{ color: 'var(--ink-3)' }}>{item.label}</dt>
                    <dd style={{ margin: 0, color: 'var(--ink)' }}>{item.value || '—'}</dd>
                  </React.Fragment>
                ))}
              </dl>
            </div>
          ) : null}

          <div
            className="row"
            style={{ justifyContent: 'center', gap: 12, marginTop: 28, flexWrap: 'wrap' }}
          >
            {secondaryAction ? (
              <button
                type="button"
                className="btn btn-ghost"
                onClick={secondaryAction.onClick}
                style={{ minHeight: 44, padding: '0 18px' }}
              >
                {secondaryAction.label}
              </button>
            ) : null}
            {primaryAction ? (
              <button
                type="button"
                className="btn btn-primary"
                onClick={primaryAction.onClick}
                style={{ minHeight: 44, padding: '0 22px' }}
              >
                {primaryAction.label} <Icon name="arrow-right" size={16} />
              </button>
            ) : null}
          </div>

          <p
            style={{
              marginTop: 22,
              fontSize: 12,
              color: 'var(--ink-3)',
              lineHeight: 1.5,
            }}
          >
            {mode === 'mailto'
              ? "Votre client mail s'est ouvert pour envoyer la demande à l'équipe Maison Sereine — pensez à cliquer « Envoyer » dans la fenêtre qui vient d'apparaître."
              : "Une copie de votre demande a été transmise à contact@maisonsereine.ca."}
          </p>
        </div>
      </div>
    </section>
  );
};

const ADHESION_FEE_TOOLTIP_TEXT =
  "Ces frais couvrent l'ouverture de votre dossier et la vérification rigoureuse des antécédents judiciaires et académiques de l'étudiant via Sterling Backcheck.";

/** Info-bulle (i) — frais d'adhésion / dossier (récap paiement & colonne latérale) */
const AdhesionFeeInfoButton = () => {
  const [open, setOpen] = React.useState(false);
  return (
    <span
      style={{ position: 'relative', display: 'inline-flex', alignItems: 'center', marginLeft: 6, verticalAlign: 'middle' }}
      onMouseEnter={() => setOpen(true)}
      onMouseLeave={() => setOpen(false)}
    >
      <button
        type="button"
        aria-label="Information sur les frais d'adhésion"
        aria-expanded={open}
        style={{
          border: 'none', background: 'transparent', color: 'var(--ink-3)', cursor: 'help',
          padding: 4, borderRadius: 8, display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          lineHeight: 0,
        }}
        onFocus={() => setOpen(true)}
        onBlur={() => setOpen(false)}
      >
        <Icon name="info" size={15} />
      </button>
      {open && (
        <span
          role="tooltip"
          style={{
            position: 'absolute', left: '50%', bottom: 'calc(100% + 10px)', transform: 'translateX(-50%)',
            zIndex: 40, width: 280, maxWidth: 'min(280px, 85vw)', padding: '12px 14px', borderRadius: 10,
            background: 'var(--ink)', color: '#fff', fontSize: 12, lineHeight: 1.5, fontWeight: 400,
            boxShadow: 'var(--shadow-2)', textAlign: 'left', pointerEvents: 'none',
          }}
        >
          {ADHESION_FEE_TOOLTIP_TEXT}
        </span>
      )}
    </span>
  );
};

const BookingPage = () => {
  const [role, setRole] = React.useState(() => location.hash.includes('student') ? 'student' : 'family');
  React.useEffect(() => {
    const sync = () => setRole(location.hash.includes('student') ? 'student' : 'family');
    window.addEventListener('hashchange', sync); return () => window.removeEventListener('hashchange', sync);
  }, []);
  return (
    <>
      <NavBar current="booking" />
      <section style={{ padding: '28px 0 0' }}>
        <div className="container" style={{ maxWidth: 1060 }}>
          <div className="row" style={{ gap: 6, background: 'var(--bg-2)', padding: 6, borderRadius: 999, width: 'fit-content', border: '1px solid var(--line)' }}>
            {[['family', "Je cherche de l’aide"], ['student', "Je suis étudiant"]].map(([r, l]) => {
              const on = role === r;
              return (
                <button key={r} onClick={() => { location.hash = r === 'student' ? 'booking?role=student' : 'booking'; setRole(r); }}
                  style={{
                    padding: '10px 20px', borderRadius: 999, border: 0,
                    background: on ? 'var(--primary)' : 'transparent',
                    color: on ? '#fff' : 'var(--ink-2)',
                    fontSize: 14, fontWeight: 500, cursor: 'pointer'
                  }}>{l}</button>
              );
            })}
          </div>
        </div>
      </section>
      {role === 'family' ? <FamilyFlow /> : <StudentFlow />}
      <Footer />
    </>
  );
};

/* =========================================================
   FAMILY FLOW — 5 steps
   ========================================================= */
const FamilyFlow = () => {
  const [step, setStep] = React.useState(0);
  const [loi25Consent, setLoi25Consent] = React.useState(false);
  /** État de soumission du formulaire de réservation (Phase « Confirmer la demande »). */
  const [submission, setSubmission] = React.useState({ status: 'idle', mode: null, snapshot: null });
  const [data, setData] = React.useState({
    tier: 'aide',
    frequency: 'hebdo',
    hours: 2,
    daysOn: [0, 3],
    slot: '10:00',
    for: "",
    age: "",
    address: "",
    notes: "",
    relation: "",
    contact: { name: "", phone: "", email: "" },
  });

  const steps = [
    "Niveau & tarification",
    "Bénéficiaire",
    "Paiement & facturation",
    "Confirmation",
    "Étudiant matché"
  ];
  const total = steps.length;
  const goNext = () => setStep(s => Math.min(total - 1, s + 1));
  const goPrev = () => setStep(s => Math.max(0, s - 1));

  const tier = bookingTiers.find(t => t.id === data.tier);
  const perVisit = tier.net * data.hours;
  const perWeek = perVisit * (data.daysOn.length || 1);
  const monthly = Math.round(perWeek * 4.33);
  const grossMonthly = Math.round(tier.gross * data.hours * (data.daysOn.length || 1) * 4.33);
  /* Total brut 1re période = mensualité services + 45 $ (FRAIS_ADHESION_DOSSIER) */
  const grossPremierMoisAvecAdhesion = grossMonthly + FRAIS_ADHESION_DOSSIER;
  const coutEffectifPremierMois = monthly + FRAIS_ADHESION_DOSSIER;

  const toggleDay = (i) => setData(d => ({ ...d, daysOn: d.daysOn.includes(i) ? d.daysOn.filter(x => x !== i) : [...d.daysOn, i] }));

  /** Déclenche l'appel à /api/send-email puis met à jour l'état pour afficher la page de confirmation. */
  const submitBooking = async () => {
    if (submission.status === 'pending') return;
    const snapshot = {
      tierLabel: tier.label,
      frequencyLabel: frequencies.find((f) => f.id === data.frequency)?.t,
      grossPremierMoisAvecAdhesion,
      grossMonthly,
      coutEffectifPremierMois,
      daysLabels: data.daysOn.length ? data.daysOn.map((i) => days[i]) : [],
      slot: data.slot,
      forName: data.for,
      contactName: data.contact && data.contact.name,
      contactEmail: data.contact && data.contact.email,
      contactPhone: data.contact && data.contact.phone,
    };
    setSubmission({ status: 'pending', mode: null, snapshot });
    const result = await handleEmailNotification({
      data,
      tierLabel: tier.label,
      grossPremierMoisAvecAdhesion,
      grossMonthly,
      frequencyLabel: snapshot.frequencyLabel,
      consentLoi25: loi25Consent,
    });
    if (result && result.ok) {
      setSubmission({ status: 'success', mode: result.mode, snapshot });
    } else {
      setSubmission({ status: 'error', mode: null, snapshot, error: result && result.error });
    }
  };

  // Page de confirmation : prend toute la place une fois la demande envoyée.
  if (submission.status === 'success') {
    const snap = submission.snapshot || {};
    const summaryItems = [
      { label: 'Niveau de soin', value: snap.tierLabel },
      { label: 'Pour', value: snap.forName },
      { label: 'Fréquence', value: snap.frequencyLabel },
      { label: 'Jours · créneau', value: `${snap.daysLabels && snap.daysLabels.length ? snap.daysLabels.join(' · ') : '—'} · ${snap.slot || '—'}` },
      { label: 'Contact', value: snap.contactName },
      { label: 'Courriel', value: snap.contactEmail },
      { label: 'Téléphone', value: snap.contactPhone },
      { label: '1re période (services + frais)', value: snap.grossPremierMoisAvecAdhesion != null ? `${snap.grossPremierMoisAvecAdhesion} $` : null },
    ];
    return (
      <BookingSuccessScreen
        kind="family"
        mode={submission.mode}
        summaryItems={summaryItems}
        notice="Aucun paiement n'est encore prélevé. La facturation Stripe interviendra uniquement après la confirmation de votre première visite par notre équipe."
        primaryAction={{
          label: 'Accéder à mon espace famille',
          onClick: () => { window.location.hash = '#dashboard'; },
        }}
        secondaryAction={{
          label: 'Faire une autre demande',
          onClick: () => {
            setSubmission({ status: 'idle', mode: null, snapshot: null });
            setStep(0);
            setLoi25Consent(false);
          },
        }}
      />
    );
  }

  return (
    <section style={{ padding: '32px 0 60px' }}>
      <div className="container" style={{ maxWidth: 1060 }}>
        {/* Progress */}
        <div className="row" style={{ gap: 6, margin: '28px 0 18px', flexWrap: 'wrap' }}>
          {steps.map((s, i) => (
            <div key={i} style={{ flex: '1 1 110px', minWidth: 100 }}>
              <div style={{ height: 4, borderRadius: 2, background: i <= step ? 'var(--accent)' : 'var(--line)', transition: 'background .3s' }} />
              <div style={{ fontSize: 11, color: i === step ? 'var(--ink)' : 'var(--ink-3)', marginTop: 6, fontWeight: i === step ? 600 : 400 }}>
                {String(i+1).padStart(2, '0')} · {s}
              </div>
            </div>
          ))}
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: '1.5fr 1fr', gap: 40 }}>
          <div>
            {step === 0 && <StepPodium data={data} setData={setData} />}
            {step === 1 && <StepBeneficiary data={data} setData={setData} toggleDay={toggleDay} />}
            {step === 2 && (
              <StepPayment
                data={data}
                setData={setData}
                monthly={monthly}
                grossMonthly={grossMonthly}
                grossPremierMoisAvecAdhesion={grossPremierMoisAvecAdhesion}
                coutEffectifPremierMois={coutEffectifPremierMois}
              />
            )}
            {step === 3 && (
              <StepConfirmed
                data={data}
                tier={tier}
                grossMonthly={grossMonthly}
                grossPremierMoisAvecAdhesion={grossPremierMoisAvecAdhesion}
                coutEffectifPremierMois={coutEffectifPremierMois}
              />
            )}
            {step === 4 && <StepMatched />}

            {step === 2 && (
              <div
                role="group"
                aria-labelledby="loi25-consent-label"
                style={{
                  marginTop: 28,
                  padding: '16px 18px',
                  borderRadius: 12,
                  border: '1px solid var(--line)',
                  background: 'var(--surface)',
                  fontSize: 14,
                  lineHeight: 1.5,
                  color: 'var(--ink-2)',
                }}
              >
                <label id="loi25-consent-label" style={{ display: 'flex', gap: 12, alignItems: 'flex-start', cursor: 'pointer', margin: 0 }}>
                  <input
                    type="checkbox"
                    checked={loi25Consent}
                    onChange={(e) => setLoi25Consent(e.target.checked)}
                    style={{ marginTop: 3, width: 18, height: 18, flexShrink: 0, accentColor: 'var(--primary)' }}
                    aria-required="true"
                  />
                  <span>
                    Je consens à la collecte, à l’utilisation et à la communication de mes renseignements personnels
                    conformément à la <strong>Loi 25</strong> (Québec) et à la{' '}
                    <a href="#privacy" style={{ color: 'var(--primary)', fontWeight: 600 }}>politique de confidentialité</a>{' '}
                    de Maison Sereine, dans le seul but de traiter ma demande de services.
                  </span>
                </label>
              </div>
            )}

            <div className="row" style={{ justifyContent: 'space-between', marginTop: 40 }}>
              <button className="btn btn-ghost" onClick={goPrev} disabled={step === 0} style={{ opacity: step === 0 ? 0.4 : 1 }}>
                <Icon name="arrow-left" size={16} /> Précédent
              </button>
              {step < total - 1 ? (
                <button
                  className="btn btn-primary"
                  type="button"
                  disabled={
                    (step === 2 && !loi25Consent) ||
                    submission.status === 'pending'
                  }
                  style={{
                    opacity:
                      (step === 2 && !loi25Consent) || submission.status === 'pending'
                        ? 0.55
                        : 1,
                    cursor: submission.status === 'pending' ? 'progress' : undefined,
                  }}
                  data-ms-premiere-facturation-brut={grossPremierMoisAvecAdhesion}
                  data-ms-premiere-facturation-centimes={Math.round(grossPremierMoisAvecAdhesion * 100)}
                  onClick={() => {
                    if (step === 2) {
                      submitBooking();
                    } else {
                      goNext();
                    }
                  }}
                >
                  {step === 2 && submission.status === 'pending'
                    ? 'Envoi en cours…'
                    : step === 2
                      ? 'Confirmer la demande'
                      : 'Continuer'}
                  {submission.status !== 'pending' && <Icon name="arrow-right" size={16} />}
                </button>
              ) : (
                <a href="#dashboard" className="btn btn-dark">
                  Accéder à l’espace famille <Icon name="arrow-right" size={16} />
                </a>
              )}
              {step === 2 && submission.status === 'error' ? (
                <div
                  role="alert"
                  style={{
                    marginTop: 14,
                    padding: '12px 14px',
                    borderRadius: 10,
                    background: '#FBECE7',
                    border: '1px solid #F1B7A6',
                    color: '#7A2412',
                    fontSize: 13,
                    lineHeight: 1.5,
                    width: '100%',
                  }}
                >
                  Une erreur est survenue lors de l'envoi. Réessayez dans un instant ou
                  contactez-nous directement à <strong>contact@maisonsereine.ca</strong>.
                </div>
              ) : null}
            </div>
          </div>

          <aside style={{ background: 'var(--surface)', border: '1px solid var(--line)', borderRadius: 16, padding: 28, alignSelf: 'start', position: 'sticky', top: 100 }}>
            <div style={{ fontFamily: 'var(--font-display)', fontSize: 22, marginBottom: 4 }}>Récapitulatif</div>
            <div style={{ color: 'var(--ink-3)', fontSize: 13, marginBottom: 20 }}>Mis à jour en temps réel</div>

            <SummaryRow label="Niveau">{tier.label}</SummaryRow>
            <SummaryRow label="Fréquence">{frequencies.find(f => f.id === data.frequency)?.t}</SummaryRow>
            <SummaryRow label="Heures / visite">{data.hours} h</SummaryRow>
            <SummaryRow label="Jours">{data.daysOn.length ? data.daysOn.map(i => days[i]).join(' · ') : '—'}</SummaryRow>
            <SummaryRow label="Créneau">{data.slot}</SummaryRow>
            <SummaryRow label="Pour">{data.for}</SummaryRow>
            <div className="row" style={{ justifyContent: 'space-between', alignItems: 'center', padding: '10px 0', fontSize: 14, borderBottom: '1px dashed var(--line)' }}>
              <div className="row" style={{ alignItems: 'center', gap: 4, flexWrap: 'wrap' }}>
                <span style={{ color: 'var(--ink)', fontWeight: 500 }}>Frais de dossier : {FRAIS_ADHESION_DOSSIER} $</span>
                <AdhesionFeeInfoButton />
              </div>
            </div>

            <hr className="hr" style={{ margin: '20px 0' }} />
            <div style={{ display: 'grid', gap: 10 }}>
              <div className="row" style={{ justifyContent: 'space-between', alignItems: 'baseline' }}>
                <span style={{ fontSize: 15 }}>Tarif facturé / mois (services)</span>
                <span style={{ fontFamily: 'var(--font-display)', fontSize: 28, color: 'var(--primary)' }}>{grossMonthly} $</span>
              </div>
              <div className="row" style={{ justifyContent: 'space-between', alignItems: 'baseline' }}>
                <span style={{ fontSize: 15, fontWeight: 600 }}>Total tarif facturé (1re période)</span>
                <span style={{ fontFamily: 'var(--font-display)', fontSize: 32, color: 'var(--primary)' }}>{grossPremierMoisAvecAdhesion} $</span>
              </div>
              <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 2, lineHeight: 1.45 }}>
                Les frais d'adhésion couvrent la vérification de sécurité rigoureuse des étudiants.
              </div>
              <div className="row" style={{ justifyContent: 'space-between', color: 'var(--ink-3)', fontSize: 13, marginTop: 6 }}>
                <span>Crédit d'impôt CMD estimé (40 %, services)</span><span>–{grossMonthly - monthly} $</span>
              </div>
              <div className="row" style={{ justifyContent: 'space-between', color: 'var(--ink-3)', fontSize: 13 }}>
                <span>Coût effectif 1re période (après CMD)</span><span>{coutEffectifPremierMois} $</span>
              </div>
              <div style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 6 }}>Facture détaillée remise après chaque visite — à conserver pour l'annexe J</div>
            </div>

            <div style={{ marginTop: 20, padding: 14, background: 'var(--bg-2)', borderRadius: 10, fontSize: 13, color: 'var(--ink-2)' }}>
              <div className="row" style={{ gap: 8 }}>
                <Icon name="shield" size={14} style={{ color: 'var(--primary)' }} />
                Sans contrat à long terme · annulation gratuite 48 h avant
              </div>
            </div>
          </aside>
        </div>
      </div>
    </section>
  );
};

const SummaryRow = ({ label, children }) => (
  <div style={{ padding: '10px 0', fontSize: 14, borderBottom: '1px dashed var(--line)', display: 'flex', flexDirection: 'column', gap: 2 }}>
    <span style={{ color: 'var(--ink-3)', fontSize: 12, textTransform: 'uppercase', letterSpacing: '.06em' }}>{label}</span>
    <span style={{ color: 'var(--ink)', fontWeight: 500, wordBreak: 'break-word' }}>{children || '—'}</span>
  </div>
);

const Field = ({ label, children, note }) => (
  <label style={{ display: 'block', marginBottom: 20 }}>
    <div style={{ fontSize: 14, color: 'var(--ink-2)', marginBottom: 6, fontWeight: 500 }}>{label}</div>
    {children}
    {note && <div style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 4 }}>{note}</div>}
  </label>
);

const inputStyle = {
  width: '100%', padding: '14px 16px',
  border: '1px solid var(--line-2)', borderRadius: 10,
  background: 'var(--surface)', fontFamily: 'inherit', fontSize: 16,
  color: 'var(--ink)', outline: 'none'
};

/* ---------- Step 1 — Podium ---------- */
const StepPodium = ({ data, setData }) => (
  <div>
    <div className="eyebrow">Étape 01 · Tarification</div>
    <h2 style={{ marginTop: 10, fontSize: 36 }}>Quel niveau d’accompagnement ?</h2>
    <p style={{ color: 'var(--ink-2)', marginTop: 10, marginBottom: 24 }}>
      Voici le tarif facturé par heure. Vous le payez en entier ; une facture détaillée vous est
      remise pour l'annexe J afin de récupérer jusqu'à 40 % via le crédit d'impôt CMD.
    </p>

    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12, alignItems: 'end' }}>
      {bookingTiers.map(t => {
        const active = data.tier === t.id;
        const podiumHeight = t.id === 'aide' ? 240 : (t.id === 'pab' ? 210 : 200);
        return (
          <button key={t.id} onClick={() => setData(d => ({...d, tier: t.id}))}
            style={{
              textAlign: 'left', padding: 18, borderRadius: 14,
              background: active ? 'var(--primary)' : 'var(--surface)',
              color: active ? '#fff' : 'var(--ink)',
              border: '2px solid ' + (active ? 'var(--primary)' : 'var(--line)'),
              display: 'flex', flexDirection: 'column', minHeight: podiumHeight,
              cursor: 'pointer', font: 'inherit'
            }}>
            {t.id === 'pab' && (
              <span className="chip" style={{ alignSelf: 'flex-start', background: 'var(--accent-soft)', color: 'var(--accent-2)', borderColor: 'var(--accent-soft)', fontSize: 11 }}>
                <Icon name="shield" size={12} /> Préposé diplômé
              </span>
            )}
            <div style={{ fontFamily: 'var(--font-display)', fontSize: 20, marginTop: 8, lineHeight: 1.2 }}>{t.label}</div>
            <div style={{ flex: 1 }} />
            <div style={{ fontSize: 12, opacity: 0.7 }}>Coût effectif estimé {t.net.toFixed(2).replace('.', ',')} $/h après crédit d'impôt</div>
            <div style={{ fontFamily: 'var(--font-display)', fontSize: 28, marginTop: 4 }}>
              {t.gross.toFixed(2).replace('.', ',')} $<span style={{ fontSize: 14, opacity: 0.7 }}> / h facturé</span>
            </div>
          </button>
        );
      })}
    </div>

    <div style={{ marginTop: 28, padding: 16, background: 'var(--bg-2)', border: '1px solid var(--line)', borderRadius: 12, fontSize: 14, color: 'var(--ink-2)' }}>
      <div className="row" style={{ gap: 10 }}>
        <Icon name="sparkle" size={16} style={{ color: 'var(--accent)' }} />
        <strong>Tarif facturé & crédit d'impôt.</strong>
        &nbsp;Vous payez la totalité à chaque facture. Une facture détaillée vous est remise pour vous permettre de récupérer jusqu'à 40 % via le crédit d'impôt CMD (Revenu Québec).
      </div>
    </div>
  </div>
);

/* ---------- Step 2 — Beneficiary (with 70+ validation) ---------- */
const StepBeneficiary = ({ data, setData, toggleDay }) => {
  const age = parseInt(data.age) || 0;
  const eligible = age >= 70;
  return (
    <div>
      <div className="eyebrow">Étape 02 · Bénéficiaire</div>
      <h2 style={{ marginTop: 10, fontSize: 36 }}>Pour qui organisez-vous ?</h2>
      <p style={{ color: 'var(--ink-2)', marginTop: 10, marginBottom: 24 }}>
        Le crédit d'impôt pour maintien à domicile des aînés (CMD) est réservé aux personnes de 70 ans ou plus résidant au Québec. Le taux de remboursement est réduit au-delà de 72 465 $ de revenu familial (2026).
      </p>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
        <Field label="La personne aidée">
          <input style={inputStyle} value={data.for} onChange={e => setData(d => ({...d, for: e.target.value}))} />
        </Field>
        <Field label="Son âge" note={eligible ? "✓ Admissible au crédit d'impôt CMD (sous conditions de revenu)" : "⚠ CMD réservé aux 70 ans ou plus"}>
          <input style={{
            ...inputStyle,
            borderColor: age === 0 ? 'var(--line-2)' : (eligible ? 'var(--good)' : 'var(--bad)'),
          }} value={data.age} onChange={e => setData(d => ({...d, age: e.target.value}))} />
        </Field>
      </div>

      <Field label="Adresse (Québec)">
        <input style={inputStyle} value={data.address} onChange={e => setData(d => ({...d, address: e.target.value}))} />
      </Field>

      <Field label="Notes — situation, habitudes, préférences" note="Aide le coordonnateur à choisir le bon étudiant.">
        <textarea rows={3} style={{...inputStyle, resize: 'vertical'}}
          value={data.notes} onChange={e => setData(d => ({...d, notes: e.target.value}))} />
      </Field>

      <Field label="Fréquence">
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12 }}>
          {frequencies.map(f => {
            const active = data.frequency === f.id;
            return (
              <button key={f.id} onClick={() => setData(d => ({...d, frequency: f.id}))}
                style={{
                  textAlign: 'left', padding: 14, borderRadius: 12,
                  background: active ? 'var(--primary-soft)' : 'var(--surface)',
                  border: '1px solid ' + (active ? 'var(--primary)' : 'var(--line)'), cursor: 'pointer', font: 'inherit'
                }}>
                <div style={{ fontWeight: 500 }}>{f.t}</div>
                <div style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 4 }}>{f.d}</div>
              </button>
            );
          })}
        </div>
      </Field>

      <Field label="Jours & créneau">
        <div className="row" style={{ gap: 8, flexWrap: 'wrap' }}>
          {days.map((d, i) => {
            const on = data.daysOn.includes(i);
            return (
              <button key={i} onClick={() => toggleDay(i)}
                style={{
                  width: 52, height: 52, borderRadius: '50%',
                  border: '1px solid ' + (on ? 'var(--primary)' : 'var(--line-2)'),
                  background: on ? 'var(--primary)' : 'var(--surface)',
                  color: on ? '#fff' : 'var(--ink-2)', fontWeight: 500, fontSize: 15, cursor: 'pointer'
                }}>{d}</button>
            );
          })}
        </div>
        <div className="row" style={{ gap: 6, flexWrap: 'wrap', marginTop: 12 }}>
          {slots.map(s => {
            const on = data.slot === s;
            return (
              <button key={s} onClick={() => setData(d => ({...d, slot: s}))} className="chip"
                style={{
                  cursor: 'pointer',
                  background: on ? 'var(--primary)' : 'var(--bg-2)',
                  color: on ? '#fff' : 'var(--ink-2)',
                  border: '1px solid ' + (on ? 'var(--primary)' : 'var(--line)'),
                  padding: '8px 14px', fontSize: 14,
                }}>{s}</button>
            );
          })}
        </div>
      </Field>

      <Field label="Heures par visite">
        <div className="row" style={{ gap: 14 }}>
          <button onClick={() => setData(d => ({...d, hours: Math.max(1, d.hours - 1)}))} style={{ width: 44, height: 44, borderRadius: '50%', border: '1px solid var(--line-2)', background: 'var(--surface)', cursor: 'pointer' }}><Icon name="minus" size={16} /></button>
          <div style={{ fontFamily: 'var(--font-display)', fontSize: 32, minWidth: 76, textAlign: 'center' }}>{data.hours} h</div>
          <button onClick={() => setData(d => ({...d, hours: Math.min(12, d.hours + 1)}))} style={{ width: 44, height: 44, borderRadius: '50%', border: '1px solid var(--line-2)', background: 'var(--surface)', cursor: 'pointer' }}><Icon name="plus" size={16} /></button>
        </div>
      </Field>
    </div>
  );
};

/* ---------- Step 3 — Payment + invoice ---------- */
const StepPayment = ({ data, setData, monthly, grossMonthly, grossPremierMoisAvecAdhesion, coutEffectifPremierMois }) => (
  <div>
    {/* Stripe / Checkout : utiliser grossPremierMoisAvecAdhesion (ou line_items services + frais dossier) pour le 1er paiement. */}
    <div className="eyebrow">Étape 03 · Paiement</div>
    <h2 style={{ marginTop: 10, fontSize: 36 }}>Paiement & facturation</h2>
    <p style={{ color: 'var(--ink-2)', marginTop: 10, marginBottom: 24 }}>
      Vous êtes facturé du montant total à chaque visite. Une <strong>facture détaillée</strong> est transmise automatiquement — à conserver pour joindre à l'<strong>annexe J</strong> de votre déclaration de revenus et récupérer jusqu'à 40 % via le crédit d'impôt CMD.
    </p>

    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
      <Field label="Votre nom"><input style={inputStyle} value={data.contact.name} onChange={e => setData(d => ({...d, contact: {...d.contact, name: e.target.value}}))} /></Field>
      <Field label="Lien avec la personne aidée">
        <select style={inputStyle} value={data.relation} onChange={e => setData(d => ({ ...d, relation: e.target.value }))}>
          <option value="">Choisir…</option>
          {['Enfant', 'Conjoint·e', 'Petit-enfant', 'Ami·e', 'La personne elle-même', 'Autre'].map(o => (
            <option key={o} value={o}>{o}</option>
          ))}
        </select>
      </Field>
    </div>
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
      <Field label="Téléphone"><input style={inputStyle} value={data.contact.phone} onChange={e => setData(d => ({...d, contact: {...d.contact, phone: e.target.value}}))} /></Field>
      <Field label="Courriel"><input style={inputStyle} value={data.contact.email} onChange={e => setData(d => ({...d, contact: {...d.contact, email: e.target.value}}))} /></Field>
    </div>

    <Field label="Mode de paiement">
      <div style={{
        padding: '28px 20px',
        border: '2px dashed var(--line-2)',
        borderRadius: 12,
        background: 'var(--bg-2)',
        textAlign: 'center',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        gap: 14,
      }}>
        <p style={{ margin: 0, fontSize: 14, color: 'var(--ink-3)', maxWidth: 400, lineHeight: 1.5 }}>
          Aucune méthode de paiement enregistrée. Vous pourrez en ajouter une lors de la finalisation avec la coordination.
        </p>
        <button type="button" className="btn btn-primary" style={{ marginTop: 2 }}>
          <Icon name="plus" size={16} /> Ajouter une méthode de paiement
        </button>
      </div>
    </Field>

    <div style={{ padding: 20, background: 'var(--bg-2)', borderRadius: 12, border: '1px solid var(--line)', marginTop: 8 }}>
      <div style={{ fontFamily: 'var(--font-display)', fontSize: 18, marginBottom: 10 }}>Récapitulatif mensuel</div>
      <div style={{ display: 'grid', gap: 8, fontSize: 14 }}>
        <div className="row" style={{ justifyContent: 'space-between' }}>
          <strong>Tarif facturé (estimé mensuel, services)</strong><strong style={{ fontFamily: 'var(--font-mono)' }}>{grossMonthly.toFixed(2)} $</strong>
        </div>
        <div className="row" style={{ justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8 }}>
          <div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 6, minWidth: 0 }}>
            <span style={{ fontWeight: 600 }}>Frais d'adhésion ({FRAIS_ADHESION_DOSSIER} $)</span>
            <AdhesionFeeInfoButton />
            <div style={{ width: '100%', fontSize: 12, color: 'var(--ink-3)', fontStyle: 'italic', lineHeight: 1.45 }}>
              Frais uniques prélevés lors de la première réservation pour la vérification de sécurité des étudiants.
            </div>
          </div>
        </div>
        <hr className="hr" />
        <div className="row" style={{ justifyContent: 'space-between' }}>
          <strong>Total tarif facturé (1re période)</strong><strong style={{ fontFamily: 'var(--font-mono)', color: 'var(--primary)' }}>{grossPremierMoisAvecAdhesion.toFixed(2)} $</strong>
        </div>
        <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 4, lineHeight: 1.45 }}>
          Les frais d'adhésion couvrent la vérification de sécurité rigoureuse des étudiants.
        </div>
        <div className="row" style={{ justifyContent: 'space-between' }}>
          <span style={{ color: 'var(--ink-3)' }}>Crédit d'impôt CMD estimé (jusqu'à 40 %, services)</span><span style={{ fontFamily: 'var(--font-mono)', color: 'var(--good)' }}>–{(grossMonthly - monthly).toFixed(2)} $</span>
        </div>
        <div className="row" style={{ justifyContent: 'space-between' }}>
          <span style={{ color: 'var(--ink-3)' }}>Coût effectif 1re période (après CMD)</span><span style={{ fontFamily: 'var(--font-mono)' }}>{coutEffectifPremierMois.toFixed(2)} $</span>
        </div>
      </div>
      <div style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 12 }}>
        Facture détaillée transmise après chaque visite — à conserver pour l'<span style={{ fontFamily: 'var(--font-mono)' }}>annexe J</span> de votre déclaration de revenus.
      </div>
    </div>
  </div>
);

/* ---------- Step 4 — Confirmed ---------- */
const StepConfirmed = ({ data, tier, grossMonthly, grossPremierMoisAvecAdhesion, coutEffectifPremierMois }) => {
  const forStr = (data.for || '').toString().trim();
  const ageStr = (data.age || '').toString().trim();
  const benLine =
    forStr && ageStr ? `${forStr} · ${ageStr} ans` :
    forStr ? forStr :
    ageStr ? `${ageStr} ans` : '—';
  const addrLine = (data.address || '').trim() || '—';
  const planningLine = `${data.daysOn.map(i => days[i]).join(' · ')} à ${data.slot} · ${data.hours} h`;
  const confirmedRows = [
    { id: 'niveau', label: 'Niveau', value: tier.label },
    { id: 'benef', label: 'Bénéficiaire', value: benLine },
    { id: 'addr', label: 'Adresse', value: addrLine },
    { id: 'plan', label: 'Planning', value: planningLine },
    { id: 'tarif-serv', label: 'Tarif facturé (services / mois)', value: `${grossMonthly} $` },
    { id: 'frais', label: "Frais d'adhésion (unique)", value: `${FRAIS_ADHESION_DOSSIER} $` },
    { id: 'total-brut', label: 'Total tarif facturé (1re période)', value: `${grossPremierMoisAvecAdhesion} $` },
    { id: 'cout-net', label: 'Coût effectif 1re période (après CMD)', value: `${coutEffectifPremierMois} $` },
  ];
  return (
  <div>
    <div className="row" style={{ gap: 14, marginBottom: 16 }}>
      <div style={{ width: 52, height: 52, borderRadius: '50%', background: 'var(--primary-soft)', color: 'var(--primary)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <Icon name="check" size={22} />
      </div>
      <div>
        <div className="eyebrow">Demande confirmée</div>
        <h2 style={{ fontSize: 32, marginTop: 4 }}>
          {(data.contact.name || '').trim()
            ? `Merci ${(data.contact.name || '').trim().split(/\s+/)[0]} — on s'en occupe.`
            : 'Merci — on s\'en occupe.'}
        </h2>
      </div>
    </div>
    <p style={{ color: 'var(--ink-2)', fontSize: 17 }}>
      Un coordonnateur étudie votre demande. Vous recevez le profil d’un étudiant matché dans les
      <strong> 24 h ouvrables</strong>. Vous validez, puis démarrage sous 48 h.
    </p>

    <div style={{ background: 'var(--bg-2)', borderRadius: 16, padding: 24, marginTop: 22, border: '1px solid var(--line)' }}>
      <div style={{ fontFamily: 'var(--font-display)', fontSize: 20, marginBottom: 12 }}>Numéro de demande</div>
      <div style={{ fontFamily: 'var(--font-mono)', fontSize: 28, color: 'var(--primary)' }}>MZN-2026-XXXXX</div>
      {confirmedRows.map((row) => (
        <div key={row.id} className="row" style={{ justifyContent: 'space-between', padding: '10px 0', borderTop: '1px solid var(--line)', fontSize: 15, marginTop: 0 }}>
          <span style={{ color: 'var(--ink-3)' }}>{row.label}</span>
          <span style={{ fontWeight: 500, textAlign: 'right', maxWidth: '60%' }}>{row.value}</span>
        </div>
      ))}
    </div>
  </div>
  );
};

/* ---------- Step 5 — Student matched ---------- */
const StepMatched = () => {
  const [isVerified, setIsVerified] = React.useState(true);
  const [showTip, setShowTip] = React.useState(false);
  return (
  <div>
    <div className="eyebrow">Étape 05 · Profil matché</div>
    <h2 style={{ marginTop: 10, fontSize: 36 }}>Profil étudiant proposé</h2>
    <p style={{ color: 'var(--ink-2)', marginTop: 10, marginBottom: 24 }}>
      Exemple de mise en page : la coordination propose un étudiant vérifié dont le programme et le secteur correspondent à la demande.
    </p>

    <div style={{ background: 'var(--surface)', border: '1px solid var(--line)', borderRadius: 20, overflow: 'hidden' }}>
      <div style={{ display: 'grid', gridTemplateColumns: '200px 1fr', gap: 24, padding: 24 }}>
        <div className="ph" style={{ height: 200, borderRadius: 16, padding: 0, fontSize: 0 }} />
        <div>
          <div className="row" style={{ gap: 12, alignItems: 'center', flexWrap: 'wrap' }}>
            <div style={{ fontFamily: 'var(--font-display)', fontSize: 28 }}>Étudiant vérifié</div>
            {isVerified && (
              <span
                onMouseEnter={() => setShowTip(true)}
                onMouseLeave={() => setShowTip(false)}
                onFocus={() => setShowTip(true)}
                onBlur={() => setShowTip(false)}
                tabIndex={0}
                className="row"
                style={{
                  position: 'relative', gap: 6, padding: '4px 10px', borderRadius: 999,
                  background: 'var(--primary-soft)', color: 'var(--primary)',
                  border: '1px solid var(--primary)', fontSize: 12, fontWeight: 500, cursor: 'help'
                }}
              >
                <Icon name="shield" size={13} />
                Vérifié Sécurité
                {showTip && (
                  <span style={{
                    position: 'absolute', top: 'calc(100% + 8px)', left: 0, zIndex: 10,
                    width: 280, padding: '12px 14px', borderRadius: 10,
                    background: 'var(--ink)', color: '#fff', fontSize: 12, lineHeight: 1.5,
                    fontWeight: 400, boxShadow: '0 10px 30px rgba(0,0,0,0.18)'
                  }}>
                    Ce profil a passé avec succès une enquête de sécurité approfondie (secteur vulnérable) via Sterling Backcheck.
                  </span>
                )}
              </span>
            )}
          </div>
          <div style={{ color: 'var(--ink-3)', fontSize: 14, marginTop: 4 }}>Programme de santé · Université du Québec</div>
          <div className="row" style={{ gap: 6, marginTop: 10, flexWrap: 'wrap' }}>
            {['RCR à jour', 'Sensibilisation troubles cognitifs', 'Français · Anglais'].map(t => (
              <span key={t} className="chip">{t}</span>
            ))}
          </div>
          <p style={{ color: 'var(--ink-2)', fontSize: 14, marginTop: 14 }}>
            « Motivation liée à l’accompagnement des aînés, intérêt pour la continuité des soins et la relation de confiance avec les familles. »
          </p>
          <div className="row" style={{ gap: 8, marginTop: 18 }}>
            <button className="btn btn-primary">Accepter ce profil <Icon name="check" size={16} /></button>
            <button className="btn btn-ghost">Voir une autre proposition</button>
          </div>
        </div>
      </div>
    </div>
  </div>
  );
};

/* =========================================================
   STUDENT FLOW — mission feed
   ========================================================= */
const parsePay = (s) => parseFloat(String(s).replace(/[^\d,]/g, '').replace(',', '.')) || 0;
const parseHours = (s) => {
  const m = String(s).match(/(\d+)\s*h/);
  return m ? parseInt(m[1], 10) : 2;
};

const INCOME_PROFILES = {
  standard:      { label: "Standard / Boursier AFE", metric: "money", cap: 1500, softCap: null, netRate: 1.00, caption: "Plafond indicatif mensuel pour rester dans les conditions AFE." },
  international: { label: "Étudiant international (24 h max)", metric: "hours", cap: 24, softCap: null, netRate: 1.00, caption: "Maximum 24 h / semaine hors campus selon IRCC." },
  aideSociale:   { label: "Prestataire Aide sociale",         metric: "money", cap: 500, softCap: 200, netRate: 0.52, caption: "Les premiers 200 $ / mois sont exemptés — au-delà, tes prestations s'ajustent partiellement." },
};

const IncomePlanner = ({ earnedMonth, hoursWeek, pendingGain = 0, pendingHours = 0 }) => {
  const [profile, setProfile] = React.useState('standard');
  const cfg = INCOME_PROFILES[profile];
  const isHours = cfg.metric === 'hours';

  const current = isHours ? hoursWeek : earnedMonth;
  const projected = current + (isHours ? pendingHours : pendingGain);
  const pctCurrent = Math.min(100, (current / cfg.cap) * 100);
  const pctProjected = Math.min(100, (projected / cfg.cap) * 100);
  const inSoftZone = cfg.softCap && current > cfg.softCap;

  const baseRate = 27.36; // taux moyen (niveau Aide à la vie quotidienne) — utilisé pour les calculs internes
  // Fourchette de gain par heure selon la catégorie de mission (Répit → PAB)
  const RATE_MIN = 20.65; // Niveau 01 · Compagnie & répit (70 % de 29,50 $)
  const RATE_MAX = 27.36; // Niveau 02 · Aide à la vie quotidienne (70 % de 39,08 $)
  const netRangeMin = profile === 'aideSociale' && current >= cfg.softCap ? RATE_MIN * cfg.netRate : RATE_MIN;
  const netRangeMax = profile === 'aideSociale' && current >= cfg.softCap ? RATE_MAX * cfg.netRate : RATE_MAX;

  const coachLine = profile === 'aideSociale'
    ? (inSoftZone
        ? "Tu restes gagnante : chaque heure ajoute encore à ton budget total."
        : "Bravo — vous êtes dans la zone d'exemption complète ce mois-ci !")
    : profile === 'international'
      ? "Tu es bien en-dessous du plafond IRCC. Continue à ton rythme."
      : "Bravo — vous avez optimisé votre budget ce mois-ci !";

  const fillColor = inSoftZone ? 'var(--accent)' : 'var(--primary)';
  const projectedColor = inSoftZone ? 'rgba(201, 110, 74, 0.35)' : 'rgba(44, 84, 73, 0.35)';

  const unit = isHours ? 'h' : '$';
  const fmt = (v) => isHours ? `${v} h` : `${Math.round(v)} $`;

  return (
    <div style={{
      marginTop: 32, padding: 26, borderRadius: 20, background: 'var(--surface)',
      border: '1px solid var(--line)'
    }}>
      <div className="row" style={{ justifyContent: 'space-between', alignItems: 'flex-start', gap: 20, flexWrap: 'wrap', marginBottom: 18 }}>
        <div>
          <div className="eyebrow">Planificateur de revenus</div>
          <h3 style={{ fontSize: 22, marginTop: 6 }}>{coachLine}</h3>
        </div>
        <label style={{ display: 'grid', gap: 6, minWidth: 240 }}>
          <span style={{ fontSize: 12, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '.08em' }}>Ta situation</span>
          <select value={profile} onChange={(e) => setProfile(e.target.value)} style={inputStyle}>
            {Object.entries(INCOME_PROFILES).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
          </select>
        </label>
      </div>

      {/* Progress bar */}
      <div style={{ marginTop: 8 }}>
        <div className="row" style={{ justifyContent: 'space-between', fontSize: 13, color: 'var(--ink-2)', marginBottom: 8 }}>
          <span>
            <strong style={{ color: 'var(--ink)', fontSize: 16, fontFamily: 'var(--font-display)' }}>{fmt(current)}</strong>
            <span style={{ color: 'var(--ink-3)' }}> / {fmt(cfg.cap)} {isHours ? 'cette semaine' : 'ce mois'}</span>
          </span>
          {pendingGain > 0 || pendingHours > 0 ? (
            <span style={{ color: fillColor }}>
              + {fmt(isHours ? pendingHours : pendingGain)} si tu acceptes → {fmt(projected)}
            </span>
          ) : null}
        </div>
        <div style={{
          position: 'relative', height: 14, borderRadius: 999, background: 'var(--bg-2)',
          overflow: 'hidden', border: '1px solid var(--line)'
        }}>
          {/* Projected (ghost) bar */}
          <div style={{
            position: 'absolute', inset: 0, width: `${pctProjected}%`,
            background: projectedColor, transition: 'width .3s ease'
          }} />
          {/* Current bar */}
          <div style={{
            position: 'absolute', inset: 0, width: `${pctCurrent}%`,
            background: fillColor, transition: 'width .3s ease'
          }} />
          {/* Soft cap marker */}
          {cfg.softCap && (
            <div style={{
              position: 'absolute', top: -3, bottom: -3,
              left: `${(cfg.softCap / cfg.cap) * 100}%`,
              width: 2, background: 'var(--ink-2)'
            }} />
          )}
        </div>
        {cfg.softCap && (
          <div className="row" style={{ justifyContent: 'flex-start', marginTop: 6 }}>
            <div style={{
              fontSize: 11, color: 'var(--ink-3)',
              marginLeft: `calc(${(cfg.softCap / cfg.cap) * 100}% - 28px)`
            }}>
              seuil {cfg.softCap} $
            </div>
          </div>
        )}
        <p style={{ fontSize: 13, color: 'var(--ink-3)', marginTop: 10, maxWidth: 640 }}>{cfg.caption}</p>
      </div>

      {/* Optimization section */}
      <div style={{
        display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14, marginTop: 22
      }}>
        <div style={{ padding: 18, background: 'var(--bg-2)', borderRadius: 12, border: '1px solid var(--line)' }}>
          <div style={{ fontSize: 12, textTransform: 'uppercase', letterSpacing: '.08em', color: 'var(--ink-3)' }}>Gain net estimé</div>

          {/* Fourchette de revenus — variable selon la catégorie de mission */}
          <div style={{ fontFamily: 'var(--font-display)', fontSize: 26, marginTop: 6, color: 'var(--primary)', lineHeight: 1.15 }}>
            De <strong>{netRangeMin.toFixed(2).replace('.', ',')}&nbsp;$</strong>
            {' '}à <strong>{netRangeMax.toFixed(2).replace('.', ',')}&nbsp;$</strong>
            <span style={{ fontSize: 14, color: 'var(--ink-3)' }}> / h net</span>
          </div>

          {/* Barre min–max visualisant la zone de gain potentiel */}
          <div style={{ marginTop: 14 }}>
            <div style={{
              position: 'relative', height: 12, borderRadius: 999,
              background: 'var(--bg)', border: '1px solid var(--line)', overflow: 'hidden'
            }}>
              <div style={{
                position: 'absolute', top: 0, bottom: 0,
                left: `${(netRangeMin / 35) * 100}%`,
                right: `${100 - (netRangeMax / 35) * 100}%`,
                background: 'linear-gradient(90deg, var(--primary) 0%, var(--accent) 100%)',
                borderRadius: 999,
              }}/>
            </div>
            <div className="row" style={{
              justifyContent: 'space-between', marginTop: 4,
              fontSize: 10, color: 'var(--ink-3)', fontFamily: 'var(--font-mono)'
            }}>
              <span>0&nbsp;$</span>
              <span>Répit</span>
              <span>AVQ</span>
              <span>PAB</span>
              <span>35&nbsp;$</span>
            </div>
          </div>

          {/* Mention discrète sous le graphique */}
          <p style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 12, lineHeight: 1.5, fontStyle: 'italic' }}>
            Le taux horaire net varie selon votre expérience, vos formations et la complexité de la mission confiée.
          </p>

          {/* Précision contextuelle selon le profil sélectionné */}
          {profile === 'aideSociale' && (
            <p style={{ fontSize: 13, color: 'var(--ink-2)', marginTop: 8, lineHeight: 1.5 }}>
              {current < cfg.softCap
                ? <>Sous le seuil d'exemption&nbsp;: <strong>100&nbsp;%</strong> du tarif s'ajoute à ton budget.</>
                : <>Au-delà de {cfg.softCap}&nbsp;$, environ <strong>{Math.round(cfg.netRate * 100)}&nbsp;%</strong> s'ajoute après ajustement des prestations.</>}
            </p>
          )}
          {profile === 'international' && (
            <p style={{ fontSize: 13, color: 'var(--ink-2)', marginTop: 8, lineHeight: 1.5 }}>
              Tu conserves <strong>100&nbsp;%</strong> du tarif. Surveille ton plafond de 24&nbsp;h&nbsp;/&nbsp;semaine IRCC.
            </p>
          )}
          {profile === 'standard' && (
            <p style={{ fontSize: 13, color: 'var(--ink-2)', marginTop: 8, lineHeight: 1.5 }}>
              Tu conserves <strong>100&nbsp;%</strong> du tarif — aucun impact sur ton AFE.
            </p>
          )}
        </div>
        <div style={{ padding: 18, background: 'var(--primary-soft)', borderRadius: 12, border: '1px solid var(--primary-soft)' }}>
          <div className="row" style={{ gap: 8, color: 'var(--primary-2)' }}>
            <Icon name="sparkle" size={16} />
            <span style={{ fontSize: 12, textTransform: 'uppercase', letterSpacing: '.08em' }}>Conseil Maison Sereine</span>
          </div>
          <p style={{ fontSize: 14, color: 'var(--ink)', marginTop: 10, lineHeight: 1.5 }}>
            Maison Sereine favorise la continuité : travailler régulièrement avec la même famille augmente ton <strong>score de fiabilité</strong> et tes opportunités.
          </p>
        </div>
      </div>
    </div>
  );
};

/* =========================================================
   STUDENT SIGNUP — 2 étapes (académique + Sterling Backcheck)
   ========================================================= */
const StudentSignupFlow = ({ onComplete }) => {
  const [step, setStep] = React.useState(1);
  const [form, setForm] = React.useState({
    nom: '',
    prenom: '',
    email: '',
    phone: '',
    institutionId: 'ulaval',
  });
  /** État de soumission — notification admin envoyée à la fin de l'étape 1. */
  const [submission, setSubmission] = React.useState({ status: 'idle', mode: null });
  const [loi25Consent, setLoi25Consent] = React.useState(false);
  const adminNotifiedRef = React.useRef(false);

  const institutionLabel = STUDENT_INSTITUTIONS.find((i) => i.id === form.institutionId)?.label || '—';

  const step1Valid =
    form.nom.trim() &&
    form.prenom.trim() &&
    form.email.trim().includes('@') &&
    form.phone.trim();

  const studentPayload = () => ({
    nom: form.nom.trim(),
    prenom: form.prenom.trim(),
    email: form.email.trim(),
    phone: form.phone.trim(),
    institution: institutionLabel,
  });

  /**
   * Étape 1 → notifie contact@maisonsereine.ca que l'étudiant a complété ses infos académiques.
   * N'envoie qu'une seule fois (même si l'étudiant revient en arrière).
   */
  const notifyAdminStep1 = async () => {
    if (submission.status === 'pending') return false;
    if (adminNotifiedRef.current) return true;

    setSubmission({ status: 'pending', mode: null });
    const result = await notifyStudentAdmin({ ...studentPayload(), consentLoi25: loi25Consent });
    if (result && result.ok) {
      adminNotifiedRef.current = true;
      setSubmission({ status: 'notified', mode: result.mode });
      return true;
    }
    setSubmission({ status: 'error', mode: null, error: result && result.error });
    return false;
  };

  /** Bouton « Continuer » étape 1 : envoi courriel admin puis passage à Sterling. */
  const continueToStep2 = async () => {
    if (!step1Valid) return;
    const sent = await notifyAdminStep1();
    if (sent) setStep(2);
  };

  /** Bouton étape 2 : ouvre Sterling puis affiche la page de confirmation. */
  const startSterlingVerification = () => {
    window.open(STERLING_BACKCHECK_PORTAL_URL, '_blank', 'noopener,noreferrer');
    setSubmission((prev) => ({ ...prev, status: 'success' }));
  };

  // Page de confirmation : remplace l'écran d'inscription dès que l'envoi serveur réussit.
  if (submission.status === 'success') {
    const summaryItems = [
      { label: 'Prénom', value: form.prenom.trim() },
      { label: 'Nom', value: form.nom.trim() },
      { label: 'Institution', value: institutionLabel },
      { label: 'Courriel', value: form.email.trim() },
      { label: 'Téléphone', value: form.phone.trim() },
    ];
    return (
      <BookingSuccessScreen
        kind="student"
        mode={submission.mode}
        summaryItems={summaryItems}
        notice="La vérification d'antécédents Sterling Backcheck prend environ 2 minutes. Les frais vous seront intégralement remboursés après vos 5 premières heures de service."
        primaryAction={{
          label: 'Ouvrir Sterling Backcheck',
          onClick: () => {
            window.open(STERLING_BACKCHECK_PORTAL_URL, '_blank', 'noopener,noreferrer');
            if (typeof onComplete === 'function') onComplete();
          },
        }}
        secondaryAction={{
          label: 'Plus tard — voir mes missions',
          onClick: () => { if (typeof onComplete === 'function') onComplete(); },
        }}
      />
    );
  }

  return (
    <div style={{ maxWidth: 720, margin: '0 auto 32px' }}>
      <div className="eyebrow">Inscription étudiant · Étape {String(step).padStart(2, '0')} / 02</div>
      <h2 style={{ marginTop: 10, fontSize: 32, lineHeight: 1.15 }}>
        {step === 1 ? 'Informations académiques' : 'Antécédents Sterling Backcheck'}
      </h2>
      <p style={{ color: 'var(--ink-2)', marginTop: 10, fontSize: 15, lineHeight: 1.55 }}>
        {step === 1
          ? 'Renseigne tes coordonnées institutionnelles. Tu poursuivras avec la vérification de sécurité obligatoire.'
          : 'Dernière étape avant l’accès aux missions : la vérification d’identité et d’antécédents.'}
      </p>

      {step === 1 && (
        <div style={{ marginTop: 28 }}>
          <Field label="Nom">
            <input
              style={inputStyle}
              autoComplete="family-name"
              value={form.nom}
              onChange={(e) => setForm((f) => ({ ...f, nom: e.target.value }))}
            />
          </Field>
          <Field label="Prénom">
            <input
              style={inputStyle}
              autoComplete="given-name"
              value={form.prenom}
              onChange={(e) => setForm((f) => ({ ...f, prenom: e.target.value }))}
            />
          </Field>
          <Field label="Courriel (Université / Cégep)" note="Adresse fournie par ton établissement si possible.">
            <input
              style={inputStyle}
              type="email"
              autoComplete="email"
              value={form.email}
              onChange={(e) => setForm((f) => ({ ...f, email: e.target.value }))}
            />
          </Field>
          <Field label="Téléphone">
            <input
              style={inputStyle}
              type="tel"
              autoComplete="tel"
              value={form.phone}
              onChange={(e) => setForm((f) => ({ ...f, phone: e.target.value }))}
            />
          </Field>
          <Field label="Institution">
            <div style={{ position: 'relative', width: '100%' }}>
              <select
                aria-label="Institution"
                value={form.institutionId}
                onChange={(e) => setForm((f) => ({ ...f, institutionId: e.target.value }))}
                style={{
                  ...inputStyle,
                  appearance: 'none',
                  WebkitAppearance: 'none',
                  MozAppearance: 'none',
                  paddingRight: 46,
                  cursor: 'pointer',
                  lineHeight: 1.35,
                }}
              >
                {STUDENT_INSTITUTIONS.map((inst) => (
                  <option key={inst.id} value={inst.id}>{inst.label}</option>
                ))}
              </select>
              <span
                aria-hidden
                style={{
                  position: 'absolute',
                  right: 14,
                  top: '50%',
                  transform: 'translateY(-50%)',
                  pointerEvents: 'none',
                  color: 'var(--ink-3)',
                  display: 'flex',
                  alignItems: 'center',
                  lineHeight: 0,
                }}
              >
                <Icon name="chevron-down" size={20} />
              </span>
            </div>
          </Field>

          <div
            role="group"
            aria-labelledby="student-loi25-consent-label"
            style={{
              marginTop: 20,
              padding: '16px 18px',
              borderRadius: 12,
              border: '1px solid var(--line)',
              background: 'var(--surface)',
              fontSize: 14,
              lineHeight: 1.5,
              color: 'var(--ink-2)',
            }}
          >
            <label id="student-loi25-consent-label" style={{ display: 'flex', gap: 12, alignItems: 'flex-start', cursor: 'pointer', margin: 0 }}>
              <input
                type="checkbox"
                checked={loi25Consent}
                onChange={(e) => setLoi25Consent(e.target.checked)}
                style={{ marginTop: 3, width: 18, height: 18, flexShrink: 0, accentColor: 'var(--primary)' }}
                aria-required="true"
              />
              <span>
                Je consens à la collecte, à l'utilisation et à la communication de mes renseignements personnels
                conformément à la <strong>Loi 25</strong> (Québec) et à la{' '}
                <a href="#privacy" style={{ color: 'var(--primary)', fontWeight: 600 }}>politique de confidentialité</a>{' '}
                de Maison Sereine, dans le seul but de traiter ma candidature comme intervenant.
              </span>
            </label>
          </div>

          <div className="row" style={{ justifyContent: 'flex-end', marginTop: 8 }}>
            <button
              type="button"
              className="btn btn-primary"
              disabled={!step1Valid || !loi25Consent || submission.status === 'pending'}
              style={{
                opacity: !step1Valid || !loi25Consent || submission.status === 'pending' ? 0.45 : 1,
                minHeight: 44,
                padding: '12px 20px',
                cursor: submission.status === 'pending' ? 'progress' : undefined,
              }}
              onClick={continueToStep2}
            >
              {submission.status === 'pending'
                ? 'Envoi en cours…'
                : 'Continuer'}
              {submission.status !== 'pending' && <Icon name="arrow-right" size={16} />}
            </button>
          </div>

          {submission.status === 'error' && step === 1 ? (
            <div
              role="alert"
              style={{
                marginTop: 16,
                padding: '12px 14px',
                borderRadius: 10,
                background: '#FBECE7',
                border: '1px solid #F1B7A6',
                color: '#7A2412',
                fontSize: 13,
                lineHeight: 1.5,
              }}
            >
              Une erreur est survenue lors de l'envoi de votre dossier. Réessayez dans un
              instant ou écrivez-nous directement à <strong>contact@maisonsereine.ca</strong>.
            </div>
          ) : null}
        </div>
      )}

      {step === 2 && (
        <div style={{ marginTop: 28 }}>
          <div
            style={{
              padding: '18px 22px',
              borderRadius: 14,
              background: '#FDECE4',
              border: '1px solid var(--accent)',
              display: 'grid',
              gridTemplateColumns: '44px minmax(0, 1fr)',
              gap: 16,
              alignItems: 'start',
            }}
          >
            <div
              style={{
                width: 44,
                height: 44,
                borderRadius: 12,
                background: 'var(--accent)',
                color: '#fff',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
              }}
            >
              <Icon name="shield" size={20} />
            </div>
            <div>
              <div style={{ fontFamily: 'var(--font-display)', fontSize: 18, color: 'var(--ink)' }}>
                Vérification Sterling Backcheck
              </div>
              <div style={{ fontSize: 13, color: 'var(--ink-2)', marginTop: 4, lineHeight: 1.5 }}>
                Obligatoire pour débloquer les missions. Traitement numérique via Sterling Backcheck (environ 2 minutes).
              </div>
            </div>
          </div>

          <div
            role="note"
            style={{
              marginTop: 20,
              padding: '16px 18px',
              borderRadius: 12,
              border: '1px solid var(--line)',
              background: 'var(--surface)',
              fontSize: 14,
              lineHeight: 1.6,
              color: 'var(--ink-2)',
            }}
          >
            <strong style={{ color: 'var(--ink)' }}>Important.</strong>{' '}
            La vérification d'antécédents est obligatoire pour garantir la sécurité de nos familles. Les frais vous seront intégralement remboursés après vos 5 premières heures de service sur la plateforme.
          </div>

          <div style={{ marginTop: 14, fontSize: 13, color: 'var(--ink-3)' }}>
            Récap : <strong>{form.prenom} {form.nom}</strong> · {institutionLabel} · {form.email}
          </div>

          <div
            style={{
              display: 'flex',
              flexWrap: 'wrap',
              justifyContent: 'space-between',
              alignItems: 'center',
              gap: 12,
              marginTop: 28,
              width: '100%',
            }}
          >
            <button
              type="button"
              className="btn btn-ghost"
              onClick={() => setStep(1)}
              disabled={submission.status === 'pending'}
              style={{
                minHeight: 44,
                padding: '12px 18px',
                opacity: submission.status === 'pending' ? 0.55 : 1,
              }}
            >
              <Icon name="arrow-left" size={16} /> Retour
            </button>
            <button
              type="button"
              className="btn btn-primary"
              onClick={startSterlingVerification}
              style={{ minHeight: 44, padding: '12px 20px' }}
            >
              Commencer la vérification (2 min) <Icon name="arrow-right" size={16} />
            </button>
          </div>
        </div>
      )}
    </div>
  );
};

const StudentFlow = () => {
  const [signupDone, setSignupDone] = React.useState(false);
  const [accepted, setAccepted] = React.useState([]);
  const [hoveredId, setHoveredId] = React.useState(null);
  const [verifStatus, setVerifStatus] = React.useState('pending'); // 'pending' | 'loading' | 'verified'

  const handleVerification = () => {
    setVerifStatus('loading');
    setTimeout(() => setVerifStatus('verified'), 3000);
  };
  // Aucune mission proposée à l'amorçage — le feed se peuple via la coordination clinique
  const missions = [];

  const myGigs = missions.filter(m => accepted.includes(m.id));
  const available = missions.filter(m => !accepted.includes(m.id));

  // Bandeau de bienvenue — chiffres remis à zéro tant qu'aucune mission n'a été réalisée
  const earnedMonth = 0 + myGigs.reduce((s, m) => s + parsePay(m.pay), 0);
  const hoursWeek = 0 + myGigs.reduce((s, m) => s + parseHours(m.tags.find(t => /h$/.test(t)) || '2 h'), 0);
  const hoveredMission = missions.find(m => m.id === hoveredId);
  const pendingGain = hoveredMission && !accepted.includes(hoveredId) ? parsePay(hoveredMission.pay) : 0;
  const pendingHours = hoveredMission && !accepted.includes(hoveredId) ? parseHours(hoveredMission.tags.find(t => /h$/.test(t)) || '2 h') : 0;

  if (!signupDone) {
    return (
      <section style={{ padding: '32px 0 60px' }}>
        <div className="container" style={{ maxWidth: 1100 }}>
          <StudentSignupFlow onComplete={() => setSignupDone(true)} />
        </div>
      </section>
    );
  }

  return (
    <section style={{ padding: '32px 0 60px' }}>
      <div className="container" style={{ maxWidth: 1100 }}>
        {/* Verification banner — shown until verified */}
        {verifStatus !== 'verified' && (
          <div style={{
            marginTop: 20, padding: '18px 22px', borderRadius: 14,
            background: verifStatus === 'loading' ? 'var(--primary-soft)' : '#FDECE4',
            border: '1px solid ' + (verifStatus === 'loading' ? 'var(--primary)' : 'var(--accent)'),
            display: 'grid', gridTemplateColumns: '44px 1fr auto', gap: 16, alignItems: 'center'
          }}>
            <div style={{
              width: 44, height: 44, borderRadius: 12,
              background: verifStatus === 'loading' ? 'var(--primary)' : 'var(--accent)',
              color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center'
            }}>
              <Icon name={verifStatus === 'loading' ? 'clock' : 'shield'} size={20} />
            </div>
            <div>
              <div style={{ fontFamily: 'var(--font-display)', fontSize: 18, color: 'var(--ink)' }}>
                {verifStatus === 'loading'
                  ? "Vérification en cours…"
                  : "Action requise : Complétez votre vérification de sécurité"}
              </div>
              <div style={{ fontSize: 13, color: 'var(--ink-2)', marginTop: 2 }}>
                {verifStatus === 'loading'
                  ? "Connexion à Sterling Backcheck · secteur vulnérable"
                  : "Obligatoire pour débloquer les missions. Traitement automatique via Sterling Backcheck (2 min)."}
              </div>
            </div>
            <button
              className="btn btn-primary"
              onClick={handleVerification}
              disabled={verifStatus === 'loading'}
              style={{ padding: '12px 18px', minHeight: 44, fontSize: 14, opacity: verifStatus === 'loading' ? 0.7 : 1 }}
            >
              {verifStatus === 'loading' ? 'En cours…' : 'Commencer la vérification (2 min)'}
            </button>
          </div>
        )}

        {/* Student header */}
        <div style={{
          display: 'grid', gridTemplateColumns: '1fr auto', gap: 24, alignItems: 'center',
          padding: 28, borderRadius: 20, background: 'var(--primary)', color: '#fff', marginTop: 20
        }}>
          <div>
            <div className="row" style={{ gap: 10, alignItems: 'center' }}>
              <div className="eyebrow" style={{ color: '#BCCCC3' }}>Bienvenue, Partenaire</div>
              {verifStatus === 'verified' && (
                <span className="row" style={{
                  gap: 6, padding: '4px 10px', borderRadius: 999,
                  background: 'rgba(255,255,255,0.14)', fontSize: 12, color: '#DCE8DF'
                }}>
                  <Icon name="shield" size={12} /> Vérifié Sécurité
                </span>
              )}
            </div>
            <div style={{ fontFamily: 'var(--font-display)', fontSize: 30, marginTop: 4 }}>Votre feed s'activera après la vérification.</div>
            <p style={{ color: '#BCCCC3', marginTop: 8, fontSize: 15 }}>
              Les missions seront filtrées selon votre programme, votre horaire et votre rayon de 10 km.
            </p>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14, minWidth: 300 }}>
            <Stat label="Payé cette semaine" value="0 $" />
            <Stat label="Heures acceptées" value="0 h" />
            <Stat label="Note famille" value="—" />
            <Stat label="Missions à vie" value="0" />
          </div>
        </div>

        {myGigs.length > 0 && (
          <div style={{ marginTop: 40 }}>
            <h3 style={{ fontSize: 22, marginBottom: 14 }}>Tes missions acceptées</h3>
            <div style={{ display: 'grid', gap: 12 }}>
              {myGigs.map(m => <MissionCard key={m.id} m={m} accepted onRelease={() => setAccepted(a => a.filter(x => x !== m.id))} />)}
            </div>
          </div>
        )}

        <IncomePlanner earnedMonth={earnedMonth} hoursWeek={hoursWeek} pendingGain={pendingGain} pendingHours={pendingHours} />

        <div style={{ marginTop: 40 }}>
          <div className="row" style={{ justifyContent: 'space-between', marginBottom: 14 }}>
            <h3 style={{ fontSize: 22 }}>Feed des missions disponibles</h3>
            <div className="row" style={{ gap: 8, color: 'var(--ink-3)', fontSize: 13 }}>
              <Icon name="sparkle" size={14} /> Survolez une mission pour voir l'impact sur votre budget
            </div>
          </div>
          {available.length === 0 ? (
            <div style={{
              padding: '56px 24px', textAlign: 'center',
              background: 'var(--surface)', border: '1px dashed var(--line)',
              borderRadius: 18,
              display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 14
            }}>
              <div style={{
                width: 64, height: 64, borderRadius: 999,
                background: 'var(--bg-2)', color: 'var(--ink-3)',
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                border: '1px solid var(--line)'
              }}>
                <Icon name="map-pin" size={26} />
              </div>
              <div style={{ fontFamily: 'var(--font-display)', fontSize: 20, color: 'var(--ink)' }}>
                Aucune mission disponible dans votre secteur pour le moment.
              </div>
              <p style={{ fontSize: 14, color: 'var(--ink-3)', maxWidth: 440, lineHeight: 1.55, margin: 0 }}>
                Revenez bientôt&nbsp;! Vous recevrez une notification dès qu'une mission compatible
                avec votre programme, vos disponibilités et votre rayon de 10 km sera publiée.
              </p>
            </div>
          ) : (
            <div style={{ display: 'grid', gap: 12 }}>
              {available.map(m => (
                <div key={m.id} onMouseEnter={() => setHoveredId(m.id)} onMouseLeave={() => setHoveredId(null)}>
                  <MissionCard m={m} onAccept={() => setAccepted(a => [...a, m.id])} />
                </div>
              ))}
            </div>
          )}
        </div>
      </div>
    </section>
  );
};

const Stat = ({ label, value }) => (
  <div style={{ padding: 14, background: 'rgba(255,255,255,0.08)', borderRadius: 10, border: '1px solid rgba(255,255,255,0.14)' }}>
    <div style={{ fontSize: 11, color: '#BCCCC3', textTransform: 'uppercase', letterSpacing: '.08em' }}>{label}</div>
    <div style={{ fontFamily: 'var(--font-display)', fontSize: 22, marginTop: 4 }}>{value}</div>
  </div>
);

const MissionCard = ({ m, onAccept, onRelease, accepted }) => (
  <article style={{
    background: 'var(--surface)', border: '1px solid ' + (accepted ? 'var(--primary)' : 'var(--line)'),
    borderRadius: 16, padding: 20, display: 'grid', gridTemplateColumns: '1fr auto', gap: 24, alignItems: 'center'
  }}>
    <div style={{ minWidth: 0 }}>
      <div className="row" style={{ gap: 10, flexWrap: 'wrap' }}>
        <span className="chip" style={{ background: 'var(--primary-soft)', color: 'var(--primary-2)', borderColor: 'var(--primary-soft)' }}>{m.tier}</span>
        {m.tags.map(t => <span key={t} className="chip">{t}</span>)}
      </div>
      <div style={{ fontFamily: 'var(--font-display)', fontSize: 22, marginTop: 10 }}>{m.who}</div>
      <div className="row" style={{ gap: 14, marginTop: 6, color: 'var(--ink-3)', fontSize: 13, flexWrap: 'wrap' }}>
        <span className="row"><Icon name="map-pin" size={13} /> {m.city}</span>
        <span className="row"><Icon name="clock" size={13} /> {m.when}</span>
        <span className="row"><Icon name="car" size={13} /> {m.distance}</span>
      </div>
    </div>
    <div style={{ textAlign: 'right' }}>
      <div style={{ fontFamily: 'var(--font-display)', fontSize: 26, color: 'var(--primary)' }}>{m.pay}</div>
      <div style={{ fontSize: 12, color: 'var(--ink-3)' }}>payé vendredi</div>
      {accepted ? (
        <button onClick={onRelease} className="btn btn-ghost" style={{ marginTop: 10, padding: '10px 18px', minHeight: 40, fontSize: 14 }}>
          Libérer
        </button>
      ) : (
        <button onClick={onAccept} className="btn btn-primary" style={{ marginTop: 10, padding: '10px 20px', minHeight: 40, fontSize: 14 }}>
          Accepter
        </button>
      )}
    </div>
  </article>
);

window.BookingPage = BookingPage;
