/* components.jsx — shared atoms for Cabinet Maris redesign */

const { useState, useEffect, useMemo, useRef } = React;

/* ─── Icons (inline SVG, currentColor) ─────────────────────────────────── */
const Icon = ({ name, size = 18 }) => {
  const paths = {
    arrow: <path d="M5 12h14M13 6l6 6-6 6" />,
    arrowBack: <path d="M19 12H5M11 6l-6 6 6 6" />,
    chevL: <path d="M15 18l-6-6 6-6" />,
    chevR: <path d="M9 6l6 6-6 6" />,
    phone: <path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6A19.79 19.79 0 0 1 2.12 4.18 2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z" />,
    mail: <><rect x="2" y="4" width="20" height="16" rx="2" /><path d="m22 7-10 6L2 7" /></>,
    pin: <><path d="M20 10c0 7-8 12-8 12s-8-5-8-12a8 8 0 0 1 16 0z" /><circle cx="12" cy="10" r="3" /></>,
    clock: <><circle cx="12" cy="12" r="10" /><path d="M12 6v6l4 2" /></>,
    shield: <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />,
    check: <path d="M20 6 9 17l-5-5" />,
    sparkle: <path d="M12 2v6m0 8v6M4 12H2m20 0h-2M5.6 5.6 4.2 4.2m15.6 1.4-1.4-1.4M5.6 18.4l-1.4 1.4m15.6-1.4 1.4 1.4" />,
    sms: <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />,
    lock: <><rect x="3" y="11" width="18" height="11" rx="2" /><path d="M7 11V7a5 5 0 0 1 10 0v4" /></>,
    calendar: <><rect x="3" y="4" width="18" height="18" rx="2" /><path d="M16 2v4M8 2v4M3 10h18" /></>,
    user: <><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" /></>,
    smile: (
      <>
        <path d="M3 9c2-3 5-5 9-5s7 2 9 5" />
        <path d="M3 9c1 6 5 11 9 11s8-5 9-11" />
        <path d="M9 9v3M12 9v3M15 9v3" />
      </>
    ),
    spray: (
      <>
        <path d="M12 4c-1.2 0-1.8-.5-3-.5-2 0-3.5 1.5-3.5 4 0 3.2.7 7 1.8 10C7.8 19 9 19 9.4 17.5c.4-1.5.8-4 2.6-4s2.2 2.5 2.6 4c.4 1.5 1.6 1.5 2.1 0 1.1-3 1.8-6.8 1.8-10 0-2.5-1.5-4-3.5-4-1.2 0-1.8.5-3 .5Z" />
        <path d="M18.4 4l.5 1.1L20 5.6l-1.1.5-.5 1.1-.5-1.1L16.8 5.6l1.1-.5zM5 17l.35.8.8.35-.8.35L5 19.3l-.35-.8L3.85 18.15l.8-.35z" fill="currentColor" stroke="none" />
      </>
    ),
    drill: (
      <>
        <path d="M12 4c-1.2 0-1.8-.5-3-.5-2 0-3.5 1.5-3.5 4 0 3.2.7 7 1.8 10C7.8 19 9 19 9.4 17.5c.4-1.5.8-4 2.6-4s2.2 2.5 2.6 4c.4 1.5 1.6 1.5 2.1 0 1.1-3 1.8-6.8 1.8-10 0-2.5-1.5-4-3.5-4-1.2 0-1.8.5-3 .5Z" />
        <circle cx="14.2" cy="8.4" r="1.4" fill="currentColor" stroke="none" />
      </>
    ),
    sparkle: (
      <>
        <path d="M12 8c-1 0-1.5-.4-2.5-.4-1.6 0-3 1.2-3 3.4 0 2.6.6 5.6 1.5 8 .4 1.2 1.4 1.2 1.7 0 .3-1.2.7-3.2 2.3-3.2s2 2 2.3 3.2c.3 1.2 1.3 1.2 1.7 0 .9-2.4 1.5-5.4 1.5-8 0-2.2-1.4-3.4-3-3.4-1 0-1.5.4-2.5.4Z" />
        <path d="M12 2.5v2M5.8 5.2l1.2 1.2M18.2 5.2L17 6.4M2.8 11h2M19.2 11h2" />
      </>
    ),
    diamond: (
      <>
        <path d="M6 3.5h12l2.5 4.5-8.5 12L3.5 8z" />
        <path d="M3.5 8h17M8 3.5l-1 4.5 5 12M16 3.5l1 4.5-5 12" />
      </>
    ),
    align: (
      <>
        <path d="M4 7c0 6 3.6 11 8 11s8-5 8-11" />
        <path d="M4 7h16" />
        <path d="M7.5 7v2.4M10 7v3M14 7v3M16.5 7v2.4" />
        <rect x="9.5" y="11" width="5" height="2.6" rx=".6" />
      </>
    ),
    screw: (
      <>
        <path d="M9 3.5h6a1 1 0 0 1 1 1V8H8V4.5a1 1 0 0 1 1-1Z" />
        <path d="M12 8v13" />
        <path d="M9.5 10.5h5M9.5 13h5M9.5 15.5h5M9.5 18h5" />
      </>
    ),
    cross: (
      <>
        <path d="M13 8.5c-1 0-1.5-.4-2.5-.4-1.6 0-3 1.2-3 3.4 0 2.6.6 5.6 1.5 8 .4 1.2 1.4 1.2 1.7 0 .3-1.2.7-3.2 2.3-3.2s2 2 2.3 3.2c.3 1.2 1.3 1.2 1.7 0 .9-2.4 1.5-5.4 1.5-8 0-2.2-1.4-3.4-3-3.4-1 0-1.5.4-2.5.4Z" />
        <path d="M5 8l2.5-2.5L10 8M7.5 5.5v8" />
      </>
    ),
    canal: (
      <>
        <path d="M12 4c-1.2 0-1.8-.5-3-.5-2 0-3.5 1.5-3.5 4 0 3.2.7 7 1.8 10C7.8 19 9 19 9.4 17.5c.4-1.5.8-4 2.6-4s2.2 2.5 2.6 4c.4 1.5 1.6 1.5 2.1 0 1.1-3 1.8-6.8 1.8-10 0-2.5-1.5-4-3.5-4-1.2 0-1.8.5-3 .5Z" />
        <path d="M9.5 7.5v7.5M14.5 7.5v7.5" strokeDasharray="1.6 1.8" />
      </>
    ),
    info: <><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></>,
    fr: <text x="50%" y="68%" textAnchor="middle" fontSize="14" fill="currentColor" stroke="none" fontFamily="ui-sans-serif">FR</text>,
    en: <text x="50%" y="68%" textAnchor="middle" fontSize="14" fill="currentColor" stroke="none" fontFamily="ui-sans-serif">EN</text>,
  };
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
         stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"
         aria-hidden="true">
      {paths[name]}
    </svg>
  );
};

/* ─── Brand mark ───────────────────────────────────────────────────────── */
const Brand = () => (
  <a className="brand" href="#" onClick={(e) => e.preventDefault()}>
    <span className="brand-mark">M</span>
    <span className="brand-text">
      <b>Cabinet Maris</b>
      <small>Chirurgien-dentiste · Monaco</small>
    </span>
  </a>
);

/* ─── Language pill ────────────────────────────────────────────────────── */
const LangPill = ({ value, onChange }) => (
  <div className="lang-pill" role="tablist">
    {['FR', 'EN'].map((l) => (
      <button key={l} className={value === l ? 'on' : ''} onClick={() => onChange(l)}>{l}</button>
    ))}
  </div>
);

/* ─── Top bar ──────────────────────────────────────────────────────────── */
const TopBar = ({ lang, setLang, view, onNav, t }) => (
  <header className="topbar">
    <div className="topbar-l">
      <a className="brand" href="#" onClick={(e) => { e.preventDefault(); onNav('booking'); }}>
        <span className="brand-mark">M</span>
        <span className="brand-text">
          <b>Cabinet Maris</b>
          <small>Chirurgien-dentiste · Monaco</small>
        </span>
      </a>
    </div>
    <div className="topbar-r">
      <button className={`nav-link ${view === 'booking' ? 'active' : ''}`}
              onClick={() => onNav('booking')}>{t.nav.home}</button>
      <button className={`nav-link ${view === 'cabinet' ? 'active' : ''}`}
              onClick={() => onNav('cabinet')}>{t.nav.cabinet}</button>
      <button className={`nav-link ${view === 'equipe' ? 'active' : ''}`}
              onClick={() => onNav('equipe')}>{t.nav.team}</button>
      <button className={`nav-link ${view === 'honoraires' ? 'active' : ''}`}
              onClick={() => onNav('honoraires')}>{t.nav.fees}</button>
      <span style={{ width: 12 }} />
      <LangPill value={lang} onChange={setLang} />
      <span style={{ width: 6 }} />
      <a className="urgence-btn" href="tel:+37797700733">{t.urgence}</a>
    </div>
  </header>
);

/* ─── Stepper ──────────────────────────────────────────────────────────── */
const Stepper = ({ steps, current, onJump, t }) => (
  <nav className="stepper" aria-label="Booking progress" data-screen-label={`Step ${current + 1} / ${steps.length}`}>
    {steps.map((s, i) => {
      const state = i < current ? 'done' : i === current ? 'active' : '';
      return (
        <React.Fragment key={s.id}>
          <button
            type="button"
            className={`stepper-item ${state}`}
            onClick={() => i <= current && onJump(i)}
            aria-current={i === current ? 'step' : undefined}
          >
            <span className="num"><span>{i + 1}</span></span>
            <span>{s.label}</span>
          </button>
          {i < steps.length - 1 && <span className="stepper-divider" />}
        </React.Fragment>
      );
    })}
    <span className="stepper-spacer" />
    <span className="stepper-aside">
      <Icon name="lock" size={14} />
      {t.stepper_secure}
    </span>
  </nav>
);

/* ─── Treatment data ───────────────────────────────────────────────────── */
const TREATMENTS = {
  fr: {
    groups: [
      {
        title: 'Soins courants',
        emTitle: 'courants',
        items: [
          { id: 'consultation', icon: 'smile', name: 'Consultation & bilan', desc: 'Premier rendez-vous, bilan annuel, examen complet.', time: '1 h', price: '60 €' },
          { id: 'detartrage', icon: 'spray', name: 'Détartrage', desc: 'Nettoyage professionnel, polissage et conseils.', time: '45 min', price: '80–120 €' },
          { id: 'carie', icon: 'drill', name: 'Soin / Carie', desc: 'Traitement de carie, obturation composite invisible.', time: '45 min', price: 'sur devis' },
        ],
      },
      {
        title: 'Esthétique',
        emTitle: 'Esthétique',
        items: [
          { id: 'blanchiment', icon: 'sparkle', name: 'Blanchiment', desc: 'Blanchiment professionnel en cabinet ou gouttières.', time: '1 h 30', price: '400–600 €' },
          { id: 'facettes', icon: 'diamond', name: 'Facettes', desc: 'Facettes céramique ou composite, smile design.', time: '1 h 30', price: '650–900 €' },
          { id: 'orthodontie', icon: 'align', name: 'Orthodontie', desc: 'Aligneurs invisibles, bilan orthodontique.', time: '1 h', price: 'sur devis' },
        ],
      },
      {
        title: 'Chirurgie & implantologie',
        emTitle: 'implantologie',
        items: [
          { id: 'implant', icon: 'screw', name: 'Implant dentaire', desc: 'Consultation implantologie et devis personnalisé.', time: '1 h', price: 'sur devis' },
          { id: 'extraction', icon: 'cross', name: 'Extraction', desc: 'Extraction simple ou dent de sagesse.', time: '45 min', price: 'sur devis' },
          { id: 'canal', icon: 'canal', name: 'Traitement de canal', desc: 'Endodontie, dépulpation, retraitement.', time: '1 h 30', price: 'sur devis' },
        ],
      },
    ],
  },
  en: {
    groups: [
      {
        title: 'Routine care',
        emTitle: 'care',
        items: [
          { id: 'consultation', icon: 'smile', name: 'Consultation & exam', desc: 'First appointment, annual check-up, full review.', time: '1 h', price: '€60' },
          { id: 'detartrage', icon: 'spray', name: 'Scaling & polish', desc: 'Professional cleaning, polishing and advice.', time: '45 min', price: '€80–120' },
          { id: 'carie', icon: 'drill', name: 'Filling / Cavity', desc: 'Cavity treatment, invisible composite filling.', time: '45 min', price: 'on quote' },
        ],
      },
      {
        title: 'Cosmetic',
        emTitle: 'Cosmetic',
        items: [
          { id: 'blanchiment', icon: 'sparkle', name: 'Whitening', desc: 'In-office whitening or custom trays.', time: '1 h 30', price: '€400–600' },
          { id: 'facettes', icon: 'diamond', name: 'Veneers', desc: 'Ceramic or composite veneers, smile design.', time: '1 h 30', price: '€650–900' },
          { id: 'orthodontie', icon: 'align', name: 'Orthodontics', desc: 'Invisible aligners, orthodontic review.', time: '1 h', price: 'on quote' },
        ],
      },
      {
        title: 'Surgery & implants',
        emTitle: 'implants',
        items: [
          { id: 'implant', icon: 'screw', name: 'Dental implant', desc: 'Implant consultation and personalised quote.', time: '1 h', price: 'on quote' },
          { id: 'extraction', icon: 'cross', name: 'Extraction', desc: 'Simple extraction or wisdom tooth.', time: '45 min', price: 'on quote' },
          { id: 'canal', icon: 'canal', name: 'Root canal', desc: 'Endodontics, devitalisation, retreatment.', time: '1 h 30', price: 'on quote' },
        ],
      },
    ],
  },
};

const PRACTITIONERS = [
  { id: 'marchetti', initials: 'EM', name: 'Dr. Élise Marchetti', role: 'Chirurgien-dentiste · Responsable du cabinet',
    roleEn: 'Doctor of Dental Surgery · Practice Lead',
    photo: 'https://images.unsplash.com/photo-1559839734-2b71ea197ec2?w=560&h=700&fit=crop&crop=face&q=80',
    tags: ['Esthétique', 'Implants', 'Blanchiment'], langs: ['FR', 'EN', 'IT'] },
  { id: 'vidal', initials: 'TV', name: 'Dr. Thomas Vidal', role: 'Chirurgien-dentiste · Orthodontie',
    roleEn: 'Doctor of Dental Surgery · Orthodontics',
    photo: 'https://images.unsplash.com/photo-1612349317150-e413f6a5b16d?w=560&h=700&fit=crop&crop=face&q=80',
    tags: ['Orthodontie', 'Aligneurs', 'Pédodontie'], langs: ['FR', 'EN'] },
  { id: 'loriol', initials: 'CL', name: 'Dr. Camille Loriol', role: 'Chirurgienne-dentiste · Collaboratrice',
    roleEn: 'Doctor of Dental Surgery · Associate',
    photo: 'https://images.unsplash.com/photo-1594824476967-48c8b964273f?w=560&h=700&fit=crop&crop=face&q=80',
    tags: ['Implants', 'Chirurgie', 'Parodontie'], langs: ['FR', 'IT', 'EN'] },
];

/* ─── i18n ─────────────────────────────────────────────────────────────── */
const I18N = {
  fr: {
    nav: { home: 'Accueil', cabinet: 'Le cabinet', team: 'Équipe', fees: 'Honoraires' },
    urgence: 'Urgence',
    stepper_secure: 'Données HDS · RGPD',
    hero: {
      eyebrow: 'Prise de rendez-vous en ligne',
      title_1: 'Votre sourire,',
      title_em: 'notre',
      title_2: 'priorité.',
      sub: 'Cabinet Maris — chirurgien-dentiste à Monaco depuis 1992. Confirmation immédiate, rappel SMS 24 h avant, données médicales sécurisées HDS.',
      cta_book: 'Prendre rendez-vous',
      cta_call: 'Nous appeler',
      stats: [
        { n: '30+', l: "Ans d'expérience" },
        { n: '3', l: 'Praticien·ne·s' },
        { n: '48 h', l: 'Délai moyen' },
      ],
      next_label: 'Prochain créneau disponible',
      next_when: "Aujourd'hui — 14:30",
      slots: ['14:30', '15:15', '16:00', '17:30'],
      address: { h: 'Adresse', l: '11 Allée des Camomilles\nMC-98000 Monaco · 1er étage' },
      hours: {
        h: 'Horaires',
        rows: [['Lun – Ven', '8h30 – 19h'], ['Samedi', '9h – 13h'], ['Dimanche', 'Fermé']],
      },
    },
    steps: [
      { id: 'tx', label: 'Soin' },
      { id: 'prac', label: 'Praticien' },
      { id: 'date', label: 'Date & heure' },
      { id: 'info', label: 'Informations' },
      { id: 'confirm', label: 'Confirmation' },
    ],
    step1: {
      title_1: 'Quel soin',
      title_em: 'recherchez-vous',
      title_2: '?',
      lede: 'Sélectionnez le type de consultation. Cela nous permet d\'allouer le créneau adapté et le bon praticien.',
      urgence_t: 'Urgence dentaire',
      urgence_em: '?',
      urgence_p: 'Douleur aiguë, dent cassée, abcès — appelez-nous directement.',
      cta_next: 'Choisir un praticien',
    },
    step2: {
      title_1: 'Choisissez votre',
      title_em: 'praticien',
      title_2: '.',
      lede: 'Tous nos praticien·ne·s sont qualifié·e·s pour votre soin. Vous pouvez en choisir un·e ou laisser le système affecter le premier disponible.',
      auto_name: 'Premier disponible',
      auto_role: 'Affectation automatique selon votre soin',
      reco: 'Recommandé',
      auto_meta: '+ de créneaux · toutes langues',
      cta_next: 'Choisir un créneau',
    },
    step3: {
      title_1: 'Choisissez une date',
      title_em: 'et un créneau',
      title_2: '.',
      lede: 'Les jours avec un point ont des disponibilités. Cliquez pour voir les créneaux.',
      morning: 'Matin',
      afternoon: 'Après-midi',
      evening: 'Soirée',
      empty_t: 'Sélectionnez un jour',
      empty_p: 'Choisissez un jour avec un point vert pour voir les créneaux disponibles.',
      cta_next: 'Mes informations',
    },
    step4: {
      title_1: 'Vos',
      title_em: 'informations',
      title_2: ' médicales.',
      lede: 'Ces informations sont strictement confidentielles et transmises uniquement au praticien concerné.',
      antecedents: 'Antécédents médicaux à signaler (cochez si applicable)',
      antecedents_list: [
        'Allergie au latex', 'Allergie à l\'anesthésie', 'Anticoagulants',
        'Diabète', 'Problème cardiaque', 'Grossesse',
      ],
      first: 'Prénom', last: 'Nom', dob: 'Date de naissance', phone: 'Téléphone', email: 'Email',
      ssn: 'Numéro de sécurité sociale (optionnel)', mutuelle: 'Mutuelle / Assurance (optionnel)',
      status: 'Êtes-vous déjà patient ?', lang_visit: 'Langue de consultation',
      notes: 'Notes pour le praticien (optionnel)', notes_ph: 'Précisez si besoin (allergies, traitements en cours, anxiété…)',
      status_opts: [{ v: '', l: 'Choisissez…' }, { v: 'new', l: 'Nouveau patient' }, { v: 'old', l: 'Patient existant' }],
      consent: 'J\'accepte que mes données de santé soient traitées par le Cabinet Maris dans le cadre de ma prise en charge. Données hébergées sur serveur certifié HDS, conformément au RGPD et au secret médical.',
      cta_next: 'Vérifier & confirmer',
    },
    step5: {
      title_1: 'Vérifiez votre',
      title_em: 'rendez-vous',
      title_2: '.',
      lede: 'Vérifiez les informations ci-dessous. Tout est correct ? Confirmez pour recevoir votre récapitulatif par email et SMS.',
      pay_t: 'Règlement sur place uniquement',
      pay_p: 'Paiement par carte, chèque ou espèces au cabinet. Remboursement selon votre mutuelle. Devis détaillé fourni avant tout acte.',
      cancel_t: 'Annulation gratuite',
      cancel_p: 'Vous pouvez annuler ou déplacer votre RDV jusqu\'à 24 h avant.',
      back_edit: 'Modifier',
      confirm: 'Confirmer définitivement',
    },
    confirm: {
      title_1: 'Rendez-vous',
      title_em: 'confirmé',
      title_2: '.',
      p: 'Une confirmation détaillée vous a été envoyée par email et SMS. Nous avons hâte de vous accueillir.',
      ref: 'Référence',
      pills: ['Email envoyé', 'SMS rappel 24 h avant', 'Agenda mis à jour', 'Données HDS sécurisées'],
      addto: 'Ajouter à mon agenda',
      another: 'Prendre un autre RDV',
      rows: [
        { k: 'Soin', key: 'treatment' },
        { k: 'Praticien', key: 'practitioner' },
        { k: 'Date', key: 'date' },
        { k: 'Lieu', key: 'place' },
        { k: 'Patient', key: 'patient' },
      ],
    },
    rail: {
      h: 'Récapitulatif',
      treatment: 'Soin', duration: 'Durée', practitioner: 'Praticien',
      date: 'Date', time: 'Heure', place: 'Lieu', patient: 'Patient',
      placeholder: '—',
    },
    trust: [
      { icon: 'shield', t: 'Données HDS · RGPD' },
      { icon: 'sms', t: 'Rappel SMS 24 h avant' },
      { icon: 'check', t: 'Annulation gratuite J−1' },
    ],
    processing: {
      title_1: 'Enregistrement de votre',
      title_em: 'rendez-vous',
      title_2: '…',
      lede: 'Cela prend quelques secondes. Ne fermez pas cette page.',
      steps: [
        'Vérification des disponibilités',
        'Réservation du créneau',
        'Envoi de la confirmation',
      ],
    },
    error: {
      title_1: 'Un',
      title_em: 'problème',
      title_2: ' est survenu.',
      p: "Nous n'avons pas pu enregistrer votre rendez-vous. Vos informations n'ont pas été perdues — vous pouvez réessayer ou nous joindre directement.",
      detail_label: 'Détail technique',
      retry: 'Réessayer',
      back_edit: 'Modifier ma demande',
      fallback_t: 'Réservez par téléphone',
      fallback_p: 'Notre secrétariat répond du lundi au samedi.',
      fallback_btn: '+377 97 70 07 33',
      causes: [
        'Le créneau choisi vient d\'être réservé par un autre patient.',
        'La connexion au serveur a expiré.',
        'Une vérification manuelle est requise pour ce type de soin.',
      ],
    },
    months: ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'],
    weekdays: ['Lun','Mar','Mer','Jeu','Ven','Sam','Dim'],
    weekdays_short: ['L','M','M','J','V','S','D'],
    today: "Aujourd'hui",
    tomorrow: 'Demain',
    avail: (n) => `${n} créneaux`,
    avail_none: 'Complet',
  },
  en: {
    nav: { home: 'Home', cabinet: 'Practice', team: 'Team', fees: 'Fees' },
    urgence: 'Emergency',
    stepper_secure: 'HDS data · GDPR',
    hero: {
      eyebrow: 'Online booking',
      title_1: 'Your smile,',
      title_em: 'our',
      title_2: 'priority.',
      sub: 'Cabinet Maris — dental surgeon in Monaco since 1992. Instant confirmation, SMS reminder 24 h ahead, HDS-secured medical data.',
      cta_book: 'Book an appointment',
      cta_call: 'Call us',
      stats: [
        { n: '30+', l: 'Years of practice' },
        { n: '3', l: 'Practitioners' },
        { n: '48 h', l: 'Average wait' },
      ],
      next_label: 'Next available slot',
      next_when: 'Today — 2:30 pm',
      slots: ['2:30 pm', '3:15 pm', '4:00 pm', '5:30 pm'],
      address: { h: 'Address', l: '11 Allée des Camomilles\nMC-98000 Monaco · 1st floor' },
      hours: {
        h: 'Hours',
        rows: [['Mon – Fri', '8:30 am – 7 pm'], ['Saturday', '9 am – 1 pm'], ['Sunday', 'Closed']],
      },
    },
    steps: [
      { id: 'tx', label: 'Care' },
      { id: 'prac', label: 'Practitioner' },
      { id: 'date', label: 'Date & time' },
      { id: 'info', label: 'Your details' },
      { id: 'confirm', label: 'Confirm' },
    ],
    step1: {
      title_1: 'What care are',
      title_em: 'you looking for',
      title_2: '?',
      lede: 'Pick the type of visit. We use it to assign the right slot and the right practitioner.',
      urgence_t: 'Dental emergency',
      urgence_em: '?',
      urgence_p: 'Acute pain, broken tooth, abscess — please call us directly.',
      cta_next: 'Choose a practitioner',
    },
    step2: {
      title_1: 'Choose your',
      title_em: 'practitioner',
      title_2: '.',
      lede: 'All our practitioners are qualified for your care. Pick one specifically or let us assign whoever is available first.',
      auto_name: 'First available',
      auto_role: 'Auto-assigned based on your care',
      reco: 'Recommended',
      auto_meta: 'More slots · all languages',
      cta_next: 'Pick a slot',
    },
    step3: {
      title_1: 'Pick a date',
      title_em: 'and a time',
      title_2: '.',
      lede: 'Days with a dot have openings. Click a day to see the slots.',
      morning: 'Morning',
      afternoon: 'Afternoon',
      evening: 'Evening',
      empty_t: 'Pick a day',
      empty_p: 'Choose a day with a green dot to see the available slots.',
      cta_next: 'My details',
    },
    step4: {
      title_1: 'Your',
      title_em: 'medical details',
      title_2: '.',
      lede: 'Strictly confidential. Shared only with the practitioner in charge of your care.',
      antecedents: 'Medical history to disclose (tick if applicable)',
      antecedents_list: [
        'Latex allergy', 'Anaesthesia allergy', 'Blood thinners',
        'Diabetes', 'Heart condition', 'Pregnancy',
      ],
      first: 'First name', last: 'Last name', dob: 'Date of birth', phone: 'Phone', email: 'Email',
      ssn: 'Social security number (optional)', mutuelle: 'Insurance (optional)',
      status: 'Are you already a patient?', lang_visit: 'Visit language',
      notes: 'Notes for the practitioner (optional)', notes_ph: 'Anything we should know (allergies, ongoing treatments, anxiety…)',
      status_opts: [{ v: '', l: 'Choose…' }, { v: 'new', l: 'New patient' }, { v: 'old', l: 'Existing patient' }],
      consent: 'I consent to my health data being processed by Cabinet Maris as part of my care. Data is hosted on an HDS-certified server, compliant with GDPR and medical confidentiality.',
      cta_next: 'Review & confirm',
    },
    step5: {
      title_1: 'Review your',
      title_em: 'booking',
      title_2: '.',
      lede: 'Check the details below. All good? Confirm to receive your summary by email and SMS.',
      pay_t: 'Payment on site only',
      pay_p: 'Card, cheque or cash at the practice. Reimbursement per your insurance. Detailed quote provided before any procedure.',
      cancel_t: 'Free cancellation',
      cancel_p: 'You can cancel or reschedule up to 24 h before.',
      back_edit: 'Edit',
      confirm: 'Confirm booking',
    },
    confirm: {
      title_1: 'Booking',
      title_em: 'confirmed',
      title_2: '.',
      p: 'A detailed confirmation has been sent by email and SMS. We look forward to welcoming you.',
      ref: 'Reference',
      pills: ['Email sent', 'SMS reminder 24 h before', 'Calendar updated', 'HDS-secured data'],
      addto: 'Add to my calendar',
      another: 'Book another visit',
      rows: [
        { k: 'Care', key: 'treatment' },
        { k: 'Practitioner', key: 'practitioner' },
        { k: 'Date', key: 'date' },
        { k: 'Place', key: 'place' },
        { k: 'Patient', key: 'patient' },
      ],
    },
    rail: {
      h: 'Summary',
      treatment: 'Care', duration: 'Duration', practitioner: 'Practitioner',
      date: 'Date', time: 'Time', place: 'Place', patient: 'Patient',
      placeholder: '—',
    },
    trust: [
      { icon: 'shield', t: 'HDS · GDPR data' },
      { icon: 'sms', t: 'SMS reminder 24 h before' },
      { icon: 'check', t: 'Free cancellation D−1' },
    ],
    processing: {
      title_1: 'Saving your',
      title_em: 'appointment',
      title_2: '…',
      lede: 'This takes a few seconds. Please don\'t close this page.',
      steps: [
        'Checking availability',
        'Reserving your slot',
        'Sending confirmation',
      ],
    },
    error: {
      title_1: 'Something',
      title_em: 'went wrong',
      title_2: '.',
      p: "We couldn't save your appointment. Your details haven't been lost — you can retry or reach us directly.",
      detail_label: 'Technical detail',
      retry: 'Try again',
      back_edit: 'Edit my request',
      fallback_t: 'Book by phone',
      fallback_p: 'Reception is open Monday to Saturday.',
      fallback_btn: '+377 97 70 07 33',
      causes: [
        'The slot you picked was just reserved by another patient.',
        'The server connection timed out.',
        'This type of care needs manual review.',
      ],
    },
    months: ['January','February','March','April','May','June','July','August','September','October','November','December'],
    weekdays: ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'],
    weekdays_short: ['M','T','W','T','F','S','S'],
    today: 'Today',
    tomorrow: 'Tomorrow',
    avail: (n) => `${n} slots`,
    avail_none: 'Full',
  },
};

/* ─── Date helpers ─────────────────────────────────────────────────────── */
const SLOTS_FR_MORNING = ['08:30', '09:00', '09:30', '10:00', '10:30', '11:00', '11:30', '12:00'];
const SLOTS_FR_AFT = ['14:00', '14:30', '15:00', '15:30', '16:00', '16:30', '17:00', '17:30'];
const SLOTS_FR_EVE = ['18:00', '18:30'];
const SLOTS_EN_MORNING = ['8:30', '9:00', '9:30', '10:00', '10:30', '11:00', '11:30', '12:00'];
const SLOTS_EN_AFT = ['2:00 pm', '2:30 pm', '3:00 pm', '3:30 pm', '4:00 pm', '4:30 pm', '5:00 pm', '5:30 pm'];
const SLOTS_EN_EVE = ['6:00 pm', '6:30 pm'];

/* deterministic pseudo-random availability for a given day */
function hashDay(d) {
  const s = `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;
  let h = 2166136261;
  for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 16777619); }
  return (h >>> 0);
}
function dayAvail(d) {
  const dow = d.getDay();
  if (dow === 0) return { open: false, slots: [] }; // Sunday
  const h = hashDay(d);
  const max = dow === 6 ? 5 : 12;
  const count = (h % (max + 1));
  return { open: true, count, dow };
}
function slotsForDay(d, lang) {
  const dow = d.getDay();
  if (dow === 0) return { morning: [], afternoon: [], evening: [] };
  const h = hashDay(d);
  const M = lang === 'fr' ? SLOTS_FR_MORNING : SLOTS_EN_MORNING;
  const A = lang === 'fr' ? SLOTS_FR_AFT : SLOTS_EN_AFT;
  const E = lang === 'fr' ? SLOTS_FR_EVE : SLOTS_EN_EVE;
  const all = [
    ...M.map((t, i) => ({ t, bucket: 'morning', i })),
    ...A.map((t, i) => ({ t, bucket: 'afternoon', i: i + 100 })),
    ...(dow === 6 ? [] : E.map((t, i) => ({ t, bucket: 'evening', i: i + 200 }))),
  ];
  const keep = all.filter((s, idx) => ((h >> (idx % 30)) & 1) === 1);
  return {
    morning: keep.filter((s) => s.bucket === 'morning').map((s) => s.t),
    afternoon: keep.filter((s) => s.bucket === 'afternoon').map((s) => s.t),
    evening: keep.filter((s) => s.bucket === 'evening').map((s) => s.t),
  };
}

function fmtDate(d, lang) {
  if (!d) return null;
  const t = I18N[lang];
  return `${t.weekdays[(d.getDay() + 6) % 7]} ${d.getDate()} ${t.months[d.getMonth()].toLowerCase()}`;
}
function isSameDay(a, b) {
  if (!a || !b) return false;
  return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
}

/* ─── Side rail summary ────────────────────────────────────────────────── */
const SideRail = ({ state, lang }) => {
  const t = I18N[lang];
  const tx = useMemo(() => {
    if (!state.treatment) return null;
    for (const g of TREATMENTS[lang].groups) {
      const m = g.items.find((x) => x.id === state.treatment);
      if (m) return m;
    }
    return null;
  }, [state.treatment, lang]);
  const prac = state.practitioner === 'auto'
    ? { name: t.step2.auto_name, role: t.step2.auto_role }
    : PRACTITIONERS.find((p) => p.id === state.practitioner);
  const dateStr = state.date ? fmtDate(state.date, lang) : null;
  const patientName = (state.patient.first || state.patient.last)
    ? `${state.patient.first} ${state.patient.last}`.trim()
    : null;
  return (
    <aside className="rail">
      <div className="rail-card">
        <h5>{t.rail.h}</h5>
        <Row k={t.rail.treatment} v={tx ? tx.name : null} />
        <Row k={t.rail.duration} v={tx ? tx.time : null} />
        <Row k={t.rail.practitioner} v={prac ? prac.name : null} />
        <Row k={t.rail.date} v={dateStr} />
        <Row k={t.rail.time} v={state.time} />
        <Row k={t.rail.patient} v={patientName} />
      </div>
      <div className="rail-trust">
        {t.trust.map((it) => (
          <div className="rail-trust-item" key={it.t}>
            <Icon name={it.icon} size={16} />
            <span>{it.t}</span>
          </div>
        ))}
      </div>
    </aside>
  );
};
const Row = ({ k, v }) => (
  <div className="rail-row">
    <span className="k">{k}</span>
    <span className={`v ${!v ? 'empty' : ''}`}>{v || '—'}</span>
  </div>
);

Object.assign(window, {
  Icon, Brand, LangPill, TopBar, Stepper, SideRail, Row,
  TREATMENTS, PRACTITIONERS, I18N,
  dayAvail, slotsForDay, fmtDate, isSameDay, hashDay,
});
