/* YeYe Expat Centre — Expat dashboard (driven by client type) */
const D = window.YEYE;

const STAGE_ICONS = {
  gd_folder_opened: 'file-plus',
  hlasenka_posted: 'megaphone',
  documents_requested: 'mail',
  application_form_filled: 'clipboard-check',
  employment_docs_prepared: 'briefcase',
  embassy_appointment_requested: 'landmark',
  accommodation_arranged: 'house',
  translations_verified: 'languages',
  documents_delivered: 'package-check',
  application_submitted: 'send',
  oamp_received: 'stamp',
  result_received: 'trophy',
  insurance_arranged: 'shield-check',
  residence_card_collected: 'id-card',
};

function WelcomeBar({ ct, blankProfile }) {
  const { t } = useT();
  return (
    <div className="phead">
      <div className="pt">
        <div className="eyebrow">{t('dashboard.hereYouAre')}</div>
        {blankProfile ? (
          <>
            <h1 className="h1">{(() => {
              const s = window.YEYE_AUTH && window.YEYE_AUTH.getState && window.YEYE_AUTH.getState();
              const first = (s && s.contact && s.contact.firstName) || (s && s.user && s.user.name) || ct.who;
              const cap = first ? first.split(' ')[0] : '';
              const name = cap ? cap.charAt(0).toUpperCase() + cap.slice(1) : '';
              return name ? t('hero.blank.title').replace('{firstName}', name) : t('hero.blank.titleNoName');
            })()}</h1>
            <div className="muted">{t('hero.blank.subtitle')}</div>
          </>
        ) : (
          <h1 className="h1">Welcome Back, {(() => {
            const s = window.YEYE_AUTH && window.YEYE_AUTH.getState && window.YEYE_AUTH.getState();
            const first = (s && s.contact && s.contact.firstName) || (s && s.user && s.user.name) || ct.who;
            const cap = first ? first.split(' ')[0] : (ct.who || '').split(' ')[0];
            return cap ? cap.charAt(0).toUpperCase() + cap.slice(1) : '';
          })()}</h1>
        )}
        <div className="muted">You <span style={{ color: 'var(--accent-500)', fontWeight: 600 }}>Dare to go Further</span> with <span style={{ color: 'var(--brand-500)', fontWeight: 700 }}>YeYe</span></div>
      </div>
    </div>
  );
}

// Blue Card professional: raw documents the client provides (YeYe does not create them).
// Each uploads into a subfolder inside the contact's own "{Name} Expats Docs" Drive folder.
const BLUE_CARD_PRO_UPLOAD_DOCS = [
  { key: 'passport', subName: 'Identity and Passaport', label: { en: 'Passport (info page)', tr: 'Pasaport (bilgi sayfası)', cs: 'Pas (informační strana)' } },
  { key: 'biometric_photo', subName: 'Identity and Passaport', label: { en: 'Biometric photo', tr: 'Biyometrik fotoğraf', cs: 'Biometrická fotografie' } },
  { key: 'residence_permit', subName: 'Identity and Passaport', label: { en: 'Current residence permit / card (if any)', tr: 'Mevcut ikamet izni / kart (varsa)', cs: 'Stávající povolení k pobytu / karta (pokud existuje)' } },
  { key: 'criminal_record', subName: 'Identity and Passaport', label: { en: 'Criminal record (original)', tr: 'Sabıka kaydı (orijinal)', cs: 'Výpis z rejstříku trestů (originál)' } },
  { key: 'university_diploma', subName: 'Education', label: { en: 'University diploma', tr: 'Üniversite diploması', cs: 'Vysokoškolský diplom' } },
  { key: 'diploma_supplement', subName: 'Education', label: { en: 'Diploma supplement', tr: 'Diploma eki', cs: 'Dodatek k diplomu' } },
  { key: 'qualification_certificate', subName: 'Education', label: { en: 'Professional qualification certificate (if applicable)', tr: 'Mesleki yeterlilik belgesi (gerekiyorsa)', cs: 'Certifikát odborné kvalifikace (pokud existuje)' } },
];

const GENERIC_PRO_PHASES = [
  { title: { en: 'Eligibility & employment', tr: 'Uygunluk ve istihdam', cs: 'Způsobilost a zaměstnání' }, hint: { en: 'We review and confirm your eligibility and service requirements.', tr: 'Uygunluğunu ve hizmet gerekliliklerini inceleyip doğruluyoruz.', cs: 'Prověříme a potvrdíme vaši způsobilost a požadavky služby.' } },
  { title: { en: 'Document preparation', tr: 'Belge hazırlığı', cs: 'Příprava dokumentů' }, hint: { en: 'We collect, prepare and verify all required documents for you.', tr: 'Gerekli tüm belgeleri senin için topluyor, hazırlıyor ve doğruluyoruz.', cs: 'Shromáždíme, připravíme a ověříme všechny potřebné dokumenty.' } },
  { title: { en: 'Application & submission', tr: 'Başvuru ve sunum', cs: 'Žádost a podání' }, hint: { en: 'We finalize the package and complete the required submission.', tr: 'Dosyayı tamamlıyor ve gerekli başvuruyu gerçekleştiriyoruz.', cs: 'Dokončíme podklady a provedeme požadované podání.' } },
  { title: { en: 'Tracking & decision', tr: 'Takip ve karar', cs: 'Sledování a rozhodnutí' }, hint: { en: 'We track the process, respond to requests and manage the outcome.', tr: 'Süreci takip ediyor, talepleri yanıtlıyor ve sonucu yönetiyoruz.', cs: 'Sledujeme proces, reagujeme na požadavky a řídíme výsledek.' } },
  { title: { en: 'Ready', tr: 'Hazır', cs: 'Připraveno' }, hint: { en: 'We complete the final checks and close the process.', tr: 'Son kontrolleri tamamlıyor ve süreci kapatıyoruz.', cs: 'Dokončíme závěrečné kontroly a proces uzavřeme.' } },
];

function proPhasesForDefinition(definition) {
  const configured = Array.isArray(definition && definition.proPhases) ? definition.proPhases : [];
  if (configured.length) return configured;
  const processIds = (definition && definition.process || []).map((step) => step && step.id).filter(Boolean);
  return GENERIC_PRO_PHASES.map((copy, index) => ({
    id: `phase${index + 1}`,
    ...copy,
    processIds: processIds.slice(Math.floor(index * processIds.length / 5), Math.floor((index + 1) * processIds.length / 5)),
  }));
}

// Blue Card professional: documents YeYe prepares. Client does not upload these —
// they turn "done" when YeYe confirms them (Firestore document fulfilled).
const BLUE_CARD_PRO_YEYE_DOCS = [
  { key: 'application_form', label: { en: 'Application form', tr: 'Başvuru formu', cs: 'Formulář žádosti' } },
  { key: 'power_of_attorney', label: { en: 'Power of Attorney', tr: 'Vekâletname', cs: 'Plná moc' } },
  { key: 'accommodation_confirmation', label: { en: 'Accommodation confirmation', tr: 'Konaklama onayı', cs: 'Potvrzení o ubytování' } },
  { key: 'qualification_translation', label: { en: 'Certified Czech translations', tr: 'Onaylı Çekçe tercümeler', cs: 'Ověřené české překlady' } },
  { key: 'diploma_recognition', label: { en: 'Diploma recognition (if needed)', tr: 'Diploma tanıma (gerekiyorsa)', cs: 'Uznání diplomu (pokud je třeba)' } },
];

function ProDocumentUploads({ contactId, localText }) {
  const { t } = useT();
  const [state, setState] = React.useState({});
  const upload = async (doc, file) => {
    if (!file || !contactId || !(window.YEYE_BACKEND && window.YEYE_BACKEND.uploadArchiveFile)) return;
    setState((s) => ({ ...s, [doc.key]: { status: 'uploading', progress: 0 } }));
    try {
      const res = await window.YEYE_BACKEND.uploadArchiveFile({
        contactId,
        subName: doc.subName,
        file,
        onProgress: (p) => setState((s) => ({ ...s, [doc.key]: { status: 'uploading', progress: p } })),
      });
      setState((s) => ({ ...s, [doc.key]: { status: 'done', url: (res && (res.webViewLink || res.url || res.fileUrl)) || '' } }));
    } catch (_) {
      setState((s) => ({ ...s, [doc.key]: { status: 'error' } }));
    }
  };
  return (
    <div className="rowlist">
      {BLUE_CARD_PRO_UPLOAD_DOCS.map((doc) => {
        const st = state[doc.key] || {};
        const done = st.status === 'done';
        return (
          <div className="lrow" key={doc.key}>
            <div className="l-ic" style={done ? { background: 'var(--ok-bg)', color: 'var(--ok)' } : { background: 'var(--surface-3)', color: 'var(--ink-300)' }}><Icon name={done ? 'file-check-2' : 'upload'} size={17} /></div>
            <div className="l-bd"><div className="l-t">{localText(doc.label)}</div><div className="muted" style={{ fontSize: 11 }}>{doc.subName}</div></div>
            {st.status === 'uploading' ? <span className="muted tnum">{Math.round(st.progress || 0)}%</span>
              : done ? (st.url ? <a href={st.url} target="_blank" rel="noopener noreferrer" className="btn btn-quiet btn-sm"><Icon name="external-link" size={14} /> {t('playbook.view')}</a> : <Badge tone="ok" dot>{t('c.done')}</Badge>)
              : st.status === 'error' ? <label className="btn btn-quiet btn-sm" style={{ cursor: 'pointer', color: 'var(--bad)' }}><Icon name="rotate-ccw" size={14} /> {t('c.retry') !== 'c.retry' ? t('c.retry') : 'Retry'}<input type="file" style={{ display: 'none' }} onChange={(e) => upload(doc, e.target.files && e.target.files[0])} /></label>
              : <label className="btn btn-quiet btn-sm" style={{ cursor: 'pointer' }}><Icon name="upload" size={14} /> {t('mod.upTitle')}<input type="file" style={{ display: 'none' }} onChange={(e) => upload(doc, e.target.files && e.target.files[0])} /></label>}
          </div>
        );
      })}
    </div>
  );
}

function SharedPlaybookTrackerCard({ ct, purchase, processOptions, selectedProcessKey, onSelectProcess }) {
  const { t, lang } = useT();
  const authState = window.YEYE_AUTH && window.YEYE_AUTH.getState ? window.YEYE_AUTH.getState() : {};
  const contact = authState.contact || null;
  const contactId = String(contact && (contact.contactId || contact.id) || '');
  const [definition, setDefinition] = React.useState(null);
  const [playbook, setPlaybook] = React.useState(null);
  const [documents, setDocuments] = React.useState(() => new Map());
  const [api, setApi] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [readFailed, setReadFailed] = React.useState(false);
  const [writeNotice, setWriteNotice] = React.useState('');
  const [openDetail, setOpenDetail] = React.useState('');
  const fallbackServiceId = ct && ct.key;
  const match = api && api.resolvePlaybookPurchase(purchase, fallbackServiceId);
  const matchKey = match ? `${match.serviceId}:${match.variantKey}:${match.isDiy ? 'diy' : 'pro'}` : '';

  React.useEffect(() => {
    let cancelled = false;
    // NOTE: do NOT use dynamic import() here — this file is transformed by in-browser
    // Babel, which rewrites import() to require() (undefined in the browser). The ES
    // module is loaded by a <script type="module"> in index.html and exposed as
    // window.YEYE_PLAYBOOK_CLIENT.
    const assign = () => {
      if (cancelled) return;
      if (window.YEYE_PLAYBOOK_CLIENT) {
        setApi(window.YEYE_PLAYBOOK_CLIENT);
      } else {
        setLoading(false);
        setReadFailed(true);
      }
    };
    if (window.YEYE_PLAYBOOK_CLIENT) {
      assign();
    } else {
      window.addEventListener('yeye-playbook-client-ready', assign, { once: true });
      window.addEventListener('yeye-playbook-client-error', assign, { once: true });
    }
    return () => {
      cancelled = true;
      window.removeEventListener('yeye-playbook-client-ready', assign);
      window.removeEventListener('yeye-playbook-client-error', assign);
    };
  }, []);

  React.useEffect(() => {
    if (!api || !match) return undefined;
    let active = true;
    let unsubscribePlaybook = null;
    let unsubscribeDocuments = null;
    setDefinition(null);
    setPlaybook(null);
    setDocuments(new Map());
    setLoading(true);
    setReadFailed(false);
    setWriteNotice('');
    setOpenDetail('');

    const start = async () => {
      try {
        const loadedDefinition = await api.loadPlaybookDefinition(match.serviceId, match.variantKey);
        if (!active) return;
        setDefinition(loadedDefinition);
      } catch (error) {
        console.warn(`Playbook definition could not be loaded for ${match.serviceId}.`, error);
        if (active) setReadFailed(true);
      }
      if (!contactId || !active) {
        if (active) setLoading(false);
        return;
      }
      try {
        await api.ensureDashboardAuth(contactId);
      } catch (error) {
        console.warn('Dashboard custom-token sign-in is unavailable; attempting read-only access.', error);
      }
      if (!active) return;
      try {
        const initial = await api.readPlaybook(contactId, match.serviceId);
        if (!active) return;
        setPlaybook(initial || { process: {}, diy: {}, documents: {} });
        unsubscribePlaybook = api.watchPlaybook(contactId, match.serviceId, (next) => {
          if (active) setPlaybook(next || { process: {}, diy: {}, documents: {} });
        });
        unsubscribeDocuments = api.watchDocuments(contactId, (next) => {
          if (active) setDocuments(next || new Map());
        });
      } catch (error) {
        console.warn(`Playbook state could not be read for ${match.serviceId}.`, error);
        if (active) setReadFailed(true);
      } finally {
        if (active) setLoading(false);
      }
    };
    start();
    return () => {
      active = false;
      if (unsubscribePlaybook) unsubscribePlaybook();
      if (unsubscribeDocuments) unsubscribeDocuments();
    };
  }, [api, matchKey, contactId]);

  React.useEffect(() => {
    const handler = (event) => {
      const detail = event && event.detail;
      if (!detail || !detail.serviceKey || !match || !api || typeof api.resolvePlaybookPurchase !== 'function') return;
      let resolved = null;
      try { resolved = api.resolvePlaybookPurchase({ k: detail.serviceKey }); } catch (_) {}
      if (resolved && resolved.serviceId === match.serviceId) {
        setOpenDetail(detail.tab === 'documents' ? 'documents' : 'steps');
      }
    };
    window.addEventListener('yeye-open-playbook-detail', handler);
    return () => window.removeEventListener('yeye-open-playbook-detail', handler);
  }, [api, match && match.serviceId]);

  if (!match) return null;
  const localText = (value) => api ? api.textFor(value, lang) : '';
  const isDone = (value) => {
    if (value === true) return true;
    if (typeof value === 'string') return ['done', 'complete', 'completed', 'fulfilled'].includes(value.toLowerCase());
    if (!value || typeof value !== 'object') return false;
    const status = String(value.status || '').toLowerCase();
    return value.done === true || value.fulfilled === true || ['done', 'complete', 'completed', 'fulfilled'].includes(status);
  };
  const isCurrent = (value) => {
    const status = String(typeof value === 'string' ? value : (value && value.status) || '').toLowerCase();
    return ['current', 'active', 'in_progress', 'in-progress', 'processing'].includes(status);
  };
  const processState = playbook && playbook.process || {};
  const processRows = (definition && definition.process || []).map((step) => ({
    key: step.id,
    label: localText(step.text || step.label || step.title),
    completed: isDone(processState[step.id]),
    explicitCurrent: isCurrent(processState[step.id]),
  }));
  const hasProcessState = Object.keys(processState).length > 0;
  const firstIncompleteKey = hasProcessState && (processRows.find((row) => !row.completed) || {}).key;
  const rows = processRows.map((row) => ({
    ...row,
    isCurrent: !row.completed && (row.explicitCurrent || (!processRows.some((item) => item.explicitCurrent) && row.key === firstIncompleteKey)),
  }));
  const doneCount = rows.filter((row) => row.completed).length;
  const totalCount = rows.length;
  const progress = totalCount ? Math.round((doneCount / totalCount) * 100) : 0;
  const current = rows.find((row) => row.isCurrent) || (hasProcessState ? rows.find((row) => !row.completed) : null);
  const playbookDocuments = playbook && playbook.documents || {};
  const docItems = (definition && definition.documents || []).map((item) => {
    const state = documents.get(item.id)
      || (item.linkedDocKey && documents.get(item.linkedDocKey))
      || playbookDocuments[item.id]
      || {};
    const url = typeof state === 'string' && /^https?:\/\//i.test(state)
      ? state
      : (typeof state.url === 'string' && /^https?:\/\//i.test(state.url) ? state.url : '');
    return {
      key: item.id,
      label: localText(item.label),
      completed: isDone(state) || !!url,
      url,
    };
  });
  const docDoneCount = docItems.filter((item) => item.completed).length;
  const diySteps = match.isDiy && definition ? definition.diy : [];
  const diyChecks = diySteps.flatMap((step) => step.checks || []);
  const diyState = playbook && playbook.diy || {};
  const diyDoneCount = diyChecks.filter((check) => !!(diyState[check.id] && diyState[check.id].done)).length;
  const showDiyProcess = match.isDiy
    && !!(definition && Array.isArray(definition.diyProcess) && definition.diyProcess.length > 0);
  const isProfessionalProcess = !match.isDiy && processRows.length > 0;
  const isBlueCardProfessional = isProfessionalProcess && match.serviceId === 'blue_card';
  const diyProcess = showDiyProcess && definition ? (definition.diyProcess || []) : [];
  const diyProcessCompleted = diyProcess.map((stage) => isDone(diyState[stage.id]));
  const firstOpenDiyIndex = diyProcessCompleted.findIndex((done) => !done);
  const diyProcessRows = diyProcess.map((stage, index) => ({
    ...stage,
    number: index + 1,
    completed: diyProcessCompleted[index],
    isCurrent: index === firstOpenDiyIndex,
    title: stage.title,
    hint: stage.hint,
  }));
  const diyProcessDoneCount = diyProcessRows.filter((stage) => stage.completed).length;
  const diyProcessProgress = diyProcessRows.length ? Math.round((diyProcessDoneCount / diyProcessRows.length) * 100) : 0;
  const professionalPhases = isProfessionalProcess ? (() => {
    const rows = proPhasesForDefinition(definition).map((phase, index) => ({
      ...phase,
      key: phase.id || `phase${index + 1}`,
      number: index + 1,
      completed: (phase.processIds || []).every((id) => isDone(processState[id])),
    }));
    const firstOpen = rows.findIndex((phase) => !phase.completed);
    return rows.map((phase, index) => ({ ...phase, isCurrent: index === firstOpen }));
  })() : [];
  const focusCurrent = diyProcessRows.find((stage) => stage.isCurrent)
    || diyProcessRows.find((stage) => !stage.completed)
    || diyProcessRows[diyProcessRows.length - 1]
    || null;
  const focusIdx = focusCurrent ? diyProcessRows.indexOf(focusCurrent) : -1;
  const focusNext = focusIdx >= 0 ? (diyProcessRows[focusIdx + 1] || null) : null;
  const allStagesDone = diyProcessRows.length > 0 && diyProcessDoneCount === diyProcessRows.length;
  const showAccomDiy = !showDiyProcess && match.isDiy && match.serviceId === 'accommodation_confirmation' && diySteps.length > 0;
  const accomDiyRows = showAccomDiy ? (() => {
    const rows = diySteps.map((step, index) => {
      const stepChecks = step.checks || [];
      const completed = stepChecks.length > 0 && stepChecks.every((c) => !!(diyState[c.id] && diyState[c.id].done));
      return { ...step, number: step.step || index + 1, completed, isCurrent: false };
    });
    const firstOpen = rows.findIndex((s) => !s.completed);
    return rows.map((row, i) => ({ ...row, isCurrent: i === firstOpen }));
  })() : [];
  const accomCurrentStep = accomDiyRows.find((s) => s.isCurrent) || accomDiyRows.find((s) => !s.completed) || accomDiyRows[accomDiyRows.length - 1] || null;
  const accomCurrentIdx = accomCurrentStep ? accomDiyRows.indexOf(accomCurrentStep) : -1;
  const accomNextStep = accomCurrentIdx >= 0 ? (accomDiyRows[accomCurrentIdx + 1] || null) : null;
  const accomAllDone = accomDiyRows.length > 0 && accomDiyRows.every((s) => s.completed);
  const accomDoneSteps = accomDiyRows.filter((s) => s.completed).length;
  const titleBase = definition ? localText(definition.title) : (match.serviceId === 'blue_card' ? t('svc.blue_card') : t('playbook.accommodationTitle'));
  const variantTitle = definition && localText(definition.variantTitle);
  const modeLabel = (match.serviceId === 'accommodation_confirmation' || match.serviceId === 'blue_card' || match.isDiy || showDiyProcess)
    ? (match.isDiy ? { en: 'Do It Yourself', tr: 'Kendin Yap', cs: 'Svépomocí' } : { en: 'Do With YeYe', tr: 'YeYe ile Yap', cs: 'Udělej s YeYe' })
    : null;
  const modeSuffix = modeLabel ? localText(modeLabel) : '';
  const title = [variantTitle ? `${titleBase} · ${variantTitle}` : titleBase, modeSuffix].filter(Boolean).join(' · ');
  const modeBadge = match.isDiy ? 'DIY' : 'PRO';
  const tabs = (processOptions || []).filter((option) => option && ['blue_card', 'employee_card', 'accommodation_confirmation'].includes(option.key));

  const toggleDiy = async (checkId, done) => {
    setWriteNotice('');
    setPlaybook((previous) => ({
      ...(previous || { process: {}, documents: {} }),
      diy: {
        ...((previous && previous.diy) || {}),
        [checkId]: { done, at: new Date().toISOString(), by: 'client' },
      },
    }));
    try {
      if (!contactId) throw new Error('missing-contact-id');
      await api.setClientDiyCheck(contactId, checkId, done, match.serviceId);
    } catch (error) {
      console.warn(`DIY progress write failed for ${match.serviceId}/${checkId}.`, error);
      setWriteNotice(t('playbook.writeFailed'));
    }
  };

  const toggleDiyDocument = async (docKey, fulfilled) => {
    setWriteNotice('');
    setDocuments((previous) => {
      const next = new Map(previous || []);
      next.set(docKey, {
        ...(next.get(docKey) || {}),
        fulfilled,
        at: new Date().toISOString(),
        by: 'client',
      });
      return next;
    });
    try {
      if (!contactId) throw new Error('missing-contact-id');
      if (typeof api.setClientDocumentFulfilled === 'function') {
        await api.setClientDocumentFulfilled(contactId, docKey, fulfilled);
      } else if (typeof api.setDocumentFulfilled === 'function') {
        await api.setDocumentFulfilled(contactId, docKey, fulfilled, 'client');
      } else {
        throw new Error('document-fulfillment-write-unavailable');
      }
    } catch (error) {
      console.warn(`DIY document write failed for ${match.serviceId}/${docKey}.`, error);
      setWriteNotice(t('playbook.writeFailed'));
    }
  };

  const stepsModalTitle = match.isDiy ? t('playbook.myChecklist') : t('playbook.processTitle');
  const stepsModalSub = match.isDiy
    ? `${diyDoneCount}/${diyChecks.length} ${t('playbook.completed')}`
    : `${doneCount}/${totalCount} ${t('playbook.completed')}`;
  const professionalDonePhases = professionalPhases.filter((phase) => phase.completed).length;
  const summaryDoneCount = isProfessionalProcess ? professionalDonePhases : showDiyProcess ? diyProcessDoneCount : showAccomDiy ? accomDoneSteps : doneCount;
  const summaryTotalCount = isProfessionalProcess ? professionalPhases.length : showDiyProcess ? diyProcessRows.length : showAccomDiy ? accomDiyRows.length : totalCount;
  const summaryProgress = summaryTotalCount ? Math.round(summaryDoneCount / summaryTotalCount * 100) : 0;

  return (
    <>
    <Card className="mb-20">
      <div className="card-bd">
        <div className="flex jb ac mb-14 wrap gap-12">
          <div>
            <div className="flex ac gap-8 wrap">
              <div className="h3">{title || t('playbook.processTitle')}</div>
              <Badge tone={match.isDiy ? 'info' : 'brand'}>{modeBadge}</Badge>
              {tabs.length > 1 && <div className="flex gap-6 wrap">{tabs.map((option) => (
                <Btn key={option.key} type="button" variant={(selectedProcessKey || match.serviceId) === option.key ? 'primary' : 'ghost'} size="sm" icon={option.key === 'accommodation_confirmation' ? 'house' : option.key === 'blue_card' ? 'badge-check' : 'briefcase-business'} onClick={() => onSelectProcess && onSelectProcess(option.key)}>
                  {option.label}
                </Btn>
              ))}</div>}
            </div>
            <div className="muted" style={{ fontSize: 12.5 }}>{summaryDoneCount}/{summaryTotalCount} {t('playbook.completed')}</div>
          </div>
          <div className="txt-r"><div className="strong tnum" style={{ fontSize: 18 }}>{summaryProgress}%</div><div className="dim" style={{ fontSize: 11.5 }}>{t('sec.complete')}</div></div>
        </div>

        <div className={match.isDiy ? 'pb-diy-layout' : ''} style={{ display: 'flex', flexDirection: 'column' }}>
        {loading ? <div className="muted mb-14">{t('playbook.loading')}</div> : isProfessionalProcess ? (
          <div className="diy-process">
            <div className="diy-process-heading">
              <div><div className="eyebrow">{t('playbook.yourProcess')}</div></div>
            </div>
            <ol className="diy-pro-phases">{professionalPhases.map((phase) => {
              const statusClass = phase.completed ? 'is-done' : phase.isCurrent ? 'is-current' : 'is-pending';
              return <li key={phase.key} className={`diy-pro-phase ${statusClass}`}>
                <div className="diy-pro-phase-node">{phase.completed ? <Icon name="check" size={16} /> : phase.number}</div>
                <div className="diy-pro-phase-body">
                  <h4>{localText(phase.title)}</h4>
                  <p>{localText(phase.hint)}</p>
                </div>
              </li>;
            })}</ol>
          </div>
        ) : showDiyProcess ? (
          diyProcessRows.length ? <div className="diy-process">
            <div className="diy-process-heading">
              <div><div className="eyebrow">{t('playbook.yourProcess')}</div></div>
            </div>
            {writeNotice && <div className="diy-write-notice" role="status"><Icon name="cloud-off" size={16} /><span>{writeNotice}</span><button type="button" onClick={() => setWriteNotice('')} aria-label={t('playbook.dismissNotice')}><Icon name="x" size={15} /></button></div>}
            <div className="diy-focus">
              {focusCurrent && <div className={`diy-focus-card ${focusCurrent.completed ? 'is-done' : 'is-current'}`}>
                <div className="diy-focus-eyebrow">{t('playbook.step').replace('{number}', focusCurrent.number)} · {focusCurrent.completed ? t('c.done') : t('playbook.current')}</div>
                <h3>{localText(focusCurrent.title)}</h3>
                <p>{localText(focusCurrent.hint)}</p>
              </div>}
              {focusNext ? <div className="diy-focus-card is-next">
                <div className="diy-focus-eyebrow">{t('playbook.step').replace('{number}', focusNext.number)} · {t('playbook.next')}</div>
                <h3>{localText(focusNext.title)}</h3>
                <p>{localText(focusNext.hint)}</p>
              </div> : <div className="diy-focus-card is-empty">
                <div className="diy-focus-eyebrow">{t('playbook.next')}</div>
                <p>{allStagesDone ? t('playbook.allDone') : '—'}</p>
              </div>}
            </div>
          </div> : <div className="muted mb-14">{t('playbook.noProcess')}</div>
        ) : showAccomDiy ? (
          <div className="diy-process">
            {writeNotice && <div className="diy-write-notice" role="status"><Icon name="cloud-off" size={16} /><span>{writeNotice}</span><button type="button" onClick={() => setWriteNotice('')} aria-label={t('playbook.dismissNotice')}><Icon name="x" size={15} /></button></div>}
            <div className="diy-focus">
              {accomCurrentStep && <div className={`diy-focus-card ${accomCurrentStep.completed ? 'is-done' : 'is-current'}`}>
                <div className="diy-focus-eyebrow">{t('playbook.step').replace('{number}', accomCurrentStep.number)} · {accomCurrentStep.completed ? t('c.done') : t('playbook.current')}</div>
                <h3>{localText(accomCurrentStep.title)}</h3>
              </div>}
              {accomNextStep ? <div className="diy-focus-card is-next">
                <div className="diy-focus-eyebrow">{t('playbook.step').replace('{number}', accomNextStep.number)} · {t('playbook.next')}</div>
                <h3>{localText(accomNextStep.title)}</h3>
              </div> : <div className="diy-focus-card is-empty">
                <div className="diy-focus-eyebrow">{t('playbook.next')}</div>
                <p>{accomAllDone ? t('playbook.allDone') : '—'}</p>
              </div>}
            </div>
          </div>
        ) : current ? (
          <div style={{ background: 'var(--brand-50)', border: '1px solid var(--brand-200)', borderRadius: 'var(--r-md)', padding: '16px 18px', marginBottom: 16 }}>
            <div className="flex ac gap-12">
              <div className="l-ic" style={{ background: 'var(--brand-500)', color: 'white', width: 44, height: 44, flexShrink: 0 }}><Icon name="hourglass" size={22} /></div>
              <div style={{ minWidth: 0, flex: '1 1 auto' }}><div className="eyebrow" style={{ color: 'var(--brand-600)', marginBottom: 4 }}>{t('playbook.current')}</div><div className="h3" style={{ margin: 0 }}>{current.label}</div></div>
            </div>
          </div>
        ) : <div className="muted mb-14">{readFailed ? t('playbook.readUnavailable') : (totalCount > 0 && doneCount === totalCount ? t('playbook.processComplete') : t('playbook.emptyProcess'))}</div>}

        {/* DIY layout: action buttons sit ABOVE the current/next step focus cards. Pro keeps them below. */}
        <div className={'flex gap-8 wrap' + (match.isDiy ? ' diy-actions' : '')} style={{ order: match.isDiy ? -1 : 1, marginBottom: match.isDiy ? 14 : 0 }}>
          {showDiyProcess && diyProcessRows.length ? <Btn variant="ghost" className="pb-allsteps-btn" icon="list-checks" onClick={() => setOpenDetail('allsteps')}>{t('playbook.allSteps')} ({diyProcessDoneCount}/{diyProcessRows.length})</Btn> : null}
          {showAccomDiy ? <Btn variant="ghost" className="pb-allsteps-btn" icon="list-checks" onClick={() => setOpenDetail('accom-allsteps')}>{t('playbook.allSteps')} ({accomDoneSteps}/{accomDiyRows.length})</Btn> : null}
          {showAccomDiy ? <Btn variant="ghost" icon="clipboard-check" onClick={() => setOpenDetail('steps')}>{t('playbook.viewDetailedChecklist')}</Btn> : null}
          {!isProfessionalProcess && !showAccomDiy && <Btn variant="ghost" icon={match.isDiy ? 'clipboard-check' : 'list-checks'} onClick={() => setOpenDetail('steps')}>{showDiyProcess ? t('playbook.viewDetailedChecklist') : t('playbook.viewSteps')}</Btn>}
          <Btn variant="ghost" icon="folder" onClick={() => setOpenDetail('documents')}>{t('playbook.myDocuments')}{isBlueCardProfessional ? '' : ` (${docDoneCount}/${docItems.length})`}</Btn>
        </div>
        </div>
      </div>
    </Card>

    {openDetail === 'accom-allsteps' && <Modal icon="list-checks" title={t('playbook.allSteps')} sub={`${accomDoneSteps}/${accomDiyRows.length} ${t('playbook.completed')}`} onClose={() => setOpenDetail('')}>
      {writeNotice && <div className="diy-write-notice" role="status"><Icon name="cloud-off" size={16} /><span>{writeNotice}</span><button type="button" onClick={() => setWriteNotice('')} aria-label={t('playbook.dismissNotice')}><Icon name="x" size={15} /></button></div>}
      <ol className="diy-allsteps-list">{accomDiyRows.map((step) => {
        const statusLabel = step.completed ? t('c.done') : step.isCurrent ? t('playbook.current') : t('playbook.pending');
        const statusClass = step.completed ? 'is-done' : step.isCurrent ? 'is-current' : 'is-pending';
        const stepChecks = step.checks || [];
        const toggleStep = (checked) => stepChecks.forEach((c) => toggleDiy(c.id, checked));
        return <li key={step.number} className={`diy-allsteps-row ${statusClass}`}>
          <label className="diy-allsteps-check" aria-label={`${localText(step.title)} — ${statusLabel}`}>
            <input type="checkbox" checked={step.completed} onChange={(e) => toggleStep(e.target.checked)} />
            <span className="diy-allsteps-node">{step.completed ? <Icon name="check" size={15} /> : step.number}</span>
          </label>
          <div className="diy-allsteps-body">
            <div className="diy-allsteps-status">{t('playbook.step').replace('{number}', step.number)} · {statusLabel}</div>
            <h3>{localText(step.title)}</h3>
          </div>
        </li>;
      })}</ol>
    </Modal>}

    {openDetail === 'allsteps' && <Modal icon="list-checks" title={t('playbook.allSteps')} sub={`${diyProcessDoneCount}/${diyProcessRows.length} ${t('playbook.completed')}`} onClose={() => setOpenDetail('')}>
      {writeNotice && <div className="diy-write-notice" role="status"><Icon name="cloud-off" size={16} /><span>{writeNotice}</span><button type="button" onClick={() => setWriteNotice('')} aria-label={t('playbook.dismissNotice')}><Icon name="x" size={15} /></button></div>}
      <ol className="diy-allsteps-list">{diyProcessRows.map((stage) => {
        const statusLabel = stage.completed ? t('c.done') : stage.isCurrent ? t('playbook.current') : t('playbook.pending');
        const statusClass = stage.completed ? 'is-done' : stage.isCurrent ? 'is-current' : 'is-pending';
        return <li key={stage.id} className={`diy-allsteps-row ${statusClass}`}>
          <label className="diy-allsteps-check" aria-label={`${localText(stage.title)} — ${statusLabel}`}>
            <input type="checkbox" checked={stage.completed} disabled={isProfessionalProcess} onChange={(event) => { if (!isProfessionalProcess) toggleDiy(stage.id, event.target.checked); }} />
            <span className="diy-allsteps-node">{stage.completed ? <Icon name="check" size={15} /> : stage.number}</span>
          </label>
          <div className="diy-allsteps-body">
            <div className="diy-allsteps-status">{t('playbook.step').replace('{number}', stage.number)} · {statusLabel}</div>
            <h3>{localText(stage.title)}</h3>
            <p>{localText(stage.hint)}</p>
          </div>
        </li>;
      })}</ol>
    </Modal>}

    {openDetail === 'steps' && <Modal icon={match.isDiy ? 'clipboard-check' : 'list-checks'} title={stepsModalTitle} sub={stepsModalSub} onClose={() => setOpenDetail('')}>
      {match.isDiy ? <>
        {diySteps.length === 0 ? <div className="muted" style={{ padding: 10 }}>{t('playbook.noChecklist')}</div> : <>
          {writeNotice && <div className="diy-write-notice" role="status"><Icon name="cloud-off" size={16} /><span>{writeNotice}</span><button type="button" onClick={() => setWriteNotice('')} aria-label={t('playbook.dismissNotice')}><Icon name="x" size={15} /></button></div>}
          <div className="diy-checklist">
            <div className="diy-progress-wrap" style={{ padding: '0 0 10px' }}><Progress value={diyChecks.length ? (diyDoneCount / diyChecks.length) * 100 : 0} thin /></div>
            <ol className="diy-step-list">{diySteps.map((step, index) => (
              <li key={step.id || step.step || index}><div className="diy-step-card" style={{ border: '1px solid var(--line-2)', borderRadius: 'var(--r-md)', overflow: 'hidden' }}>
                <div className="diy-step-head"><span className="diy-step-number">{step.step || index + 1}</span><div><div className="eyebrow">{t('playbook.step').replace('{number}', step.step || index + 1)}</div><h2>{localText(step.title)}</h2></div></div>
                <div className="diy-checks">{(step.checks || []).map((check) => {
                  const checked = !!(diyState[check.id] && diyState[check.id].done);
                  return <label key={check.id} className={checked ? 'checked' : ''}><input type="checkbox" checked={checked} onChange={(event) => toggleDiy(check.id, event.target.checked)} /><span className="diy-checkbox"><Icon name="check" size={14} /></span><span>{localText(check.text)}</span></label>;
                })}</div>
              </div></li>
            ))}</ol>
          </div>
        </>}
      </> : <div className="rowlist">
        {rows.length === 0 ? <div className="muted" style={{ padding: 10 }}>{t('playbook.noSteps')}</div> : rows.map((row) => (
          <div className="lrow" key={row.key}>
            <div className="l-ic" style={row.completed ? { background: 'var(--ok-bg)', color: 'var(--ok)' } : row.isCurrent ? { background: 'var(--brand-50)', color: 'var(--brand-600)' } : { background: 'var(--surface-3)', color: 'var(--ink-300)' }}><Icon name={row.completed ? 'check' : row.isCurrent ? 'hourglass' : 'circle'} size={17} /></div>
            <div className="l-bd"><div className="l-t">{row.label}</div></div>
            {row.completed ? <Badge tone="ok" dot>{t('c.done')}</Badge> : row.isCurrent ? <Badge tone="info" dot>{t('playbook.current')}</Badge> : <Badge tone="neut">{t('playbook.pending')}</Badge>}
          </div>
        ))}
      </div>}
    </Modal>}

    {openDetail === 'documents' && <Modal icon="folder" title={t('playbook.myDocuments')} sub={`${docDoneCount}/${docItems.length} ${t('playbook.completed')}`} onClose={() => setOpenDetail('')}>
      {match.isDiy ? <>
        {writeNotice && <div className="diy-write-notice" role="status"><Icon name="cloud-off" size={16} /><span>{writeNotice}</span><button type="button" onClick={() => setWriteNotice('')} aria-label={t('playbook.dismissNotice')}><Icon name="x" size={15} /></button></div>}
        {docItems.length === 0 ? <div className="muted" style={{ padding: 10 }}>{t('playbook.noDocuments')}</div> : <div className="diy-checklist">
          <div className="diy-step-card" style={{ border: '1px solid var(--line-2)', borderRadius: 'var(--r-md)', overflow: 'hidden' }}>
            <div className="diy-checks">{docItems.map((item) => (
              <label key={item.key} className={item.completed ? 'checked' : ''}>
                <input type="checkbox" checked={item.completed} onChange={(event) => toggleDiyDocument(item.key, event.target.checked)} />
                <span className="diy-checkbox"><Icon name="check" size={14} /></span>
                <span>{item.label}{item.url && <> · <a href={item.url} target="_blank" rel="noopener noreferrer" onClick={(event) => event.stopPropagation()}>{t('playbook.view')}</a></>}</span>
              </label>
            ))}</div>
          </div>
        </div>}
      </> : isBlueCardProfessional ? <div className="flex col gap-16">
          <div>
            <div className="eyebrow" style={{ marginBottom: 8 }}>{t('playbook.docsYouProvide')}</div>
            <ProDocumentUploads contactId={contactId} localText={localText} />
          </div>
          <div>
            <div className="eyebrow" style={{ marginBottom: 8 }}>{t('playbook.docsWePrepare')}</div>
            <div className="rowlist">{BLUE_CARD_PRO_YEYE_DOCS.map((doc) => {
              const done = isDone(documents.get(doc.key)) || isDone(playbookDocuments[doc.key]);
              return <div className="lrow" key={doc.key}>
                <div className="l-ic" style={done ? { background: 'var(--ok-bg)', color: 'var(--ok)' } : { background: 'var(--surface-3)', color: 'var(--ink-300)' }}><Icon name={done ? 'file-check-2' : 'file-clock'} size={17} /></div>
                <div className="l-bd"><div className="l-t">{localText(doc.label)}</div></div>
                {done ? <Badge tone="ok" dot>{t('c.done')}</Badge> : <Badge tone="neut">{t('playbook.pending')}</Badge>}
              </div>;
            })}</div>
          </div>
        </div>
        : <div className="rowlist">
          {docItems.length === 0 ? <div className="muted" style={{ padding: 10 }}>{t('playbook.noDocuments')}</div> : docItems.map((item) => (
            <div className="lrow" key={item.key}>
              <div className="l-ic" style={item.completed ? { background: 'var(--ok-bg)', color: 'var(--ok)' } : { background: 'var(--surface-3)', color: 'var(--ink-300)' }}><Icon name={item.completed ? 'file-check-2' : 'file'} size={17} /></div>
              <div className="l-bd"><div className="l-t">{item.label}</div></div>
              {item.url ? <a href={item.url} target="_blank" rel="noopener noreferrer" className="btn btn-quiet btn-sm"><Icon name="external-link" size={14} /> {t('playbook.view')}</a> : item.completed ? <Badge tone="ok" dot>{t('c.done')}</Badge> : <Badge tone="neut">{t('playbook.pending')}</Badge>}
            </div>
          ))}
        </div>}
    </Modal>}
    </>
  );
}

function ProcessTrackerCard(props) {
  const key = props && props.ct && props.ct.key;
  const normalizedKey = String(key || '').toLowerCase();
  const playbookClient = window.YEYE_PLAYBOOK_CLIENT;
  const mappedServiceId = playbookClient && typeof playbookClient.playbookServiceIdForPurchaseKey === 'function'
    ? playbookClient.playbookServiceIdForPurchaseKey(normalizedKey)
    : '';
  const isPlaybookServiceId = [
    'accommodation_confirmation',
    'bank_account_opening',
    'blue_card',
    'blue_card_extension',
    'change_of_employer',
    'czech_birth_number',
    'employee_card',
    'employee_card_extension',
    'family_reunification',
    'foreign_police_visit',
    'home_finding',
    'lost_card_renewal',
    'lt_business_visa',
    'new_auto_registration',
    'verified_translation',
  ].includes(normalizedKey);
  if (key === 'blue_card' || key === 'blue_card_diy' || key === 'accommodation_confirmation' || /^accom\d+(_diy)?$/.test(String(key || '')) || mappedServiceId || isPlaybookServiceId) return <SharedPlaybookTrackerCard {...props} />;
  return <LegacyProcessTrackerCard {...props} />;
}

function LegacyProcessTrackerCard({ ct, go, processOptions, selectedProcessKey, onSelectProcess, compact = false }) {
  const { t, lang } = useT();
  const tracker = window.PROCESS_TRACKER;
  const authState = window.YEYE_AUTH && window.YEYE_AUTH.getState ? window.YEYE_AUTH.getState() : {};
  const contact = authState.contact || null;
  const formType = tracker && ct && (ct.key === 'blue_card' || ct.key === 'employee_card') ? ct.key : null;
  const [showSteps, setShowSteps] = React.useState(false);
  const [showDocs, setShowDocs] = React.useState(false);
  const copy = {
    tr: {
      current: 'Şu an',
      empty: 'Süreç henüz başlatılmadı',
      unavailable: 'Süreç bilgisi henüz bağlanmadı.',
      stepsBtn: 'Tüm adımları gör',
      stepsBtnClose: 'Adımları kapat',
      docsBtn: 'Belgelerim',
      docsBtnClose: 'Belgeleri kapat',
      view: 'Görüntüle',
      pending: 'Bekleniyor',
      complete: 'tamamlandı',
      servicesLink: 'Servislerimi gör →',
    },
    en: {
      current: 'Current',
      empty: 'Process not started yet',
      unavailable: 'Process information is not connected yet.',
      stepsBtn: 'View all steps',
      stepsBtnClose: 'Hide steps',
      docsBtn: 'My documents',
      docsBtnClose: 'Hide documents',
      view: 'View',
      pending: 'Pending',
      complete: 'completed',
      servicesLink: 'View my services →',
    },
    cs: {
      current: 'Aktuální',
      empty: 'Proces zatím nezačal',
      unavailable: 'Informace o procesu zatím nejsou připojeny.',
      stepsBtn: 'Zobrazit všechny kroky',
      stepsBtnClose: 'Skrýt kroky',
      docsBtn: 'Moje dokumenty',
      docsBtnClose: 'Skrýt dokumenty',
      view: 'Zobrazit',
      pending: 'Čeká',
      complete: 'dokončeno',
      servicesLink: 'Zobrazit mé služby →',
    },
  }[lang] || {};

  if (!tracker || !formType) return null;
  const title = tracker.PROCESS_TYPES?.[formType]?.title?.[lang] || tracker.PROCESS_TYPES?.[formType]?.title?.en || 'Application Process';
  const fallbackTabs = tracker.detectProcessTypes
    ? tracker.detectProcessTypes(contact).map((key) => ({
      key,
      label: key === 'blue_card' ? t('svc.blue_card') : t('svc.emp_card'),
    }))
    : [];
  const processTabs = (processOptions && processOptions.length ? processOptions : fallbackTabs)
    .filter((option) => option && (option.key === 'blue_card' || option.key === 'employee_card'));
  if (!contact) {
    return (
      <Card className="mb-20">
        <CardHead icon="list-checks" title={title} sub={copy.unavailable} />
      </Card>
    );
  }

  const effectiveDoc = tracker.processDocFromContact(contact, formType, { completedStages: {}, currentStage: tracker.PROCESS_STAGES[0] });
  const rows = tracker.getProcessStatus(effectiveDoc, null, lang) || [];
  const progress = tracker.calculateProgress(effectiveDoc);
  const doneCount = rows.filter((row) => row.completed).length;
  const totalCount = rows.length;
  const current = rows.find((row) => row.isCurrent) || rows.find((row) => !row.completed) || rows[rows.length - 1];
  const docStatusRaw = tracker.documentStatusFromContact ? tracker.documentStatusFromContact(contact, formType) : { items: [], total: 0, doneCount: 0 };
  const docItems = (docStatusRaw.items || []).filter((doc) => doc.key !== 'application_fee');
  const docStatus = {
    ...docStatusRaw,
    items: docItems,
    total: docItems.length,
    doneCount: docItems.filter((doc) => doc.completed).length,
  };

  return (
    <Card className="mb-20">
      <div className="card-bd">
        <div className="flex jb ac mb-14 wrap gap-12">
          <div>
            <div className="flex ac gap-8 wrap">
              <div className="h3">{title}</div>
              {processTabs.length > 1 && (
                <div className="flex gap-6 wrap">
                  {processTabs.map((option) => (
                    <Btn
                      key={option.key}
                      type="button"
                      variant={(selectedProcessKey || formType) === option.key ? 'primary' : 'ghost'}
                      size="sm"
                      icon={option.key === 'blue_card' ? 'badge-check' : 'briefcase-business'}
                      onClick={() => onSelectProcess && onSelectProcess(option.key)}
                    >
                      {option.label || (option.key === 'blue_card' ? t('svc.blue_card') : t('svc.emp_card'))}
                    </Btn>
                  ))}
                </div>
              )}
            </div>
            <div className="muted" style={{ fontSize: 12.5 }}>{doneCount}/{totalCount} {copy.complete}</div>
          </div>
          <div className="txt-r"><div className="strong tnum" style={{ fontSize: 18 }}>{progress}%</div><div className="dim" style={{ fontSize: 11.5 }}>{t('sec.complete')}</div></div>
        </div>

        <div style={{ height: 8, background: 'var(--surface-3)', borderRadius: 99, overflow: 'hidden', marginBottom: 18 }}>
          <span style={{ display: 'block', height: '100%', width: progress + '%', background: 'var(--brand-500)', borderRadius: 99 }} />
        </div>

        {/* Big active step */}
        {current ? (
          <div style={{ background: 'var(--brand-50)', border: '1px solid var(--brand-200)', borderRadius: 'var(--r-md)', padding: '16px 18px', marginBottom: 16 }}>
            <div className="flex ac gap-12">
              <div className="l-ic" style={{ background: 'var(--brand-500)', color: 'white', width: 44, height: 44, flexShrink: 0 }}>
                <Icon name={STAGE_ICONS[current.key] || (current.completed ? 'check' : 'hourglass')} size={22} />
              </div>
              <div style={{ minWidth: 0, flex: '1 1 auto' }}>
                <div className="eyebrow" style={{ color: 'var(--brand-600)', marginBottom: 4 }}>{copy.current}</div>
                <div className="h3" style={{ margin: 0 }}>{current.label}</div>
              </div>
            </div>
          </div>
        ) : (
          <div className="muted mb-14">{copy.empty}</div>
        )}

        {compact && typeof go === 'function' && (
          <button
            type="button"
            onClick={() => go('purchasedServices')}
            style={{ color: 'var(--brand-600)', fontSize: 12.5, fontWeight: 650, cursor: 'pointer', border: 'none', background: 'none', padding: 0, textDecoration: 'underline' }}
          >
            {copy.servicesLink}
          </button>
        )}

        {/* Toggle buttons */}
        {!compact && <div className="flex gap-8 wrap">
          <Btn variant="ghost" icon={showSteps ? 'chevron-up' : 'list-checks'} onClick={() => setShowSteps((v) => !v)}>
            {showSteps ? copy.stepsBtnClose : copy.stepsBtn}
          </Btn>
          <Btn variant="ghost" icon={showDocs ? 'chevron-up' : 'folder'} onClick={() => setShowDocs((v) => !v)}>
            {showDocs ? copy.docsBtnClose : copy.docsBtn} ({docStatus.doneCount}/{docStatus.total})
          </Btn>
        </div>}

        {/* Steps expand */}
        {!compact && showSteps && (
          <div className="rowlist" style={{ marginTop: 16, paddingTop: 12, borderTop: '1px solid var(--line-2)' }}>
            {rows.map((row) => (
              <div className="lrow" key={row.key}>
                <div className="l-ic" style={row.completed ? { background: 'var(--ok-bg)', color: 'var(--ok)' } : row.isCurrent ? { background: 'var(--brand-50)', color: 'var(--brand-600)' } : { background: 'var(--surface-3)', color: 'var(--ink-300)' }}>
                  <Icon name={STAGE_ICONS[row.key] || (row.completed ? 'check' : row.isCurrent ? 'hourglass' : 'circle')} size={17} />
                </div>
                <div className="l-bd"><div className="l-t">{row.label}</div></div>
                {row.completed ? <Badge tone="ok" dot>{t('c.done')}</Badge> : row.isCurrent ? <Badge tone="info" dot>{copy.current}</Badge> : <Badge tone="neut">{copy.pending}</Badge>}
              </div>
            ))}
          </div>
        )}

        {/* Docs expand */}
        {!compact && showDocs && (
          <div className="rowlist" style={{ marginTop: 16, paddingTop: 12, borderTop: '1px solid var(--line-2)' }}>
            {docStatus.items.map((doc) => (
              <div className="lrow" key={doc.key}>
                <div className="l-ic" style={doc.completed ? { background: 'var(--ok-bg)', color: 'var(--ok)' } : { background: 'var(--surface-3)', color: 'var(--ink-300)' }}>
                  <Icon name={doc.completed ? 'file-check-2' : 'file'} size={17} />
                </div>
                <div className="l-bd"><div className="l-t">{doc.label}</div></div>
                {doc.url ? (
                  <a href={doc.url} target="_blank" rel="noopener noreferrer" className="btn btn-quiet btn-sm">
                    <Icon name="external-link" size={14} /> {copy.view}
                  </a>
                ) : doc.completed ? <Badge tone="ok" dot>{t('c.done')}</Badge> : <Badge tone="neut">{copy.pending}</Badge>}
              </div>
            ))}
          </div>
        )}

      </div>
    </Card>
  );
}

function PurchasedServicesDashboardCard({ purchasedServices, go, lang, t }) {
  const [api, setApi] = React.useState(null);
  const [progress, setProgress] = React.useState({}); // { [row.k]: { done, total } }

  // Load YEYE_PLAYBOOK_CLIENT
  React.useEffect(() => {
    const assign = () => {
      if (window.YEYE_PLAYBOOK_CLIENT) setApi(window.YEYE_PLAYBOOK_CLIENT);
    };
    if (window.YEYE_PLAYBOOK_CLIENT) assign();
    else window.addEventListener('yeye-playbook-client-ready', assign, { once: true });
    return () => window.removeEventListener('yeye-playbook-client-ready', assign);
  }, []);

  // Load progress for each purchased service
  React.useEffect(() => {
    if (!api) return;
    const authState = window.YEYE_AUTH && window.YEYE_AUTH.getState ? window.YEYE_AUTH.getState() : {};
    const contact = authState.contact || null;
    const contactId = String(contact && (contact.contactId || contact.id) || '');
    if (!contactId) return;

    let cancelled = false;
    (async () => {
      const results = {};
      for (const row of (purchasedServices || [])) {
        try {
          // Resolve which playbook this purchase maps to
          const match = api.resolvePlaybookPurchase && api.resolvePlaybookPurchase(row, row.k);
          if (!match) continue;
          // Ensure auth
          try { await api.ensureDashboardAuth(contactId); } catch (_) {}
          // Read the playbook doc (may be null if user has not yet interacted)
          const doc = await api.readPlaybook(contactId, match.serviceId);
          if (cancelled) return;
          // Load definition to know total step count (proceed even when doc is null)
          let def = null;
          try { def = await api.loadPlaybookDefinition(match.serviceId, match.variantKey); } catch (_) {}
          if (!def) continue;
          let done = 0, total = 0;
          if (match.isDiy) {
            const diySteps = Array.isArray(def.diy) ? def.diy : [];
            const diyProcess = Array.isArray(def.diyProcess) ? def.diyProcess : [];
            const allDiy = diySteps.length ? diySteps : diyProcess;
            total = allDiy.reduce((acc, s) => acc + (Array.isArray(s.checks) ? s.checks.length : 1), 0) || allDiy.length;
            if (doc) {
              const diyMap = doc.diy && typeof doc.diy === 'object' ? doc.diy : {};
              done = Object.values(diyMap).filter((s) => s && s.done).length;
            }
          } else {
            const processSteps = Array.isArray(def.process) ? def.process : [];
            total = processSteps.length;
            if (doc) {
              const processMap = doc.process && typeof doc.process === 'object' ? doc.process : {};
              done = Object.values(processMap).filter((s) => s && s.status === 'done').length;
            }
          }
          if (total > 0) results[row.k] = { done, total };
        } catch (_) {}
      }
      if (!cancelled) setProgress(results);
    })();
    return () => { cancelled = true; };
  }, [api, (purchasedServices || []).map((r) => r.k).join(',')]);

  // variantInfoForKey helper (same as before)
  function variantInfoForKey(key) {
    const raw = String(key || '').toLowerCase();
    for (const svc of (D.services || [])) {
      if (!svc.family || !Array.isArray(svc.tabs)) continue;
      for (const tab of svc.tabs) {
        const variants = Array.isArray(tab.variants) ? tab.variants : [];
        const v = variants.find((variant) => String(variant.k || '').toLowerCase() === raw);
        if (v) {
          const tabLbl = (tab.label && (tab.label[lang] || tab.label.en)) || tab.k || '';
          const vLbl = (v.label && (v.label[lang] || v.label.en)) || (v.labelKey && t(v.labelKey)) || '';
          const isDiy = v.labelKey === 'svc.variant.diy' || /_diy$/.test(String(v.k || ''));
          const icon = D.catIcon[svc.cat] || 'package-check';
          return { tabLbl, vLbl, isDiy, icon };
        }
      }
    }
    // Flat services (top-level variants, no family/tabs) — e.g. birth_no, biz_visa,
    // family, employer_change. The family loop above misses these, so the row would
    // fall back to the raw key. Resolve the service name + variant label here.
    for (const svc of (D.services || [])) {
      if (svc.family || !Array.isArray(svc.variants)) continue;
      const v = svc.variants.find((variant) => String(variant.k || '').toLowerCase() === raw);
      if (!v && String(svc.k || '').toLowerCase() !== raw) continue;
      const svcName = (svc.name && typeof svc.name === 'object' ? (svc.name[lang] || svc.name.en) : svc.name) || svc.k || '';
      const matched = v
        || svc.variants.find((variant) => String(variant.k || '').toLowerCase() === String(svc.k || '').toLowerCase())
        || svc.variants[0];
      const vLbl = matched ? ((matched.label && (matched.label[lang] || matched.label.en)) || (matched.labelKey && t(matched.labelKey)) || '') : '';
      const isDiy = matched ? (matched.labelKey === 'svc.variant.diy' || /_diy$/.test(String(matched.k || ''))) : /_diy$/.test(raw);
      const icon = D.catIcon[svc.cat] || 'package-check';
      return { tabLbl: svcName, vLbl, isDiy, icon };
    }
    return null;
  }

  return (
    <Card className="mb-20">
      <CardHead icon="list-checks" title={t('nav.purchasedServices')} sub={(purchasedServices.length) + ' ' + t('svc.itemsWord')}
        action={<Btn variant="ghost" size="sm" iconR="arrow-right" onClick={() => go('purchasedServices')}>{{ tr: 'Tümünü gör', en: 'View all', cs: 'Zobrazit vše' }[lang] || 'View all'}</Btn>} />
      <div className="card-bd" style={{ paddingTop: 4, paddingBottom: 4 }}>
        <div className="rowlist">
          {(purchasedServices || []).map((row) => {
            const info = variantInfoForKey(row.k);
            const icon = info ? info.icon : 'package-check';
            const name = info ? (info.tabLbl + (info.vLbl ? ' · ' + info.vLbl : '')) : row.k;
            const isDiy = info ? info.isDiy : /_diy$/.test(String(row.k || ''));
            const status = row.status || 'active';
            const prog = progress[row.k];
            const pct = prog && prog.total > 0 ? Math.round((prog.done / prog.total) * 100) : null;
            return (
              <div className="lrow" key={row.id || row.k} style={{ flexDirection: 'column', alignItems: 'flex-start', gap: 6 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, width: '100%' }}>
                  <div className="l-ic" style={{ background: isDiy ? 'var(--brand-50)' : 'var(--surface-2)', flexShrink: 0 }}><Icon name={icon} size={18} /></div>
                  <div className="l-bd" style={{ flex: 1, minWidth: 0 }}><div className="l-t" style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{name}</div></div>
                  <Badge tone={status === 'cancelled' ? 'bad' : status === 'paused' ? 'warn' : 'ok'} dot>{t('st.' + status)}</Badge>
                </div>
                {pct !== null && (
                  <div style={{ width: '100%', paddingLeft: 42 }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                      <div style={{ flex: 1, height: 4, background: 'var(--surface-3)', borderRadius: 2, overflow: 'hidden' }}>
                        <div style={{ height: '100%', width: pct + '%', background: 'var(--brand-500)', borderRadius: 2, transition: 'width .4s' }} />
                      </div>
                      <span style={{ fontSize: 11, color: 'var(--ink-400)', fontWeight: 600, flexShrink: 0 }}>{pct}%</span>
                    </div>
                  </div>
                )}
              </div>
            );
          })}
        </div>
      </div>
    </Card>
  );
}

function dashboardContactFieldValue(contact, key) {
  if (!contact || !key) return '';
  const normalized = String(key).replace(/^contact\./, '');
  const raw = contact.raw || {};
  const direct = contact[key] ?? contact[normalized] ?? raw[key] ?? raw[normalized];
  if (direct != null && String(direct).trim()) return direct;
  const fields = contact.customFields || contact.customField || raw.customFields || raw.customField || [];
  const norm = (value) => String(value || '').toLowerCase().replace(/[\s._-]+/g, '');
  if (Array.isArray(fields)) {
    const found = fields.find((field) => [field?.key, field?.fieldKey, field?.name].some((candidate) => norm(candidate) === norm(key)));
    return found?.value ?? found?.field_value ?? found?.fieldValue ?? '';
  }
  if (fields && typeof fields === 'object') {
    const foundKey = Object.keys(fields).find((candidate) => norm(candidate) === norm(key));
    return foundKey ? fields[foundKey] : '';
  }
  return '';
}

function ExpatDashboard({ ct, go, openModal, calendarEvents, tasks, purchasedServices, blankProfile, calendarSync, onConnectGoogleCalendar, onSyncGoogleCalendar, onToggleTask, onRefresh }) {
  const { t, dict, currency, lang } = useT();
  const [selectedProcessKey, setSelectedProcessKey] = React.useState(ct?.key || 'employee_card');
  const [invoiceState, setInvoiceState] = React.useState({ invoices: [], loading: true, error: '' });
  const steps = (D.stepFlows[ct.steps] || []).map((s) => {
    if (ct.managerApproved && (s.k === 'ministry' || s.k === 'approved')) return { label: t('step.' + s.k), state: 'done' };
    return { label: t('step.' + s.k), state: s.state };
  });
  const apptDate = (a) => `${dict.weekdays[a.dowi]} ${a.d} ${dict.months[a.moi]} · ${a.time}`;
  const eventTitle = (e) => e.title || t('ev.' + e.key);
  const eventLoc = (e) => e.loc || t('ev.' + e.key + 'Loc');
  const eventDow = (d) => dict.weekdays[(FIRST_DOW + d - 1) % 7];
  const customAppts = (calendarEvents || []).filter((e) => e.key === 'custom' || e.key === 'google').map((e) => ({
    tk: e.key,
    title: eventTitle(e),
    loc: eventLoc(e),
    dowi: (FIRST_DOW + e.d - 1) % 7,
    d: e.d,
    moi: MONTH_IDX,
    dateLabel: e.dateLabel,
    time: e.time,
    tag: e.tag || (e.key === 'custom' ? 'confirmed' : (e.key === 'deadline' ? 'pending' : 'upcoming')),
    tagc: e.tagc || (e.key === 'deadline' ? 'warn' : (e.tone === 'ok' ? 'ok' : 'info')),
    ic: e.ic,
  }));
  const dashboardAppts = customAppts.concat(blankProfile ? [] : D.appts).slice(0, 4).map((a) => {
    if (ct.managerApproved && a.tk === 'ministry') return { ...a, tag: 'confirmed', tagc: 'ok' };
    return a;
  });
  const TICKET_TITLE_RE = /^\[([^\]]+)\]\s*/;
  const _pureTasks = (tasks || []).filter((task) => !TICKET_TITLE_RE.test(String(task.title || '')));
  const _ticketTasks = (tasks || []).filter((task) => TICKET_TITLE_RE.test(String(task.title || '')));
  const dashboardTasks = _pureTasks.slice(0, 4);
  const dashboardTickets = _ticketTasks
    .map((task) => {
      const title = String(task.title || '');
      const match = title.match(TICKET_TITLE_RE);
      const catK = match ? match[1].toLowerCase() : '';
      const subject = title.replace(TICKET_TITLE_RE, '').trim() || title;
      return { id: task.ghlTaskId || task.id, catK, subject, due: task.due || task.dateLabel || '—', done: !!task.done };
    })
    .slice(0, 4);
  const dashboardDocs = blankProfile ? [] : D.docs;
  const tracker = window.PROCESS_TRACKER;
  const authState = window.YEYE_AUTH && window.YEYE_AUTH.getState ? window.YEYE_AUTH.getState() : {};
  const contact = authState.contact || null;
  const contactId = contact && (contact.contactId || contact.id);
  const invoiceUtils = window.YEYE_INVOICES;
  React.useEffect(() => {
    if (!onRefresh) return undefined;
    const refreshDocuments = () => Promise.resolve(onRefresh()).catch(() => {});
    window.addEventListener('yeye:documents-changed', refreshDocuments);
    return () => window.removeEventListener('yeye:documents-changed', refreshDocuments);
  }, [onRefresh]);
  React.useEffect(() => {
    let active = true;
    const loadInvoices = async (silent) => {
      if (!contactId || !invoiceUtils || !window.YEYE_BACKEND || !window.YEYE_BACKEND.listInvoices) {
        if (active) setInvoiceState({ invoices: [], loading: false, error: '' });
        return;
      }
      if (!silent && active) setInvoiceState((prev) => ({ ...prev, loading: true, error: '' }));
      try {
        const result = await window.YEYE_BACKEND.listInvoices(contactId);
        if (active) setInvoiceState({ invoices: invoiceUtils.normalizeList(result), loading: false, error: '' });
      } catch (err) {
        if (active) setInvoiceState((prev) => ({
          ...prev,
          loading: false,
          error: (err && err.message) || 'Invoice sync failed',
        }));
      }
    };
    loadInvoices(false);
    const poll = () => {
      if (document.visibilityState === 'visible') loadInvoices(true);
    };
    const timer = window.setInterval(poll, 30000);
    document.addEventListener('visibilitychange', poll);
    return () => {
      active = false;
      window.clearInterval(timer);
      document.removeEventListener('visibilitychange', poll);
    };
  }, [contactId]);
  const dashboardInvoices = invoiceUtils
    ? invoiceUtils.visibleInvoices(invoiceState.invoices, contact)
    : [];
  React.useEffect(() => {
    if (!invoiceUtils) return;
    const unpaid = dashboardInvoices.filter((invoice) => invoiceUtils.statusFor(invoice) !== 'paid').length;
    window.dispatchEvent(new CustomEvent('yeye:invoices-changed', { detail: { unpaid } }));
  }, [invoiceState.invoices]);
  const invoiceTotalsReady = !invoiceState.loading && !invoiceState.error;
  const paidInvoices = dashboardInvoices.filter((invoice) => invoiceUtils.statusFor(invoice) === 'paid');
  const unpaidInvoices = dashboardInvoices.filter((invoice) => invoiceUtils.statusFor(invoice) !== 'paid');
  const paidInvoiceTotal = paidInvoices.reduce((sum, invoice) => sum + invoiceUtils.amountInCurrency(invoice, currency), 0);
  const outstandingInvoiceTotal = unpaidInvoices.reduce((sum, invoice) => sum + invoiceUtils.amountInCurrency(invoice, currency), 0);
  const recentInvoices = dashboardInvoices.slice().sort((a, b) => {
    const aTime = new Date(invoiceUtils.dateValueFor(a) || 0).getTime() || 0;
    const bTime = new Date(invoiceUtils.dateValueFor(b) || 0).getTime() || 0;
    return bTime - aTime;
  }).slice(0, 4);
  const openTaskCount = _pureTasks.filter((task) => !task.done).length;
  const documentCenterVerifiedCount = dashboardDocs.filter((doc) => doc.ok).length;
  const confirmedDocumentKeys = [
    ['blue_card_application_form', 'contact.blue_card_application_form_url'],
    ['employee_card_application_form', 'contact.employee_card_application_form_url'],
    ['employment_contract', 'contact.employment_contract_url'],
    ['salary_certificate', 'contact.salary_certificate_url'],
    ['power_of_attorney', 'contact.power_of_attorney_url'],
    ['accommodation_confirmation', 'contact.accommodation_confirmation_url'],
    ['hlasenka', 'contact.hlasenka_url'],
    ['criminal_record_apostilled', 'contact.criminal_record_apostilled_url'],
    ['diploma_apostilled', 'contact.diploma_apostilled_url'],
    ['certificates', 'contact.certificates_url'],
    ['passport_visa_copy', 'contact.passport_visa_copy_url'],
    ['biometric_photo', 'contact.biometric_photo_url'],
  ];
  const verifiedDocumentIds = new Set(dashboardDocs.filter((doc) => doc.ok).map((doc) => doc.tk));
  confirmedDocumentKeys.forEach(([documentId, fieldKey]) => {
    if (dashboardContactFieldValue(contact, fieldKey)) verifiedDocumentIds.add(documentId);
  });
  const verifiedDocCount = blankProfile ? 0 : verifiedDocumentIds.size;
  const missingDocCount = dashboardDocs.length - documentCenterVerifiedCount;
  const documentCenterSub = dashboardDocs.length === 0
    ? t('doc.awaiting')
    : t('sec.docCenterSub').replace('{ok}', documentCenterVerifiedCount).replace('{missing}', missingDocCount);
  const purchasedMap = new Map();
  const serviceForPurchase = (purchaseKey) => {
    const rawKey = String(purchaseKey || '').toLowerCase();
    if (rawKey === 'accommodation' || rawKey === 'accommodation_confirmation') {
      return (D.services || []).find((service) => service.k === 'accommodation_family')
        || (D.services || []).find((service) => service.k === 'accom12');
    }
    const normalizedKey = typeof window.YEYE_NORMALIZE_SERVICE_KEY === 'function'
      ? window.YEYE_NORMALIZE_SERVICE_KEY(rawKey)
      : rawKey;
    return (D.services || []).find((service) => {
      const keys = [service.k];
      if (service.family && Array.isArray(service.tabs)) {
        service.tabs.forEach((tab) => {
          keys.push(tab.k);
          (tab.variants || []).forEach((variant) => keys.push(variant.k));
        });
      }
      return keys.some((key) => key === rawKey || (typeof window.YEYE_NORMALIZE_SERVICE_KEY === 'function' && window.YEYE_NORMALIZE_SERVICE_KEY(key) === normalizedKey));
    });
  };
  const paidServicesFromContact = window.YEYE_PURCHASES && window.YEYE_PURCHASES.getPaidServices
    ? window.YEYE_PURCHASES.getPaidServices(authState.contact || {})
    : [];
  const userEmail = String((authState.user && authState.user.email) || (authState.contact && authState.contact.email) || '').toLowerCase();
  const hideSeededServices = ['toptenvideoyou@gmail.com', 'testblue.demo+20260619@yeye-internal.example.com'].includes(userEmail);
  if (!blankProfile && !hideSeededServices) {
    (D.services || []).filter((s) => s.owned).forEach((service) => {
      purchasedMap.set(service.k, { service, purchase: { k: service.k, status: 'active', source: 'profile' } });
    });
  }
  (purchasedServices || []).forEach((purchase) => {
    const service = serviceForPurchase(purchase.k);
    if (!service) return;
    const existing = purchasedMap.get(service.k);
    const authoritative = purchase.source === 'expats_services_status';
    const existingAuthoritative = existing && existing.purchase && existing.purchase.source === 'expats_services_status';
    if (!existing || authoritative || !existingAuthoritative) purchasedMap.set(service.k, { service, purchase });
  });
  const processSeen = new Set();
  const dashboardProcessOptions = Array.from(purchasedMap.values())
    .map((row) => {
      const purchaseKey = String(row.purchase && row.purchase.k || row.service.k || '').toLowerCase();
      const normalizedKey = typeof window.YEYE_NORMALIZE_SERVICE_KEY === 'function' ? window.YEYE_NORMALIZE_SERVICE_KEY(purchaseKey) : purchaseKey;
      const key = normalizedKey === 'blue_card'
        ? 'blue_card'
        : normalizedKey === 'emp_card'
          ? 'employee_card'
          : ['accommodation_confirmation', 'accommodation', 'accom12', 'accom24', 'accom36'].includes(normalizedKey)
            ? 'accommodation_confirmation'
            : '';
      if (!key || processSeen.has(key)) return null;
      processSeen.add(key);
      return { key, serviceKey: row.service.k, purchase: row.purchase, label: typeof svcName === 'function' ? svcName(row.service, t) : t('svc.' + row.service.k) };
    })
    .filter(Boolean);
  const contactProcessOptions = tracker && tracker.detectProcessTypes
    ? tracker.detectProcessTypes(authState.contact).map((key) => ({
      key,
      serviceKey: key === 'blue_card' ? 'blue_card' : 'emp_card',
      label: key === 'blue_card' ? t('svc.blue_card') : t('svc.emp_card'),
    }))
    : [];
  contactProcessOptions.forEach((option) => {
    if (!processSeen.has(option.key)) {
      processSeen.add(option.key);
      dashboardProcessOptions.push(option);
    }
  });
  React.useEffect(() => {
    if (!dashboardProcessOptions.length) {
      setSelectedProcessKey(ct?.key || 'employee_card');
      return;
    }
    if (!dashboardProcessOptions.some((option) => option.key === selectedProcessKey)) {
      setSelectedProcessKey(dashboardProcessOptions[0].key);
    }
  }, [ct?.key, dashboardProcessOptions.map((option) => option.key).join('|'), selectedProcessKey]);
  const activeProcessKey = dashboardProcessOptions.some((option) => option.key === selectedProcessKey)
    ? selectedProcessKey
    : (dashboardProcessOptions[0]?.key || ct.key);
  const activeCt = { ...ct, key: activeProcessKey };
  const activeProcessPurchase = dashboardProcessOptions.find((option) => option.key === activeProcessKey)?.purchase || null;
  const apptLine = (a) => a.title ? `${a.dateLabel || `${eventDow(a.d)} ${a.d} ${dict.months[a.moi]}`} · ${a.time}` : apptDate(a);
  const emptyRow = (icon, text) => (
    <div className="lrow" style={{ borderBottom: 'none' }}>
      <div className="l-ic" style={{ background: 'var(--surface-3)', color: 'var(--ink-300)' }}><Icon name={icon} size={18} /></div>
      <div className="l-bd"><div className="l-s">{text}</div></div>
    </div>
  );

  return (
    <>
      <WelcomeBar ct={ct} blankProfile={blankProfile} />

      {!blankProfile && (purchasedServices || []).length > 0 && (
        <PurchasedServicesDashboardCard
          purchasedServices={purchasedServices}
          go={go}
          lang={lang}
          t={t}
        />
      )}

      {/* stat row */}
      <div className="grid g-12 mb-20" style={{ gap: 16 }}>
        <div className="col-3"><Stat icon="file-check-2" value={String(verifiedDocCount)} label={t('stt.docs')} onClick={() => go('documents')} /></div>
        <div className="col-3"><Stat icon="calendar-clock" tone="blue" value="—" label={t('stt.nextAppt')} /></div>
        <div className="col-3"><Stat icon="receipt" tone="amber" value={invoiceTotalsReady ? moneyLabel(outstandingInvoiceTotal, currency) : '—'} label={t('stt.outstanding')} /></div>
        <div className="col-3"><Stat icon="circle-check-big" value={String(openTaskCount)} label={t('stt.openTasks')} /></div>
      </div>

      <div className="dash-grid">
        {paidServicesFromContact.length === 0 && (
          <div className="col-12">
            <Card>
              <CardHead icon="sparkles" title={t('dash.noServices.title')} sub={t('dash.noServices.body')}
                action={<Btn variant="primary" size="sm" iconR="arrow-right" onClick={() => go('services')}>{t('dash.noServices.cta')}</Btn>} />
            </Card>
          </div>
        )}

        {/* appointments */}
        <div className="col-12">
          <Card>
            <CardHead icon="calendar-days" title={t('sec.appointments')} sub={t('sec.apptSub')}
              action={<div className="flex gap-8 wrap">
                <Btn variant="quiet" size="sm" icon={calendarSync && calendarSync.connected ? 'refresh-cw' : 'calendar-check'} onClick={calendarSync && calendarSync.connected ? onSyncGoogleCalendar : onConnectGoogleCalendar}>
                  {calendarSync && calendarSync.syncing ? t('cal.syncing') : (calendarSync && calendarSync.connected ? t('cal.sync') : t('cal.connect'))}
                </Btn>
                <Btn variant="quiet" size="sm" icon="calendar-plus" onClick={() => openModal('calendarEvent')}>{t('c.add')}</Btn>
              </div>} />
            <div className="card-bd" style={{ paddingTop: 4, paddingBottom: 4 }}>
              <div className="rowlist">
                {dashboardAppts.length === 0 ? emptyRow('calendar-x', t('appt.noEvents')) : dashboardAppts.map((a, i) => (
                  <div className="lrow" key={i}>
                    <div className="l-ic"><Icon name={a.ic} size={18} /></div>
                    <div className="l-bd"><div className="l-t">{a.title || t(a.titleK)}</div><div className="l-s">{apptLine(a)} · {a.loc || t(a.locK)}</div></div>
                    <Badge tone={a.tagc} dot>{t('st.' + a.tag)}</Badge>
                  </div>
                ))}
              </div>
            </div>
          </Card>
        </div>

        {/* document center */}
        <div className="col-6">
          <Card>
            <CardHead icon="folder" title={t('sec.docCenter')} sub={documentCenterSub}
              action={<Btn variant="soft" size="sm" icon="upload" onClick={() => openModal('upload')}>{t('c.upload')}</Btn>} />
            <div className="card-bd" style={{ paddingTop: 4, paddingBottom: 4 }}>
              <div className="rowlist">
                {dashboardDocs.length === 0 ? emptyRow('folder-open', t('doc.awaiting')) : dashboardDocs.map((d, i) => (
                  <div className="lrow" key={i}>
                    <div className="l-ic" style={d.ok ? { background: 'var(--ok-bg)', color: 'var(--ok)' } : { background: 'var(--bad-bg)', color: 'var(--bad)' }}>
                      <Icon name={d.ok ? 'file-check-2' : 'file-warning'} size={18} />
                    </div>
                    <div className="l-bd"><div className="l-t">{t('doc.' + d.tk)}</div><div className="l-s">{d.ok ? t('c.verified') : (d.tk === 'criminal' ? t('doc.criminalSub') : t('doc.missingSub'))}</div></div>
                    {d.ok ? <Badge tone="ok" dot>{t('c.verified')}</Badge> : <Btn variant="soft" size="sm" icon="upload" onClick={() => openModal('upload')}>{t('c.upload')}</Btn>}
                  </div>
                ))}
              </div>
            </div>
          </Card>
        </div>

        {/* tasks */}
        <div className="col-6">
          <Card>
            <CardHead icon="circle-check-big" title={t('nav.tasks')} sub={t('sub.tasks')}
              action={<Btn variant="quiet" size="sm" iconR="arrow-right" onClick={() => go('tasks')}>{t('c.all')}</Btn>} />
            <div className="card-bd" style={{ paddingTop: 4, paddingBottom: 4 }}>
              <div className="rowlist">
                {dashboardTasks.length === 0 ? emptyRow('circle-check-big', '—') : dashboardTasks.map((tk, i) => (
                  <div className="lrow" key={i}>
                    <button className="icon-btn" onClick={() => onToggleTask && onToggleTask(tk)} style={{ width: 28, height: 28, color: tk.done ? 'var(--brand-500)' : 'var(--ink-300)' }}><Icon name={tk.done ? 'circle-check-big' : 'circle'} size={20} /></button>
                    <div className="l-bd">
                      <div className="l-t" style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', color: tk.done ? 'var(--ink-400)' : 'var(--ink-900)' }}>
                        {tk.taskType && <Badge tone={tk.taskTypeTone}>{t(tk.taskTypeLabelKey)}</Badge>}
                        <span style={{ textDecoration: tk.done ? 'line-through' : 'none' }}>{tk.title || t(tk.tk)}</span>
                      </div>
                    </div>
                    <Badge tone={tk.tone} dot>{(tk.done ? t('c.done') : t('c.due')) + ' ' + tk.due}</Badge>
                  </div>
                ))}
              </div>
            </div>
          </Card>
        </div>

        {/* tickets */}
        <div className="col-12">
          <Card>
            <CardHead icon="life-buoy" title={t('sec.tickets')} sub={t('sub.tickets')}
              action={<Btn variant="quiet" size="sm" iconR="arrow-right" onClick={() => go('support')}>{t('c.all')}</Btn>} />
            <div className="card-bd" style={{ paddingTop: 4, paddingBottom: 4 }}>
              <div className="rowlist">
                {dashboardTickets.length === 0 ? emptyRow('life-buoy', t('tickets.empty')) : dashboardTickets.map((tk, i) => (
                  <div className="lrow" key={tk.id || i}>
                    <div className="l-ic" style={tk.done ? { background: 'var(--ok-bg)', color: 'var(--ok)' } : { background: 'var(--info-bg)', color: 'var(--info)' }}>
                      <Icon name={tk.done ? 'check-circle-2' : 'life-buoy'} size={18} />
                    </div>
                    <div className="l-bd">
                      <div className="l-t" style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
                        {tk.catK && <Badge tone="info">{(() => { const key = 'ticket.cat.' + tk.catK; const val = t(key); return val === key ? tk.catK : val; })()}</Badge>}
                        <span>{tk.subject}</span>
                      </div>
                    </div>
                    <Badge tone={tk.done ? 'ok' : 'info'} dot>{tk.done ? t('c.resolved') : t('c.open')}</Badge>
                  </div>
                ))}
              </div>
            </div>
          </Card>
        </div>

        {/* payments */}
        <div className="col-12">
          <Card>
            <CardHead icon="receipt" title={t('sec.payments')} action={<div className="flex gap-8 wrap"><Btn variant="quiet" size="sm" icon="life-buoy" onClick={() => openModal('ticket', { catK: 'it', subject: t('ticket.itInvoiceSubject') })}>{t('c.openItTicket')}</Btn><Btn variant="quiet" size="sm" iconR="arrow-right" onClick={() => go('invoices')}>{t('c.all')}</Btn></div>} />
            <div className="card-bd">
              <div className="flex gap-12 mb-16">
                <div className="grow" style={{ background: 'var(--ok-bg)', border: '1px solid var(--ok-line)', borderRadius: 'var(--r-md)', padding: '12px 14px' }}>
                  <div className="dim" style={{ fontSize: 11.5, fontWeight: 600, color: 'var(--ok)' }}>{t('st.paid')}</div><div className="strong tnum" style={{ fontSize: 19 }}>{invoiceTotalsReady ? moneyLabel(paidInvoiceTotal, currency) : '—'}</div>
                </div>
                <div className="grow" style={{ background: 'var(--warn-bg)', border: '1px solid var(--warn-line)', borderRadius: 'var(--r-md)', padding: '12px 14px' }}>
                  <div className="dim" style={{ fontSize: 11.5, fontWeight: 600, color: 'var(--warn)' }}>{t('st.outstanding')}</div><div className="strong tnum" style={{ fontSize: 19 }}>{invoiceTotalsReady ? moneyLabel(outstandingInvoiceTotal, currency) : '—'}</div>
                </div>
              </div>
              {(() => {
                if (invoiceState.loading) return emptyRow('receipt', '—');
                if (!recentInvoices.length) return emptyRow('receipt', '—');
                return (
                  <div className="rowlist">
                    {recentInvoices.map((invoice, i) => {
                      const id = invoiceUtils.idFor(invoice);
                      const status = invoiceUtils.statusFor(invoice);
                      const invoiceUrl = invoice.invoiceUrl || invoice.url || invoice.paymentUrl;
                      const invoicePdfUrl = invoice.pdfUrl || invoice.downloadUrl;
                      return (
                        <div className="lrow" key={id || i} style={{ padding: '6px 0' }}>
                          <div className="l-bd">
                            <div className="l-t">{invoiceUtils.nameFor(invoice, t('invoices.title'))}</div>
                            <div className="l-s">{invoiceUtils.amountFor(invoice)} · {invoiceUtils.dateFor(invoice)}</div>
                          </div>
                          <Badge tone={invoiceUtils.toneFor(status)} dot>{t('invoices.status.' + status)}</Badge>
                          <div className="flex gap-6 wrap">
                            {status !== 'paid' && invoiceUrl && <a className="btn btn-primary btn-sm" href={invoiceUrl} target="_blank" rel="noopener noreferrer"><Icon name="credit-card" size={14} />{t('invoices.pay')}</a>}
                            {invoicePdfUrl && <a className="btn btn-ghost btn-sm" href={invoicePdfUrl} target="_blank" rel="noopener noreferrer" download><Icon name="download" size={14} />{t('invoices.downloadPdf')}</a>}
                          </div>
                        </div>
                      );
                    })}
                  </div>
                );
              })()}
            </div>
          </Card>
        </div>

      </div>
    </>
  );
}

Object.assign(window, { ExpatDashboard });
