// Admin pages: Dashboard, Upload, Categories, Profiles, Stats, Settings (with Admin-PW, AdSense, Premium).

// === Admin auth gate ===
const AdminLoginGate = ({ navigate, onAuthed }) => {
  const [settings] = useSettings();
  const [pw, setPw] = React.useState('');
  const [err, setErr] = React.useState('');
  const submit = (e) => {
    e.preventDefault();
    if (!pw) return setErr('Bitte Admin-Passwort eingeben.');
    if (pw !== settings.adminPassword) return setErr('Passwort ist falsch.');
    sessionStorage.setItem('aw.adminAuthed', '1');
    onAuthed();
  };
  return (
    <main className="min-h-[calc(100vh-72px)] flex items-center justify-center px-5 py-12 bg-cream">
      <form onSubmit={submit} className="w-full max-w-md bg-paper rounded-3xl ring-2 ring-line shadow-card p-8">
        <div className="flex items-center gap-3 mb-6">
          <span className="inline-flex w-12 h-12 rounded-2xl bg-ink text-white items-center justify-center">
            <IconShield size={22}/>
          </span>
          <div>
            <div className="text-[12px] uppercase tracking-wider font-bold text-inkSoft">Geschützter Bereich</div>
            <h1 className="font-display text-2xl font-extrabold leading-tight">Admin-Anmeldung</h1>
          </div>
        </div>
        <p className="text-sm text-inkSoft mb-5 leading-relaxed">
          Bitte gib das Admin-Passwort ein, um den Verwaltungsbereich zu öffnen.
        </p>
        <Field label="Admin-Passwort">
          <input type="password" value={pw} onChange={(e) => setPw(e.target.value)} autoFocus
            className="w-full px-4 py-3 rounded-xl bg-cream ring-1 ring-line focus:ring-2 focus:ring-coral outline-none"/>
        </Field>
        {err && <div className="mt-3 text-[13px] text-coralDeep bg-coral/10 ring-1 ring-coral/30 rounded-xl px-3 py-2">{err}</div>}
        <div className="mt-5 flex items-center justify-between gap-3">
          <button type="button" onClick={() => navigate({ view: 'home' })}
            className="text-sm font-bold text-inkSoft hover:text-ink">Zurück zur Webseite</button>
          <Btn type="submit" leading={<IconShield size={16}/>}>Anmelden</Btn>
        </div>
      </form>
    </main>
  );
};

// === Wrapper: requires admin auth in session storage ===
const RequireAdmin = ({ navigate, children }) => {
  const [authed, setAuthed] = React.useState(() => sessionStorage.getItem('aw.adminAuthed') === '1');
  if (!authed) return <AdminLoginGate navigate={navigate} onAuthed={() => setAuthed(true)}/>;
  return children;
};

// === Admin shell with sidebar ===
const AdminShell = ({ current, navigate, children }) => {
  const items = [
    { id: 'adminDashboard',  label: 'Dashboard',         icon: <IconHome size={18}/>,    target: { view:'adminDashboard' } },
    { id: 'adminColorings',  label: 'Ausmalbilder',      icon: <IconImage size={18}/>,   target: { view:'adminColorings' } },
    { id: 'adminUpload',     label: 'Neues Bild',        icon: <IconPlus size={18}/>,    target: { view:'adminUpload' } },
    { id: 'adminCategories', label: 'Kategorien',        icon: <IconGrid size={18}/>,    target: { view:'adminCategories' } },
    { id: 'adminProfiles',   label: 'Profile',           icon: <IconUser size={18}/>,    target: { view:'adminProfiles' } },
    { id: 'adminStats',      label: 'Statistiken',       icon: <IconChart size={18}/>,   target: { view:'adminStats' } },
    { id: 'adminPages',      label: 'Unterseiten',       icon: <IconEdit size={18}/>,    target: { view:'adminSettings', tab:'staticpages' } },
    { id: 'adminSettings',   label: 'Einstellungen',     icon: <IconSettings size={18}/>,target: { view:'adminSettings' } },
  ];
  return (
    <div className="bg-cream min-h-[calc(100vh-72px)]">
      <div className="max-w-7xl mx-auto px-5 sm:px-8 py-8 grid lg:grid-cols-[240px,minmax(0,1fr)] gap-6">
        <aside className="hidden lg:block">
          <div className="sticky top-24 bg-paper ring-1 ring-line rounded-xl3 p-3">
            <div className="px-3 py-3 mb-2 flex items-center gap-2 border-b border-line">
              <span className="inline-flex w-9 h-9 rounded-xl bg-ink text-white items-center justify-center">
                <IconShield size={18}/>
              </span>
              <div>
                <div className="text-[11px] uppercase tracking-wider text-inkSoft font-bold">Admin</div>
                <div className="font-display font-bold leading-tight">Redaktion</div>
              </div>
            </div>
            <nav className="grid gap-0.5">
              {items.map(it => (
                <button key={it.id} onClick={() => navigate(it.target)}
                  className={cls("flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-semibold transition",
                    current === it.id ? 'bg-ink text-white' : 'text-ink hover:bg-cream')}>
                  <span className={cls("inline-flex w-7 h-7 rounded-lg items-center justify-center",
                    current === it.id ? 'bg-white/15' : 'bg-cream')}>{it.icon}</span>
                  {it.label}
                </button>
              ))}
              <button onClick={() => { sessionStorage.removeItem('aw.adminAuthed'); navigate({ view: 'home' }); }}
                className="mt-2 flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-semibold text-coralDeep hover:bg-coral/10">
                <span className="inline-flex w-7 h-7 rounded-lg bg-cream items-center justify-center"><IconClose size={16}/></span>
                Admin abmelden
              </button>
            </nav>
          </div>
        </aside>
        <section className="min-w-0">{children}</section>
      </div>
    </div>
  );
};

// === Reusable bits ===
const Card = ({ title, subtitle, children, action }) => (
  <section className="bg-paper ring-1 ring-line rounded-xl3 p-5 sm:p-6">
    {(title || action) && <div className="mb-4 flex items-start justify-between gap-3">
      <div>
        {title && <h3 className="font-display text-lg font-bold leading-tight">{title}</h3>}
        {subtitle && <p className="text-[13px] text-inkSoft mt-0.5">{subtitle}</p>}
      </div>
      {action}
    </div>}
    {children}
  </section>
);

const Field = ({ label, hint, children }) => (
  <label className="block">
    <div className="flex items-center justify-between mb-1.5">
      <span className="text-[13px] font-semibold">{label}</span>
      {hint && <span className="text-[11px] text-inkSoft">{hint}</span>}
    </div>
    {children}
  </label>
);

const Input = ({ value, onChange, placeholder, prefix, compact, type = 'text' }) => (
  <div className={cls("flex items-center bg-paper ring-1 ring-line rounded-xl px-3 focus-within:ring-2 focus-within:ring-coral",
    compact ? 'py-1.5' : 'py-1')}>
    {prefix && <span className="text-[12px] text-inkSoft pr-1.5">{prefix}</span>}
    <input type={type} value={value} onChange={(e)=>onChange(e.target.value)} placeholder={placeholder}
      className={cls("flex-1 bg-transparent outline-none", compact ? 'py-1.5 text-sm' : 'py-2.5')}/>
  </div>
);

const FileDrop = ({ label, hint, file, onChange, icon, preview }) => {
  const ref = React.useRef();
  return (
    <div>
      <div className="text-[13px] font-semibold mb-1.5">{label}</div>
      <button type="button" onClick={() => ref.current?.click()}
        className="w-full flex flex-col items-center justify-center gap-2 px-4 py-6 rounded-xl border-2 border-dashed border-line bg-cream/50 hover:border-coral hover:bg-coral/5 transition text-center">
        {preview || <span className="inline-flex w-12 h-12 rounded-full bg-paper items-center justify-center text-inkSoft">{icon}</span>}
        <div className="text-sm font-semibold">{file ? file.name : 'Datei auswählen oder ablegen'}</div>
        <div className="text-[11px] text-inkSoft">{hint}</div>
      </button>
      <input ref={ref} type="file" className="hidden" onChange={(e) => onChange(e.target.files?.[0] || null)}/>
    </div>
  );
};

// Cover image upload control, reads as data URL inline.
const CoverUpload = ({ label, hint, value, onChange, onClear }) => {
  const ref = React.useRef();
  const pick = async (file) => {
    if (!file) return;
    if (file.size > 1500_000) { alert('Bild zu groß (max 1,5 MB).'); return; }
    const reader = new FileReader();
    reader.onload = () => onChange(reader.result);
    reader.readAsDataURL(file);
  };
  return (
    <div>
      <div className="text-[13px] font-semibold mb-1.5">{label}</div>
      <div className="flex items-stretch gap-3">
        <div className="w-32 h-24 rounded-xl ring-1 ring-line bg-cream overflow-hidden flex items-center justify-center shrink-0">
          {value
            ? <img src={value} alt="" className="w-full h-full object-cover"/>
            : <span className="text-inkSoft text-[11px] text-center px-2">Keine Bild­vorschau</span>}
        </div>
        <div className="flex-1 grid gap-2 min-w-0">
          <button type="button" onClick={() => ref.current?.click()}
            className="w-full flex items-center justify-center gap-2 px-3 py-2.5 rounded-xl ring-2 border-2 border-dashed border-line bg-paper hover:border-coral hover:bg-coral/5 transition text-sm font-semibold">
            <IconUpload size={16}/> {value ? 'Bild ersetzen' : 'Bild hochladen'}
          </button>
          {value && (
            <button type="button" onClick={onClear}
              className="text-[12px] font-bold text-coralDeep hover:underline">Bild entfernen</button>
          )}
          {hint && <div className="text-[11px] text-inkSoft">{hint}</div>}
        </div>
      </div>
      <input ref={ref} type="file" className="hidden" accept="image/*" onChange={(e) => pick(e.target.files?.[0])}/>
    </div>
  );
};

// Inline cover-picker button (icon-only, for tables)
const CoverPickerButton = ({ onPick, hasCover }) => {
  const ref = React.useRef();
  return (
    <>
      <button onClick={() => ref.current?.click()}
        className={cls("p-2 rounded-lg", hasCover ? 'bg-sky/30 text-sky-900' : 'hover:bg-cream')}
        title={hasCover ? 'Bild ändern' : 'Container-Bild hochladen'}>
        <IconImage size={16}/>
      </button>
      <input ref={ref} type="file" className="hidden" accept="image/*"
        onChange={(e) => onPick(e.target.files?.[0])}/>
    </>
  );
};

const Toggle = ({ checked, onChange, label, sub }) => (
  <label className="flex items-center justify-between gap-4 px-4 py-3 rounded-xl ring-1 ring-line bg-paper cursor-pointer hover:ring-ink/30 transition">
    <div className="min-w-0">
      <div className="text-sm font-semibold leading-tight">{label}</div>
      {sub && <div className="text-[12px] text-inkSoft mt-0.5">{sub}</div>}
    </div>
    <button type="button" role="switch" aria-checked={checked}
      onClick={() => onChange(!checked)}
      className={cls("relative w-12 h-7 rounded-full transition shrink-0",
        checked ? 'bg-coral' : 'bg-ink/15')}>
      <span className={cls("absolute top-1 left-1 w-5 h-5 bg-white rounded-full shadow transition",
        checked && 'translate-x-5')}/>
    </button>
  </label>
);

// === Dashboard ===
const AdminDashboardPage = ({ navigate }) => {
  const top = [...COLORINGS].sort((a,b)=>b.points-a.points)[0];
  const recent = [...COLORINGS].slice(-6).reverse();

  // Real-data stats derived from local sources
  const totalImages    = COLORINGS.length;
  const totalDownloads = COLORINGS.reduce((s,c) => s + (c.downloads||0), 0);
  const totalPrints    = COLORINGS.reduce((s,c) => s + (c.prints||0), 0);

  const [profilesMap, setProfilesMap] = React.useState(null);
  React.useEffect(() => {
    let alive = true;
    awLoadProfiles().then(({ map }) => { if (alive) setProfilesMap(map); });
    return () => { alive = false; };
  }, []);
  const _pm = profilesMap || {};
  const profilesCount = Object.keys(_pm).length;
  const premiumCount  = Object.values(_pm).filter(c => c.premium && c.premium.active).length;
  let commentsCount = 0;
  try {
    const all = JSON.parse(localStorage.getItem('aw.comments') || '{}');
    commentsCount = Object.values(all).reduce((s, arr) => s + (arr || []).length, 0);
  } catch {}

  return (
    <RequireAdmin navigate={navigate}>
    <AdminShell current="adminDashboard" navigate={navigate}>
      <div className="flex items-end justify-between gap-4 mb-6 flex-wrap">
        <div>
          <div className="text-[12px] uppercase tracking-wider font-bold text-inkSoft">Übersicht</div>
          <h1 className="font-display text-4xl font-extrabold tracking-tight">Dashboard</h1>
        </div>
        <div className="flex items-center gap-2">
          <Btn variant="soft" size="sm" leading={<IconBell size={16}/>}>Benachrichtigungen</Btn>
          <Btn size="sm" leading={<IconPlus size={16}/>} onClick={() => navigate({view:'adminUpload'})}>Neues Bild</Btn>
        </div>
      </div>

      <div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-4">
        <StatCard label="Ausmalbilder gesamt" value={totalImages.toLocaleString('de-DE')}
          sub={`${CATEGORIES.length} Kategorien`}
          icon={<IconImage size={16}/>} tone="sky"/>
        <StatCard label="Downloads gesamt" value={totalDownloads.toLocaleString('de-DE')}
          sub="Lifetime aller Bilder"
          icon={<IconDownload size={16}/>} tone="mint"/>
        <StatCard label="Druckvorgänge gesamt" value={totalPrints.toLocaleString('de-DE')}
          sub="Lifetime aller Bilder"
          icon={<IconPrinter size={16}/>} tone="sun"/>
        <StatCard label="Beliebtestes Bild" value={top ? (top.title.length > 14 ? top.title.slice(0, 13) + '…' : top.title) : '-'}
          sub={top ? `${top.points.toLocaleString('de-DE')} Punkte` : 'Noch keine Bilder'} icon={<IconTrophy size={16}/>} tone="coral"/>
      </div>

      <div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-4 mt-4">
        <StatCard label="Registrierte Profile" value={profilesCount.toLocaleString('de-DE')}
          sub={`${premiumCount} davon Premium`}
          icon={<IconUser size={16}/>} tone="lilac"/>
        <StatCard label="Kommentare" value={commentsCount.toLocaleString('de-DE')}
          sub="auf allen Bildern"
          icon={<IconBell size={16}/>} tone="sky"/>
        <StatCard label="Premium-Mitglieder" value={premiumCount.toLocaleString('de-DE')}
          sub={profilesCount ? `${Math.round(premiumCount/profilesCount*100)} % aller Profile` : '-'}
          icon={<IconStar size={16}/>} tone="sun"/>
        <StatCard label="Aktive Kategorien" value={CATEGORIES.filter(c => c.count > 0).length}
          sub={`${CATEGORIES.length} insgesamt`}
          icon={<IconGrid size={16}/>} tone="mint"/>
      </div>

      <div className="mt-6 bg-paper ring-1 ring-line rounded-xl3 overflow-hidden min-w-0">
        {/* Recent uploads, full width */}
        <div className="flex items-center justify-between px-5 py-4 border-b border-line">
          <h3 className="font-display text-lg font-bold">Zuletzt hochgeladen</h3>
          <Btn variant="ghost" size="sm" trailing={<IconArrowR size={14}/>}
            onClick={() => navigate({view:'adminColorings'})}>Alle ansehen</Btn>
        </div>
        <div className="overflow-x-auto scroll-soft">
            <table className="w-full text-sm">
              <thead className="bg-cream text-[12px] uppercase tracking-wider text-inkSoft">
                <tr>
                  <th className="text-left font-bold px-5 py-3 whitespace-nowrap">Titel</th>
                  <th className="text-left font-bold px-3 py-3 whitespace-nowrap">Kategorie</th>
                  <th className="text-left font-bold px-3 py-3 whitespace-nowrap">Status</th>
                  <th className="text-right font-bold px-3 py-3 whitespace-nowrap">Punkte</th>
                  <th className="text-right font-bold px-5 py-3 whitespace-nowrap">Aktionen</th>
                </tr>
              </thead>
              <tbody>
                {recent.map((c, i) => {
                  const cat = findCategory(c.category);
                  const tone = TONE_CLASSES[cat.tone];
                  return (
                    <tr key={c.id} className={cls(i !== 0 && 'border-t border-line')}>
                      <td className="px-5 py-3">
                        <div className="flex items-center gap-3">
                          <div className={cls("w-10 h-10 rounded-lg flex items-center justify-center shrink-0", tone.soft)}>
                            <ColoringArt artKey={c.art} className="w-7 h-7"/>
                          </div>
                          <div className="min-w-0">
                            <div className="font-semibold leading-tight truncate">{c.title}</div>
                            <div className="text-[11px] text-inkSoft truncate">/{c.slug}</div>
                          </div>
                        </div>
                      </td>
                      <td className="px-3 py-3 whitespace-nowrap">
                        <span className={cls("inline-flex items-center px-2 py-0.5 rounded-full text-[11px] font-semibold", tone.soft, tone.text)}>{cat.name}</span>
                      </td>
                      <td className="px-3 py-3 whitespace-nowrap">
                        <span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-mint/40 text-emerald-800 text-[11px] font-semibold">
                          <span className="w-1.5 h-1.5 rounded-full bg-emerald-600"></span>Aktiv
                        </span>
                      </td>
                      <td className="px-3 py-3 text-right font-semibold whitespace-nowrap">{c.points.toLocaleString('de-DE')}</td>
                      <td className="px-5 py-3 text-right whitespace-nowrap">
                        <div className="inline-flex items-center gap-1">
                          <button className="p-2 rounded-lg hover:bg-cream"><IconEye size={16}/></button>
                          <button className="p-2 rounded-lg hover:bg-cream"><IconEdit size={16}/></button>
                        </div>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </div>

      {/* Top diesen Monat, own row with side-by-side items + Trend card */}
      <div className="grid lg:grid-cols-3 gap-5 mt-6">
        <div className="lg:col-span-2 bg-paper ring-1 ring-line rounded-xl3 p-5 min-w-0">
          <div className="flex items-center justify-between mb-4">
            <h3 className="font-display text-lg font-bold">Top diesen Monat</h3>
            <span className="inline-flex items-center gap-1.5 text-[11px] uppercase tracking-wider text-emerald-700 font-bold">
              <span className="w-2 h-2 rounded-full bg-emerald-500 heart-beat"></span>Live
            </span>
          </div>
          <div className="grid sm:grid-cols-2 gap-3">
            {[...COLORINGS].sort((a,b)=>b.points-a.points).slice(0,6).map((c, idx) => {
              const cat = findCategory(c.category);
              const tone = TONE_CLASSES[cat.tone];
              return (
                <button key={c.id} onClick={() => navigate({ view: 'detail', slug: c.slug })}
                  className="w-full text-left flex items-center gap-3 p-2 rounded-xl hover:bg-cream hover:ring-1 hover:ring-coral transition group">
                  <RankBadge rank={idx+1}/>
                  <div className={cls("w-12 h-12 rounded-lg flex items-center justify-center shrink-0", tone.soft)}>
                    <ColoringArt artKey={c.art} className="w-8 h-8"/>
                  </div>
                  <div className="flex-1 min-w-0">
                    <div className="font-semibold leading-tight truncate text-sm group-hover:text-coralDeep transition">{c.title}</div>
                    <div className="text-[11px] text-inkSoft">{cat.name}</div>
                  </div>
                  <div className="font-display text-sm font-bold inline-flex items-center gap-1 shrink-0">
                    <IconStar size={13} className="text-sunDeep"/>{(c.points/1000).toFixed(1)}k
                  </div>
                  <IconChevronR size={14} className="text-inkSoft opacity-0 group-hover:opacity-100 transition"/>
                </button>
              );
            })}
          </div>
        </div>

        <div className="bg-paper ring-1 ring-line rounded-xl3 p-5 min-w-0">
          <div className="text-[12px] uppercase tracking-wider font-bold text-coralDeep mb-2">Top-Kategorie nach Punkten</div>
          {(() => {
            const catTotals = {};
            COLORINGS.forEach(c => { catTotals[c.category] = (catTotals[c.category] || 0) + (c.points || 0); });
            const sorted = Object.entries(catTotals).sort((a,b) => b[1] - a[1]);
            const [topSlug, topPts] = sorted[0] || ['', 0];
            const topCat = CATEGORIES.find(c => c.slug === topSlug);
            return (
              <>
                <div className="font-display text-2xl font-extrabold leading-tight mb-1">
                  {topCat ? topCat.name : '-'}
                </div>
                <div className="text-[13px] text-inkSoft mb-3">
                  {topPts.toLocaleString('de-DE')} Punkte über {topCat ? topCat.count : 0} Bilder
                </div>
                <div className="rounded-2xl bg-cream p-4">
                  <div className="text-[11px] uppercase tracking-wider font-bold text-inkSoft mb-1">Ranking</div>
                  <ol className="text-sm grid gap-1">
                    {sorted.slice(0, 5).map(([slug, pts], i) => {
                      const c = CATEGORIES.find(x => x.slug === slug);
                      return (
                        <li key={slug} className="flex items-center justify-between gap-2">
                          <span className="font-semibold">{i+1}. {c ? c.name : slug}</span>
                          <span className="text-inkSoft text-[12px]">{pts.toLocaleString('de-DE')}</span>
                        </li>
                      );
                    })}
                  </ol>
                </div>
              </>
            );
          })()}
        </div>
      </div>

      <div className="mt-6 bg-paper ring-1 ring-line rounded-xl3 p-5">
        <div className="flex items-center justify-between mb-4">
          <h3 className="font-display text-lg font-bold">Punkte pro Bild</h3>
          <span className="text-[11px] uppercase tracking-wider text-inkSoft font-bold">Top 14</span>
        </div>
        <div className="grid gap-1.5 h-44" style={{ gridTemplateColumns: 'repeat(14, minmax(0, 1fr))' }}>
          {(() => {
            const sorted = [...COLORINGS].sort((a,b) => b.points - a.points).slice(0, 14);
            const max = Math.max(1, sorted[0]?.points || 1);
            return sorted.map((c, i) => (
              <div key={c.id} className="flex flex-col h-full min-w-0" title={`${c.title}: ${c.points.toLocaleString('de-DE')} Punkte`}>
                <div className="flex-1 flex items-end">
                  <div className="w-full rounded-t-md bg-coral/80 hover:bg-coral transition" style={{height: `${(c.points / max) * 100}%`}}></div>
                </div>
                <div className="text-[10px] text-inkSoft text-center mt-1.5">{i+1}</div>
              </div>
            ));
          })()}
        </div>
      </div>
    </AdminShell>
    </RequireAdmin>
  );
};

// === Upload ===
const AdminUploadPage = ({ navigate }) => {
  const [form, setForm] = React.useState({
    title: '', slug: '', description: '', category: '', tags: '',
    metaTitle: '', metaDesc: '', status: 'aktiv', format: 'auto',
  });
  const [imgFile, setImgFile] = React.useState(null);
  const [pdfFile, setPdfFile] = React.useState(null);
  const [imgData, setImgData] = React.useState('');
  const [pdfData, setPdfData] = React.useState('');
  const [saved, setSaved] = React.useState(false);
  const [err, setErr] = React.useState('');

  const cats = getCategoriesWithOverrides();
  const slugify = (s) => s.toLowerCase().replace(/[äÄ]/g,'ae').replace(/[öÖ]/g,'oe').replace(/[üÜ]/g,'ue').replace(/ß/g,'ss').replace(/[^a-z0-9]+/g,'-').replace(/(^-|-$)/g,'');
  const setF = (k, v) => setForm(s => ({ ...s, [k]: v, ...(k === 'title' && !s.slug.length ? { slug: slugify(v) } : {}) }));

  const readDataURL = (file) => new Promise((res, rej) => { const r = new FileReader(); r.onload = () => res(r.result); r.onerror = rej; r.readAsDataURL(file); });
  const onImg = async (f) => { setImgFile(f); setImgData(f ? await readDataURL(f) : ''); };
  const onPdf = async (f) => { setPdfFile(f); setPdfData(f ? await readDataURL(f) : ''); };

  const submit = (e) => {
    e.preventDefault();
    setErr('');
    if (!form.title.trim()) return setErr('Bitte gib einen Titel ein.');
    if (!form.category) return setErr('Bitte wähle eine Kategorie (lege zuerst eine an, falls keine vorhanden ist).');
    if (!imgData) return setErr('Bitte lade ein Vorschaubild hoch.');
    createColoring({
      title: form.title.trim(), slug: form.slug || slugify(form.title), category: form.category,
      tags: form.tags.split(',').map(t => t.trim()).filter(Boolean), previewUrl: imgData, pdfUrl: pdfData,
      status: form.status, metaTitle: form.metaTitle, metaDesc: form.metaDesc, isNew: true,
    });
    setSaved(true);
    setForm({ title: '', slug: '', description: '', category: form.category, tags: '', metaTitle: '', metaDesc: '', status: 'aktiv', format: 'auto' });
    setImgFile(null); setImgData(''); setPdfFile(null); setPdfData('');
    setTimeout(() => setSaved(false), 2800);
  };

  return (
    <RequireAdmin navigate={navigate}>
    <AdminShell current="adminUpload" navigate={navigate}>
      <div className="mb-6">
        <Crumbs items={[{label:'Admin', target:{view:'adminDashboard'}},{label:'Neues Ausmalbild'}]} navigate={navigate}/>
        <h1 className="mt-2 font-display text-4xl font-extrabold tracking-tight">Neues Ausmalbild hochladen</h1>
        <p className="text-inkSoft mt-2 max-w-2xl">Vorschau und Druckdatei hochladen, Metadaten ausfüllen, fertig. Felder mit * sind Pflicht.</p>
      </div>

      <form onSubmit={submit} className="grid lg:grid-cols-3 gap-5">
        <div className="lg:col-span-2 grid gap-5 min-w-0">
          <Card title="Dateien" subtitle="Vorschau (PNG/JPG) und Druckdatei (PDF).">
            <div className="grid sm:grid-cols-2 gap-4">
              <FileDrop label="Vorschaubild" hint="PNG oder JPG · max 5 MB"
                file={imgFile} onChange={onImg} icon={<IconImage size={20}/>}
                preview={imgData ? <img src={imgData} alt="" className="w-20 h-20 object-contain"/> : null}/>
              <FileDrop label="Druckdatei (PDF)" hint="A4, 300 dpi · max 20 MB"
                file={pdfFile} onChange={onPdf} icon={<IconPrinter size={20}/>}/>
            </div>
          </Card>

          <Card title="Druckformat" subtitle="Wie soll das Bild auf DIN A4 gedruckt werden?">
            <div className="grid sm:grid-cols-3 gap-3">
              {[
                { v:'auto', l:'Automatisch', d:'Größtmöglich aufs Blatt', icon:'✨' },
                { v:'portrait', l:'Hochkant 9:16', d:'A4 portrait, volle Höhe', icon:'📄' },
                { v:'landscape', l:'Quer 16:9', d:'A4 quer, volle Breite', icon:'🖼️' },
              ].map(o => (
                <button key={o.v} type="button" onClick={() => setF('format', o.v)}
                  className={cls("text-left p-4 rounded-2xl ring-2 transition",
                    form.format === o.v ? 'ring-coral bg-coral/5' : 'ring-line bg-paper hover:ring-ink/30')}>
                  <div className="flex items-center justify-between mb-2">
                    <span className="text-2xl" aria-hidden="true">{o.icon}</span>
                    {/* mini paper shape */}
                    <span className={cls("bg-cream ring-1 ring-line block",
                      o.v === 'landscape' ? 'w-9 h-6' : o.v === 'portrait' ? 'w-6 h-9' : 'w-7 h-8')}
                      style={{ borderRadius: 3 }}></span>
                  </div>
                  <div className="font-display font-extrabold text-sm leading-tight">{o.l}</div>
                  <div className="text-[12px] text-inkSoft mt-0.5">{o.d}</div>
                  {form.format === o.v && <div className="mt-2 inline-flex items-center gap-1 text-[11px] font-bold text-coralDeep"><IconCheck size={12}/>gewählt</div>}
                </button>
              ))}
            </div>
            {form.format === 'auto' && (
              <div className="mt-3 p-3 rounded-xl bg-sky/30 ring-1 ring-sky text-[12px] leading-relaxed">
                <strong>Automatisch:</strong> Das System erkennt das Seitenverhältnis des hochgeladenen Bildes und
                skaliert es so, dass es größtmöglich auf ein A4-Blatt passt, Hochformat-Bilder werden hochkant,
                Querformat-Bilder quer gedruckt. Ränder werden gleichmäßig verteilt.
              </div>
            )}
          </Card>

          <Card title="Grunddaten">
            <div className="grid sm:grid-cols-2 gap-4">
              <Field label="Titel*"><Input value={form.title} onChange={(v) => setF('title', v)} placeholder="z. B. Einhorn mit Regenbogen"/></Field>
              <Field label="Slug (URL)" hint="Wird automatisch erzeugt."><Input value={form.slug} onChange={(v) => setF('slug', v)} placeholder="einhorn-mit-regenbogen" prefix="/ausmalbild/"/></Field>
              <div className="sm:col-span-2">
                <Field label="Beschreibung">
                  <textarea value={form.description} onChange={(e)=>setF('description', e.target.value)} rows="4"
                    className="w-full bg-paper ring-1 ring-line rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-coral resize-none"/>
                </Field>
              </div>
              <Field label="Kategorie*">
                {cats.length === 0 ? (
                  <div className="rounded-xl bg-sun/30 ring-1 ring-sun text-[13px] px-4 py-3 leading-relaxed">
                    Es gibt noch keine Kategorie. Lege zuerst unter <button type="button" className="font-bold underline" onClick={() => navigate({ view: 'adminCategories' })}>Kategorien</button> eine an.
                  </div>
                ) : (
                  <select value={form.category} onChange={(e)=>setF('category', e.target.value)}
                    className="w-full bg-paper ring-1 ring-line rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-coral">
                    <option value="" disabled>Kategorie wählen …</option>
                    {cats.map(c => <option key={c.slug} value={c.slug}>{c.name}</option>)}
                  </select>
                )}
              </Field>
              <Field label="Tags" hint="Komma-getrennt."><Input value={form.tags} onChange={(v) => setF('tags', v)} placeholder="einfach, magisch, kinder"/></Field>
            </div>
          </Card>

          <Card title="SEO" subtitle="Diese Felder steuern den Eintrag in Google.">
            <div className="grid gap-4">
              <Field label="Meta Title" hint={`${form.metaTitle.length}/60 Zeichen`}><Input value={form.metaTitle} onChange={(v) => setF('metaTitle', v)} placeholder="Einhorn Ausmalbild, kostenlos zum Ausdrucken"/></Field>
              <Field label="Meta Description" hint={`${form.metaDesc.length}/160 Zeichen`}>
                <textarea value={form.metaDesc} onChange={(e)=>setF('metaDesc', e.target.value)} rows="3"
                  className="w-full bg-paper ring-1 ring-line rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-coral resize-none"/>
              </Field>
              <div className="rounded-xl bg-cream p-4 ring-1 ring-line">
                <div className="text-[11px] uppercase tracking-wider text-inkSoft font-bold mb-2">Google-Vorschau</div>
                <div className="text-[13px] text-emerald-800">malfino.de › ausmalbild › {form.slug || 'einhorn'}</div>
                <div className="text-[#1a0dab] text-[18px] font-medium leading-snug mt-0.5">{form.metaTitle || form.title || 'Titel des Ausmalbildes'}</div>
                <div className="text-[13px] text-[#4d5156] mt-1 leading-snug">{form.metaDesc || 'Kurzer Beschreibungstext erscheint hier.'}</div>
              </div>
            </div>
          </Card>
        </div>

        <div className="grid gap-5 content-start">
          <Card title="Status">
            <div className="grid gap-2">
              {[{v:'aktiv', l:'Aktiv', d:'Sofort sichtbar'},{v:'inaktiv', l:'Inaktiv', d:'Nur intern'}].map(o => (
                <label key={o.v} className={cls("flex items-center gap-3 p-3 rounded-xl ring-1 cursor-pointer transition",
                  form.status === o.v ? 'ring-coral bg-coral/5' : 'ring-line hover:ring-ink/30 bg-paper')}>
                  <input type="radio" checked={form.status === o.v} onChange={() => setF('status', o.v)} className="accent-coralDeep"/>
                  <div className="flex-1">
                    <div className="font-semibold text-sm">{o.l}</div>
                    <div className="text-[12px] text-inkSoft">{o.d}</div>
                  </div>
                  {form.status === o.v && <IconCheck size={16} className="text-coralDeep"/>}
                </label>
              ))}
            </div>
          </Card>

          <Card title="Vorschau">
            <div className="paper-bg rounded-xl p-3 aspect-[4/3] flex items-center justify-center ring-1 ring-line">
              {imgData ? <img src={imgData} alt="" className="max-w-[78%] max-h-[82%] object-contain"/>
                       : <div className="text-center text-inkSoft text-sm"><IconImage size={32} className="mx-auto mb-2"/>Noch kein Bild</div>}
            </div>
            <div className="mt-3 text-sm">
              <div className="font-display text-lg font-bold leading-tight truncate">{form.title || 'Unbenanntes Ausmalbild'}</div>
              <div className="text-[12px] text-inkSoft truncate">/{form.slug || 'slug'}</div>
            </div>
          </Card>

          <div className="grid gap-2">
            <Btn size="lg" type="submit" leading={<IconCheck size={18}/>}>Speichern</Btn>
            <Btn size="md" variant="soft" type="button" onClick={() => setForm(s => ({ ...s, status: 'inaktiv' }))}>Als Entwurf markieren</Btn>
          </div>
          {err && (
            <div className="p-3 rounded-xl bg-coral/15 ring-1 ring-coral text-coralDeep text-[13px] font-semibold">{err}</div>
          )}
          {saved && (
            <div className="p-4 rounded-xl bg-mint/40 ring-1 ring-mint text-emerald-900 text-sm font-semibold inline-flex items-center gap-2">
              <IconCheck size={18}/> Gespeichert! Das Bild ist jetzt im Katalog.
            </div>
          )}
        </div>
      </form>
    </AdminShell>
    </RequireAdmin>
  );
};

// === Categories admin ===
const AdminCategoriesPage = ({ navigate }) => {
  const [list, setList] = React.useState(() => getCategoriesWithOverrides());
  const [edit, setEdit] = React.useState(null);
  const [showAdd, setShowAdd] = React.useState(false);
  const [newCat, setNewCat] = React.useState({ name: '', slug: '', description: '', parent: '', coverImage: '' });
  const [delTarget, setDelTarget] = React.useState(null); // category pending deletion
  const [moveTo, setMoveTo] = React.useState('');
  const slugify = (s) => s.toLowerCase().replace(/[äÄ]/g,'ae').replace(/[öÖ]/g,'oe').replace(/[üÜ]/g,'ue').replace(/ß/g,'ss').replace(/[^a-z0-9]+/g,'-').replace(/(^-|-$)/g,'');
  const addCat = () => {
    if (!newCat.name) return;
    createCategory({ name: newCat.name, slug: newCat.slug, description: newCat.description, parent: newCat.parent, coverImage: newCat.coverImage });
    setList(getCategoriesWithOverrides());
    setNewCat({ name: '', slug: '', description: '', parent: '', coverImage: '' });
    setShowAdd(false);
  };
  const saveEdit = (slug) => {
    const c = list.find(x => x.slug === slug);
    if (c) updateCategory(slug, { name: c.name });
    setEdit(null);
  };

  const readFileAsDataURL = (file) => new Promise((resolve, reject) => {
    const r = new FileReader();
    r.onload = () => resolve(r.result);
    r.onerror = reject;
    r.readAsDataURL(file);
  });
  const onCoverPicked = async (slug, file) => {
    if (!file) return;
    if (file.size > 1500_000) { alert('Bild zu groß (max 1,5 MB).'); return; }
    const data = await readFileAsDataURL(file);
    setList(L => L.map(c => c.slug === slug ? { ...c, coverImage: data } : c));
    saveCategoryOverride(slug, { coverImage: data });
  };
  const clearCover = (slug) => {
    setList(L => L.map(c => c.slug === slug ? { ...c, coverImage: '' } : c));
    saveCategoryOverride(slug, { coverImage: '' });
  };

  // Keep the list in sync with the store (covers add/delete/edit + backend hydration).
  React.useEffect(() => {
    const h = () => setList(getCategoriesWithOverrides());
    window.addEventListener('aw:data-changed', h);
    return () => window.removeEventListener('aw:data-changed', h);
  }, []);
  return (
    <RequireAdmin navigate={navigate}>
    <AdminShell current="adminCategories" navigate={navigate}>
      <div className="flex items-end justify-between gap-4 mb-6 flex-wrap">
        <div>
          <Crumbs items={[{label:'Admin', target:{view:'adminDashboard'}},{label:'Kategorien'}]} navigate={navigate}/>
          <h1 className="mt-2 font-display text-4xl font-extrabold tracking-tight">Kategorien</h1>
          <p className="text-inkSoft mt-2">{list.length} Kategorien · zusammen {list.reduce((s,c)=>s+c.count,0)} Bilder</p>
        </div>
        <Btn size="md" leading={<IconPlus size={18}/>} onClick={() => setShowAdd(s => !s)}>Hinzufügen</Btn>
      </div>

      {showAdd && (
        <Card title="Neue Kategorie">
          <div className="grid sm:grid-cols-2 gap-4">
            <Field label="Name*"><Input value={newCat.name} onChange={(v) => setNewCat(c => ({ ...c, name: v, slug: c.slug || slugify(v) }))} placeholder="z. B. Meerestiere"/></Field>
            <Field label="Slug"><Input value={newCat.slug} onChange={(v) => setNewCat(c => ({ ...c, slug: v }))} prefix="/kategorie/" placeholder="meerestiere"/></Field>
            <div className="sm:col-span-2">
              <Field label="Beschreibung">
                <textarea value={newCat.description} onChange={(e) => setNewCat(c => ({...c, description: e.target.value}))} rows="3"
                  className="w-full bg-paper ring-1 ring-line rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-coral resize-none"/>
              </Field>
            </div>
            <div className="sm:col-span-2">
              <CoverUpload label="Container-Bild (optional)" hint="Erscheint groß auf der Kategorienkarte. JPG/PNG, max 1,5 MB."
                value={newCat.coverImage}
                onChange={(data) => setNewCat(c => ({ ...c, coverImage: data }))}
                onClear={() => setNewCat(c => ({ ...c, coverImage: '' }))}/>
            </div>
          </div>
          <div className="mt-4 flex items-center justify-end gap-2">
            <Btn variant="soft" size="sm" onClick={() => setShowAdd(false)}>Abbrechen</Btn>
            <Btn size="sm" leading={<IconCheck size={16}/>} onClick={addCat}>Anlegen</Btn>
          </div>
        </Card>
      )}

      <div className="bg-paper ring-1 ring-line rounded-xl3 overflow-hidden mt-5">
        <div className="overflow-x-auto scroll-soft">
          <table className="w-full text-sm">
            <thead className="bg-cream text-[12px] uppercase tracking-wider text-inkSoft">
              <tr>
                <th className="text-left font-bold px-5 py-3 whitespace-nowrap">Kategorie</th>
                <th className="text-left font-bold px-3 py-3 whitespace-nowrap">Slug</th>
                <th className="text-right font-bold px-3 py-3 whitespace-nowrap">Bilder</th>
                <th className="text-right font-bold px-5 py-3 whitespace-nowrap">Aktionen</th>
              </tr>
            </thead>
            <tbody>
              {list.map((c, i) => {
                const tone = TONE_CLASSES[c.tone];
                const isEdit = edit === c.slug;
                return (
                  <tr key={c.slug} className={cls(i !== 0 && 'border-t border-line')}>
                    <td className="px-5 py-3">
                      <div className="flex items-center gap-3">
                        <div className={cls("w-12 h-12 rounded-lg overflow-hidden flex items-center justify-center shrink-0 ring-1 ring-line", c.coverImage ? 'bg-cream' : tone.soft)}>
                          {c.coverImage
                            ? <img src={c.coverImage} alt="" className="w-full h-full object-cover"/>
                            : <ColoringArt artKey={c.art} className="w-7 h-7"/>}
                        </div>
                        <div className="min-w-0">
                          {isEdit ? (
                            <Input value={c.name} compact onChange={(v) => setList(L => L.map(x => x.slug===c.slug ? {...x, name:v} : x))}/>
                          ) : (
                            <div className="font-semibold leading-tight truncate">{c.name}</div>
                          )}
                          <div className="text-[11px] text-inkSoft">
                            {c.count} Bilder
                            {c.coverImage && <span className="ml-2 inline-flex items-center gap-1 text-emerald-700"><IconCheck size={11}/>Bild gesetzt</span>}
                          </div>
                        </div>
                      </div>
                    </td>
                    <td className="px-3 py-3 text-inkSoft text-[13px] font-mono whitespace-nowrap">/{c.slug}</td>
                    <td className="px-3 py-3 text-right font-semibold whitespace-nowrap">
                      <button onClick={() => navigate({ view: 'adminColorings', category: c.slug })}
                        className="hover:text-coralDeep transition inline-flex items-center gap-1"
                        title="Bilder dieser Kategorie ansehen">
                        {c.count}
                        <IconArrowR size={12} className="text-inkSoft"/>
                      </button>
                    </td>
                    <td className="px-5 py-3 text-right whitespace-nowrap">
                      <div className="inline-flex items-center gap-1">
                        <button onClick={() => navigate({ view: 'adminColorings', category: c.slug })}
                          className="p-2 rounded-lg hover:bg-cream" title="Bilder ansehen">
                          <IconEye size={16}/>
                        </button>
                        <CoverPickerButton onPick={(file) => onCoverPicked(c.slug, file)} hasCover={!!c.coverImage}/>
                        {c.coverImage && (
                          <button onClick={() => clearCover(c.slug)}
                            className="p-2 rounded-lg hover:bg-coral/10 text-coralDeep" title="Bild entfernen">
                            <IconClose size={16}/>
                          </button>
                        )}
                        <button onClick={() => isEdit ? saveEdit(c.slug) : setEdit(c.slug)}
                          className={cls("p-2 rounded-lg", isEdit ? 'bg-mint/40 text-emerald-800' : 'hover:bg-cream')}>
                          {isEdit ? <IconCheck size={16}/> : <IconEdit size={16}/>}
                        </button>
                        <button onClick={() => { setDelTarget(c); setMoveTo(''); }} className="p-2 rounded-lg hover:bg-coral/10 text-coralDeep">
                          <IconTrash size={16}/>
                        </button>
                      </div>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>

      {delTarget && (() => {
        const imgCount = COLORINGS.filter(x => x.category === delTarget.slug).length;
        const others = list.filter(c => c.slug !== delTarget.slug);
        const confirmDelete = () => {
          // Persist deletion; move images first if chosen.
          if (imgCount > 0 && moveTo) {
            moveImagesToCategory(delTarget.slug, moveTo);
          } else if (imgCount > 0) {
            deleteImagesByCategory(delTarget.slug);
          }
          deleteCategoryGlobal(delTarget.slug);
          setDelTarget(null);
        };
        return (
          <div className="fixed inset-0 z-50 flex items-center justify-center p-4">
            <div className="absolute inset-0 bg-ink/40 backdrop-blur-sm" onClick={() => setDelTarget(null)}></div>
            <div className="relative w-full max-w-md bg-paper rounded-3xl ring-2 ring-line shadow-2xl p-6">
              <div className="flex items-start gap-3 mb-4">
                <span className="inline-flex w-12 h-12 rounded-2xl bg-coral/15 text-coralDeep items-center justify-center"><IconTrash size={22}/></span>
                <div>
                  <h3 className="font-display text-xl font-extrabold leading-tight">Kategorie „{delTarget.name}" löschen</h3>
                  <p className="text-[13px] text-inkSoft mt-1">
                    {imgCount === 0
                      ? 'Diese Kategorie enthält keine Bilder.'
                      : `Diese Kategorie enthält ${imgCount} Bild${imgCount === 1 ? '' : 'er'}. Was soll damit passieren?`}
                  </p>
                </div>
              </div>

              {imgCount > 0 && (
                <div className="grid gap-2 mb-4">
                  <label className={cls("flex items-center gap-3 p-3 rounded-xl ring-2 cursor-pointer transition",
                    moveTo === '' ? 'ring-coral bg-coral/5' : 'ring-line hover:ring-ink/30')}>
                    <input type="radio" name="del" checked={moveTo === ''} onChange={() => setMoveTo('')} className="accent-coralDeep"/>
                    <div>
                      <div className="font-semibold text-sm">Bilder ebenfalls löschen</div>
                      <div className="text-[12px] text-inkSoft">Alle {imgCount} Bilder werden mitentfernt.</div>
                    </div>
                  </label>
                  <label className={cls("flex items-center gap-3 p-3 rounded-xl ring-2 cursor-pointer transition",
                    moveTo ? 'ring-coral bg-coral/5' : 'ring-line hover:ring-ink/30')}>
                    <input type="radio" name="del" checked={!!moveTo} onChange={() => setMoveTo(others[0]?.slug || '')} className="accent-coralDeep"/>
                    <div className="flex-1 min-w-0">
                      <div className="font-semibold text-sm">Bilder verschieben nach …</div>
                      <select value={moveTo} onChange={(e) => setMoveTo(e.target.value)}
                        onClick={(e) => e.stopPropagation()}
                        disabled={!moveTo}
                        className="mt-2 w-full bg-paper ring-1 ring-line rounded-lg px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-coral disabled:opacity-50">
                        {others.map(c => <option key={c.slug} value={c.slug}>{c.name}</option>)}
                      </select>
                    </div>
                  </label>
                </div>
              )}

              <div className="flex items-center justify-end gap-2">
                <Btn variant="soft" size="md" onClick={() => setDelTarget(null)}>Abbrechen</Btn>
                <Btn size="md" className="!bg-coralDeep !text-white" leading={<IconTrash size={16}/>}
                  onClick={confirmDelete}>
                  {imgCount > 0 && moveTo ? 'Verschieben & löschen' : 'Endgültig löschen'}
                </Btn>
              </div>
            </div>
          </div>
        );
      })()}
    </AdminShell>
    </RequireAdmin>
  );
};

// === All Colorings admin ===
const AdminColoringsPage = ({ navigate, categoryFilter }) => {
  const [list, setList] = React.useState(() => [...COLORINGS]);
  const [q, setQ] = React.useState('');
  React.useEffect(() => {
    const h = () => setList([...COLORINGS]);
    window.addEventListener('aw:data-changed', h);
    return () => window.removeEventListener('aw:data-changed', h);
  }, []);
  const [activeCat, setActiveCat] = React.useState(categoryFilter || 'alle');

  React.useEffect(() => { if (categoryFilter) setActiveCat(categoryFilter); }, [categoryFilter]);

  const filtered = list.filter(c =>
    (!q || c.title.toLowerCase().includes(q.toLowerCase())) &&
    (activeCat === 'alle' || c.category === activeCat)
  );
  const activeCatObj = activeCat !== 'alle' ? CATEGORIES.find(c => c.slug === activeCat) : null;

  return (
    <RequireAdmin navigate={navigate}>
    <AdminShell current="adminColorings" navigate={navigate}>
      <div className="flex items-end justify-between gap-4 mb-6 flex-wrap">
        <div>
          <Crumbs items={[
            {label:'Admin', target:{view:'adminDashboard'}},
            ...(activeCatObj ? [{label:'Kategorien', target:{view:'adminCategories'}}] : []),
            {label: activeCatObj ? activeCatObj.name : 'Ausmalbilder'}
          ]} navigate={navigate}/>
          <h1 className="mt-2 font-display text-4xl font-extrabold tracking-tight">
            {activeCatObj ? `Bilder in „${activeCatObj.name}"` : 'Ausmalbilder'}
          </h1>
          <p className="text-inkSoft mt-2">
            {filtered.length} von {COLORINGS.length} Einträgen{activeCat !== 'alle' && ' in dieser Kategorie'}
          </p>
        </div>
        <div className="flex items-center gap-2 w-full sm:w-auto">
          <div className="w-full sm:w-72"><Input value={q} onChange={setQ} placeholder="Suche…"/></div>
          <Btn size="md" leading={<IconPlus size={18}/>} onClick={() => navigate({view:'adminUpload'})}>Neu</Btn>
        </div>
      </div>

      {/* Category filter chips */}
      <div className="mb-5 flex flex-wrap gap-2">
        <button onClick={() => setActiveCat('alle')}
          className={cls("inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full ring-1 text-[12px] font-bold transition",
            activeCat === 'alle' ? 'bg-ink text-white ring-ink' : 'bg-paper ring-line hover:ring-ink/30')}>
          Alle
          <span className={cls("text-[10px] px-1.5 py-0.5 rounded-full",
            activeCat === 'alle' ? 'bg-white/20' : 'bg-cream')}>{COLORINGS.length}</span>
        </button>
        {getCategoriesWithOverrides().map(cat => (
          <button key={cat.slug} onClick={() => setActiveCat(cat.slug)}
            className={cls("inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full ring-1 text-[12px] font-bold transition",
              activeCat === cat.slug ? 'bg-ink text-white ring-ink' : 'bg-paper ring-line hover:ring-ink/30')}>
            {cat.name}
            <span className={cls("text-[10px] px-1.5 py-0.5 rounded-full",
              activeCat === cat.slug ? 'bg-white/20' : 'bg-cream')}>{cat.count}</span>
          </button>
        ))}
      </div>

      <div className="bg-paper ring-1 ring-line rounded-xl3 overflow-hidden">
        <div className="overflow-x-auto scroll-soft">
          <table className="w-full text-sm">
            <thead className="bg-cream text-[12px] uppercase tracking-wider text-inkSoft">
              <tr>
                <th className="text-left font-bold px-5 py-3 whitespace-nowrap">Titel</th>
                <th className="text-left font-bold px-3 py-3 whitespace-nowrap">Kategorie</th>
                <th className="text-right font-bold px-3 py-3 whitespace-nowrap">Downloads</th>
                <th className="text-right font-bold px-3 py-3 whitespace-nowrap">Druck</th>
                <th className="text-right font-bold px-3 py-3 whitespace-nowrap">Punkte</th>
                <th className="text-right font-bold px-5 py-3 whitespace-nowrap">Aktionen</th>
              </tr>
            </thead>
            <tbody>
              {filtered.length === 0 ? (
                <tr><td colSpan="6" className="text-center py-10 text-inkSoft">
                  Keine Bilder gefunden.
                </td></tr>
              ) : filtered.map((c, i) => {
                const cat = findCategory(c.category);
                const tone = TONE_CLASSES[cat.tone];
                return (
                  <tr key={c.id} className={cls(i !== 0 && 'border-t border-line')}>
                    <td className="px-5 py-3">
                      <div className="flex items-center gap-3 min-w-0">
                        <div className={cls("w-10 h-10 rounded-lg flex items-center justify-center shrink-0", tone.soft)}>
                          <ColoringArt artKey={c.art} className="w-7 h-7"/>
                        </div>
                        <div className="min-w-0">
                          <div className="font-semibold truncate">{c.title}</div>
                          <div className="text-[11px] text-inkSoft truncate">/{c.slug}</div>
                        </div>
                      </div>
                    </td>
                    <td className="px-3 py-3 whitespace-nowrap">
                      <button onClick={() => setActiveCat(c.category)}
                        className={cls("inline-flex items-center px-2 py-0.5 rounded-full text-[11px] font-semibold hover:scale-105 transition", tone.soft, tone.text)}>
                        {cat.name}
                      </button>
                    </td>
                    <td className="px-3 py-3 text-right whitespace-nowrap">{c.downloads.toLocaleString('de-DE')}</td>
                    <td className="px-3 py-3 text-right whitespace-nowrap">{c.prints.toLocaleString('de-DE')}</td>
                    <td className="px-3 py-3 text-right font-semibold whitespace-nowrap">{c.points.toLocaleString('de-DE')}</td>
                    <td className="px-5 py-3 text-right whitespace-nowrap">
                      <div className="inline-flex items-center gap-1">
                        <button onClick={() => navigate({view:'detail', slug:c.slug})} className="p-2 rounded-lg hover:bg-cream"><IconEye size={16}/></button>
                        <button className="p-2 rounded-lg hover:bg-cream"><IconEdit size={16}/></button>
                        <button onClick={() => { if (confirm(`„${c.title}" löschen?`)) { deleteImageGlobal(c.id); setList(L => L.filter(x => x.id !== c.id)); } }}
                          className="p-2 rounded-lg hover:bg-coral/10 text-coralDeep"><IconTrash size={16}/></button>
                      </div>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>
    </AdminShell>
    </RequireAdmin>
  );
};

// === Profiles admin ===
const AdminProfilesPage = ({ navigate }) => {
  const [creds, setCreds] = React.useState({});
  const [loading, setLoading] = React.useState(true);
  const [source, setSource] = React.useState('local');
  const [detail, setDetail] = React.useState(null); // email of opened profile
  const refresh = () => {
    awLoadProfiles().then(({ map, source }) => { setCreds(map); setSource(source); setLoading(false); });
  };
  React.useEffect(() => { refresh(); }, []);

  // Derive rich per-profile stats from the various local stores.
  const statsFor = (email) => {
    const key = (email || '').toLowerCase();
    const read = (k, def) => { try { return JSON.parse(localStorage.getItem(k) || JSON.stringify(def)); } catch { return def; } };
    const mappen = read(`aw.mappen.${key}`, []);
    const userStats = read(`aw.userStats.${key}`, {});
    const logins = read('aw.logins', {});
    const lastSeen = read('aw.lastSeen', {});
    const childrenArr = read(`aw.children.${key}`, []);
    let comments = 0;
    try {
      const all = JSON.parse(localStorage.getItem('aw.comments') || '{}');
      Object.values(all).forEach(arr => (arr||[]).forEach(c => { if ((c.email||'').toLowerCase() === key) comments++; }));
    } catch {}
    let referred = 0;
    try {
      const r = JSON.parse(localStorage.getItem('aw.referrals') || '{}');
      Object.values(r).forEach(e => { if ((e.ownerEmail||'').toLowerCase() === key) referred += (e.redeemed||[]).length; });
    } catch {}
    return {
      mappen: mappen.length,
      images: mappen.reduce((s,m) => s + (m.items?.length||0), 0),
      downloads: userStats.downloads || 0,
      prints: userStats.prints || 0,
      logins: logins[key] || 0,
      lastSeen: lastSeen[key] || null,
      children: childrenArr.length,
      comments, referred,
    };
  };

  const writeCreds = (next) => { localStorage.setItem('aw.creds', JSON.stringify(next)); setCreds(next); };

  const remove = (email) => {
    if (!confirm(`Profil „${email}" wirklich löschen? Alle Mappen und Daten gehen verloren.`)) return;
    const next = { ...creds }; const entry = next[email]; delete next[email];
    awDeleteProfile(email, entry && entry.id);
    localStorage.removeItem(`aw.mappen.${email}`);
    localStorage.removeItem(`aw.activeMappe.${email}`);
    localStorage.removeItem(`aw.children.${email}`);
    localStorage.removeItem(`aw.userStats.${email}`);
    setCreds(next); setDetail(null);
  };
  const togglePremium = (email) => {
    const c = creds[email]; if (!c) return;
    const active = !(c.premium && c.premium.active);
    const next = { ...creds, [email]: { ...c, premium: active
      ? { active: true, cycle: 'admin', since: new Date().toISOString() } : { active: false } } };
    writeCreds(next);
  };
  const toggleBan = (email) => {
    const c = creds[email]; if (!c) return;
    writeCreds({ ...creds, [email]: { ...c, banned: !c.banned } });
  };
  const makeAdmin = (email) => {
    const c = creds[email]; if (!c) return;
    writeCreds({ ...creds, [email]: { ...c, isAdmin: !c.isAdmin } });
  };

  const profiles = Object.entries(creds).map(([email, c]) => ({ email, ...c }));
  const [q, setQ] = React.useState('');
  const filtered = profiles.filter(p => !q ||
    (p.name || '').toLowerCase().includes(q.toLowerCase()) || p.email.toLowerCase().includes(q.toLowerCase()));

  const isInactive = (p) => {
    const seen = statsFor(p.email).lastSeen || p.joined;
    if (!seen) return false;
    return (Date.now() - new Date(seen).getTime()) / 864e5 > 30;
  };
  const inactive = profiles.filter(isInactive);
  const removeAllInactive = () => {
    if (!inactive.length || !confirm(`${inactive.length} inaktive Profile (über 30 Tage) löschen?`)) return;
    const next = { ...creds };
    inactive.forEach(p => { delete next[p.email];
      localStorage.removeItem(`aw.mappen.${p.email}`); localStorage.removeItem(`aw.activeMappe.${p.email}`); });
    writeCreds(next);
  };

  const relTime = (iso) => {
    if (!iso) return 'nie';
    const d = (Date.now() - new Date(iso).getTime()) / 864e5;
    if (d < 1) return 'heute';
    if (d < 2) return 'gestern';
    if (d < 30) return `vor ${Math.floor(d)} Tagen`;
    return new Date(iso).toLocaleDateString('de-DE');
  };

  const detailProfile = detail ? profiles.find(p => p.email === detail) : null;
  const detailStats = detailProfile ? statsFor(detailProfile.email) : null;

  return (
    <RequireAdmin navigate={navigate}>
    <AdminShell current="adminProfiles" navigate={navigate}>
      <div className="flex items-end justify-between gap-4 mb-6 flex-wrap">
        <div>
          <Crumbs items={[{label:'Admin', target:{view:'adminDashboard'}},{label:'Profile'}]} navigate={navigate}/>
          <h1 className="mt-2 font-display text-4xl font-extrabold tracking-tight">Profile</h1>
          <p className="text-inkSoft mt-2">{profiles.length} registriert · {inactive.length} inaktiv (&gt; 30 Tage)</p>
        </div>
        <div className="flex items-center gap-2 w-full sm:w-auto">
          <div className="w-full sm:w-72"><Input value={q} onChange={setQ} placeholder="Name oder E-Mail suchen…"/></div>
          {inactive.length > 0 && (
            <Btn variant="soft" size="md" leading={<IconTrash size={16}/>} onClick={removeAllInactive}
              className="text-coralDeep">Inaktive löschen ({inactive.length})</Btn>
          )}
        </div>
      </div>

      {profiles.length === 0 ? (
        <Card title="Keine Profile vorhanden">
          <p className="text-inkSoft text-sm">Sobald sich Nutzer registrieren, erscheinen sie hier.</p>
        </Card>
      ) : (
        <div className="bg-paper ring-1 ring-line rounded-xl3 overflow-hidden">
          <div className="overflow-x-auto scroll-soft">
            <table className="w-full text-sm">
              <thead className="bg-cream text-[12px] uppercase tracking-wider text-inkSoft">
                <tr>
                  <th className="text-left font-bold px-5 py-3 whitespace-nowrap">Nutzer</th>
                  <th className="text-right font-bold px-3 py-3 whitespace-nowrap">Mappen</th>
                  <th className="text-right font-bold px-3 py-3 whitespace-nowrap">Bilder</th>
                  <th className="text-right font-bold px-3 py-3 whitespace-nowrap">Downloads</th>
                  <th className="text-right font-bold px-3 py-3 whitespace-nowrap">Logins</th>
                  <th className="text-left font-bold px-3 py-3 whitespace-nowrap">Zuletzt</th>
                  <th className="text-left font-bold px-3 py-3 whitespace-nowrap">Plan</th>
                  <th className="text-right font-bold px-5 py-3 whitespace-nowrap">Aktionen</th>
                </tr>
              </thead>
              <tbody>
                {filtered.map((p, i) => {
                  const s = statsFor(p.email);
                  return (
                    <tr key={p.email} className={cls(i !== 0 && 'border-t border-line', p.banned && 'bg-coral/5')}>
                      <td className="px-5 py-3">
                        <button onClick={() => setDetail(p.email)} className="flex items-center gap-3 min-w-0 text-left hover:opacity-80">
                          <Avatar user={p} size={36}/>
                          <div className="min-w-0">
                            <div className="font-semibold truncate flex items-center gap-1.5">
                              {p.name || '-'}
                              {p.banned && <span className="text-[10px] px-1.5 py-0.5 rounded-full bg-coralDeep text-white font-bold">gesperrt</span>}
                              {p.isAdmin && <span className="text-[10px] px-1.5 py-0.5 rounded-full bg-ink text-white font-bold">Admin</span>}
                            </div>
                            <div className="text-[11px] text-inkSoft truncate">{p.email}</div>
                          </div>
                        </button>
                      </td>
                      <td className="px-3 py-3 text-right font-semibold whitespace-nowrap">{s.mappen}</td>
                      <td className="px-3 py-3 text-right whitespace-nowrap">{s.images}</td>
                      <td className="px-3 py-3 text-right whitespace-nowrap">{s.downloads}</td>
                      <td className="px-3 py-3 text-right whitespace-nowrap">{s.logins}</td>
                      <td className="px-3 py-3 whitespace-nowrap text-[13px] text-inkSoft">{relTime(s.lastSeen)}</td>
                      <td className="px-3 py-3 whitespace-nowrap">
                        {p.premium && p.premium.active
                          ? <span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-sun/50 text-amber-900 text-[11px] font-bold">⭐ Premium</span>
                          : <span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-cream ring-1 ring-line text-[11px] font-semibold">Free</span>}
                      </td>
                      <td className="px-5 py-3 text-right whitespace-nowrap">
                        <button onClick={() => setDetail(p.email)}
                          className="px-3 py-1.5 rounded-lg bg-cream hover:bg-ink hover:text-white text-[12px] font-bold transition">
                          Details
                        </button>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </div>
      )}

      {/* Detail drawer */}
      {detailProfile && detailStats && (
        <div className="fixed inset-0 z-50 flex">
          <div className="absolute inset-0 bg-ink/40 backdrop-blur-sm" onClick={() => setDetail(null)}></div>
          <aside className="ml-auto relative w-full sm:w-[460px] h-full bg-cream flex flex-col shadow-2xl panel-slide-in">
            <div className="flex items-center gap-3 p-5 border-b-2 border-line bg-paper">
              <Avatar user={detailProfile} size={48}/>
              <div className="flex-1 min-w-0">
                <div className="font-display text-xl font-extrabold leading-tight truncate flex items-center gap-2">
                  {detailProfile.name || '-'}
                  {detailProfile.banned && <span className="text-[10px] px-1.5 py-0.5 rounded-full bg-coralDeep text-white font-bold">gesperrt</span>}
                </div>
                <div className="text-[12px] text-inkSoft truncate">{detailProfile.email}</div>
              </div>
              <button onClick={() => setDetail(null)} className="p-2 rounded-full hover:bg-ink/5"><IconClose/></button>
            </div>

            <div className="flex-1 overflow-y-auto scroll-soft p-5 space-y-4">
              {/* Stat grid */}
              <div className="grid grid-cols-2 gap-3">
                {[
                  ['Sammelmappen', detailStats.mappen, '🎒'],
                  ['Gesammelte Bilder', detailStats.images, '🌟'],
                  ['Downloads', detailStats.downloads, '⬇️'],
                  ['Druckvorgänge', detailStats.prints, '🖨️'],
                  ['Logins', detailStats.logins, '🔑'],
                  ['Kommentare', detailStats.comments, '💬'],
                  ['Kinder', detailStats.children, '🧒'],
                  ['Geworbene Freunde', detailStats.referred, '🎁'],
                ].map(([label, val, emoji]) => (
                  <div key={label} className="bg-paper rounded-2xl ring-1 ring-line p-3">
                    <div className="text-[11px] uppercase tracking-wider font-bold text-inkSoft">{emoji} {label}</div>
                    <div className="font-display text-2xl font-extrabold leading-none mt-1">{val}</div>
                  </div>
                ))}
              </div>

              {/* Meta */}
              <div className="bg-paper rounded-2xl ring-1 ring-line p-4 grid gap-2 text-[13px]">
                <div className="flex justify-between"><span className="text-inkSoft">Registriert</span><span className="font-semibold">{detailProfile.joined ? new Date(detailProfile.joined).toLocaleDateString('de-DE') : '-'}</span></div>
                <div className="flex justify-between"><span className="text-inkSoft">Zuletzt online</span><span className="font-semibold">{relTime(detailStats.lastSeen)}</span></div>
                <div className="flex justify-between"><span className="text-inkSoft">Bereich</span><span className="font-semibold">{(BEREICH_OPTIONS.find(b => b.value === detailProfile.bereich)||{}).label || '-'}</span></div>
                {detailProfile.city && <div className="flex justify-between"><span className="text-inkSoft">Ort</span><span className="font-semibold">{detailProfile.plz} {detailProfile.city}</span></div>}
                <div className="flex justify-between"><span className="text-inkSoft">Plan</span><span className="font-semibold">{detailProfile.premium && detailProfile.premium.active ? '⭐ Premium' : 'Free'}</span></div>
              </div>

              {/* Actions */}
              <div className="grid gap-2">
                <div className="text-[11px] uppercase tracking-wider font-bold text-inkSoft">Aktionen</div>
                <Btn variant={detailProfile.premium && detailProfile.premium.active ? 'soft' : 'sun'} size="md"
                  leading={<IconStar size={16}/>} onClick={() => togglePremium(detailProfile.email)}>
                  {detailProfile.premium && detailProfile.premium.active ? 'Premium entziehen' : 'Premium freischalten'}
                </Btn>
                <Btn variant="soft" size="md" leading={<IconShield size={16}/>} onClick={() => makeAdmin(detailProfile.email)}>
                  {detailProfile.isAdmin ? 'Admin-Rechte entfernen' : 'Zum Admin machen'}
                </Btn>
                <Btn variant="soft" size="md"
                  leading={<IconClose size={16}/>} onClick={() => toggleBan(detailProfile.email)}
                  className={detailProfile.banned ? 'text-emerald-700' : 'text-amber-700'}>
                  {detailProfile.banned ? 'Entsperren' : 'Benutzer sperren'}
                </Btn>
                <Btn variant="ghost" size="md" leading={<IconTrash size={16}/>}
                  onClick={() => remove(detailProfile.email)}
                  className="text-coralDeep hover:bg-coral/10">Profil löschen</Btn>
              </div>
            </div>
          </aside>
        </div>
      )}

      <div className="mt-5 p-4 rounded-2xl bg-sky/30 ring-1 ring-sky text-[13px]">
        {source === 'backend'
          ? <><strong>Live:</strong> Diese Profile kommen direkt aus der Datenbank (<code className="bg-paper px-1.5 py-0.5 rounded">GET /api/admin/users</code>) und umfassen alle registrierten Nutzer:innen, geräteübergreifend.</>
          : <><strong>Hinweis:</strong> Diese Profile stammen aus diesem Browser. Sobald die App mit Backend &amp; Datenbank läuft, erscheinen hier automatisch alle registrierten Nutzer:innen.</>}
      </div>
    </AdminShell>
    </RequireAdmin>
  );
};

// === Statistics ===
const AdminStatsPage = ({ navigate }) => {
  // Derive real numbers — profiles come from the backend when available, else local.
  const [creds, setCreds] = React.useState({});
  React.useEffect(() => { awLoadProfiles().then(({ map }) => setCreds(map)); }, []);
  const profiles = Object.entries(creds);
  const totalProfiles = profiles.length;
  const premiumProfiles = profiles.filter(([, p]) => p.premium && p.premium.active).length;
  const premiumRate = totalProfiles ? Math.round((premiumProfiles / totalProfiles) * 100) : 0;

  let totalComments = 0;
  try {
    const all = JSON.parse(localStorage.getItem('aw.comments') || '{}');
    totalComments = Object.values(all).reduce((s, arr) => s + (arr || []).length, 0);
  } catch {}

  // Referrals
  let totalReferrals = 0;
  try {
    const r = JSON.parse(localStorage.getItem('aw.referrals') || '{}');
    totalReferrals = Object.values(r).reduce((s, e) => s + (e.redeemed?.length || 0), 0);
  } catch {}

  // Suggestions submitted across all users (only stored under current device's user, we use the
  // single suggestions list as a sample)
  let totalSuggestions = 0;
  try { totalSuggestions = (JSON.parse(localStorage.getItem('aw.suggestions') || '[]')).length; } catch {}

  // Mappen per user
  let totalMappen = 0;
  for (const [email] of profiles) {
    try {
      const list = JSON.parse(localStorage.getItem(`aw.mappen.${email}`) || '[]');
      totalMappen += list.length;
    } catch {}
  }

  // Bereich distribution
  const bereichDist = {};
  profiles.forEach(([, p]) => {
    const k = p.bereich || 'sonstiges';
    bereichDist[k] = (bereichDist[k] || 0) + 1;
  });
  const bereichSorted = Object.entries(bereichDist).sort((a,b) => b[1] - a[1]);

  // Top 5 most-commented coloring pages
  let coloringComments = [];
  try {
    const all = JSON.parse(localStorage.getItem('aw.comments') || '{}');
    coloringComments = Object.entries(all)
      .map(([slug, arr]) => ({ slug, count: (arr || []).length, title: findColoring(slug)?.title || slug }))
      .filter(c => c.count > 0).sort((a,b) => b.count - a.count).slice(0, 5);
  } catch {}

  return (
    <RequireAdmin navigate={navigate}>
    <AdminShell current="adminStats" navigate={navigate}>
      <div className="mb-6">
        <Crumbs items={[{label:'Admin', target:{view:'adminDashboard'}},{label:'Statistiken'}]} navigate={navigate}/>
        <h1 className="mt-2 font-display text-4xl font-extrabold tracking-tight">Statistiken</h1>
        <p className="text-inkSoft mt-2">Auswertung aus realen, lokal gespeicherten Daten.</p>
      </div>

      <div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-4">
        <StatCard label="Registrierte Profile" value={totalProfiles.toLocaleString('de-DE')}
          icon={<IconUser size={16}/>} tone="sky"/>
        <StatCard label="Premium-Mitglieder" value={`${premiumProfiles}`}
          sub={`${premiumRate} % aller Profile`} icon={<IconStar size={16}/>} tone="sun"/>
        <StatCard label="Sammelmappen gesamt" value={totalMappen.toLocaleString('de-DE')}
          sub={totalProfiles ? `Ø ${(totalMappen/totalProfiles).toFixed(1)} pro Profil` : '-'}
          icon={<IconImage size={16}/>} tone="mint"/>
        <StatCard label="Kommentare" value={totalComments.toLocaleString('de-DE')}
          icon={<IconBell size={16}/>} tone="coral"/>
      </div>

      <div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-4 mt-4">
        <StatCard label="Eingeladene Freunde" value={totalReferrals.toLocaleString('de-DE')}
          sub="erfolgreich eingelöst" icon={<IconHeart size={16}/>} tone="coral"/>
        <StatCard label="Vorschläge" value={totalSuggestions.toLocaleString('de-DE')}
          sub="eingereicht" icon={<IconSparkle size={16}/>} tone="lilac"/>
        <StatCard label="Kategorien" value={CATEGORIES.length}
          sub={`${CATEGORIES.filter(c => c.count > 0).length} mit Bildern`}
          icon={<IconGrid size={16}/>} tone="sky"/>
        <StatCard label="Ausmalbilder" value={COLORINGS.length}
          icon={<IconImage size={16}/>} tone="mint"/>
      </div>

      <Card title="Punkte je Bild (Top 14)" className="mt-6">
        <div className="grid gap-1.5 h-44" style={{ gridTemplateColumns: 'repeat(14, minmax(0, 1fr))' }}>
          {(() => {
            const sorted = [...COLORINGS].sort((a,b) => b.points - a.points).slice(0, 14);
            const max = Math.max(1, sorted[0]?.points || 1);
            return sorted.map((c, i) => (
              <div key={c.id} className="flex flex-col h-full min-w-0" title={`${c.title}: ${c.points.toLocaleString('de-DE')} Punkte`}>
                <div className="flex-1 flex items-end">
                  <div className="w-full rounded-t-md bg-coral/80" style={{height: `${(c.points / max) * 100}%`}}></div>
                </div>
                <div className="text-[10px] text-inkSoft text-center mt-1.5">{i+1}</div>
              </div>
            ));
          })()}
        </div>
      </Card>

      <div className="grid lg:grid-cols-2 gap-5 mt-5">
        <Card title="Profile nach Bereich">
          {bereichSorted.length === 0 ? (
            <div className="text-center py-6 text-inkSoft text-sm">Noch keine Profile registriert.</div>
          ) : (
            <div className="grid gap-2">
              {bereichSorted.map(([k, n]) => {
                const opt = BEREICH_OPTIONS.find(b => b.value === k);
                const pct = totalProfiles ? Math.round((n / totalProfiles) * 100) : 0;
                return (
                  <div key={k} className="flex items-center gap-3">
                    <span className="text-xl shrink-0">{opt?.emoji || '✨'}</span>
                    <span className="text-sm font-semibold w-40 shrink-0 truncate">{opt?.label || k}</span>
                    <div className="flex-1 h-3 bg-cream rounded-full overflow-hidden">
                      <div className="h-full bg-coral" style={{ width: `${pct}%` }}></div>
                    </div>
                    <span className="text-sm font-bold w-12 text-right shrink-0">{n} ({pct}%)</span>
                  </div>
                );
              })}
            </div>
          )}
        </Card>

        <Card title="Meistdiskutierte Bilder">
          {coloringComments.length === 0 ? (
            <div className="text-center py-6 text-inkSoft text-sm">Noch keine Kommentare.</div>
          ) : (
            <div className="grid gap-2">
              {coloringComments.map((c, i) => (
                <div key={c.slug} className="flex items-center gap-3 px-3 py-2 rounded-xl bg-cream">
                  <RankBadge rank={i+1}/>
                  <div className="flex-1 min-w-0">
                    <div className="font-semibold text-sm truncate">{c.title}</div>
                    <div className="text-[11px] text-inkSoft truncate">/{c.slug}</div>
                  </div>
                  <span className="font-display text-lg font-extrabold">{c.count}</span>
                  <span className="text-[11px] text-inkSoft">💬</span>
                </div>
              ))}
            </div>
          )}
        </Card>
      </div>

      <div className="mt-5 p-4 rounded-2xl bg-sky/30 ring-1 ring-sky text-[13px] leading-relaxed">
        <strong>Hinweis:</strong> Diese Werte werden aus echten Profilen, Mappen, Kommentaren und Vorschlägen
        berechnet, keine erfundenen Zahlen. Mit angebundener Datenbank gelten sie zentral und geräteübergreifend.
      </div>
    </AdminShell>
    </RequireAdmin>
  );
};

// === Settings: Admin PW, AdSense, Premium ===
const AdminSettingsPage = ({ navigate, initialTab }) => {
  const [settings, setSettings] = useSettings();
  const [tab, setTab] = React.useState(initialTab || 'general');
  React.useEffect(() => { if (initialTab) setTab(initialTab); }, [initialTab]);
  return (
    <RequireAdmin navigate={navigate}>
    <AdminShell current={tab === 'staticpages' ? 'adminPages' : 'adminSettings'} navigate={navigate}>
      <div className="mb-6">
        <Crumbs items={[{label:'Admin', target:{view:'adminDashboard'}},{label: tab === 'staticpages' ? 'Unterseiten' : 'Einstellungen'}]} navigate={navigate}/>
        <h1 className="mt-2 font-display text-4xl font-extrabold tracking-tight">{tab === 'staticpages' ? 'Unterseiten bearbeiten' : 'Einstellungen'}</h1>
      </div>
      <div className="mb-6">
        <Tabs tabs={[
          {label:'Allgemein',  value:'general'},
          {label:'Unterseiten',    value:'staticpages'},
          {label:'Admin-Passwort', value:'password'},
          {label:'Google AdSense', value:'adsense'},
          {label:'Premium-Plan',   value:'premium'},
          {label:'Zahlungen',      value:'payments'},
          {label:'Freundeswerbung', value:'referral'},
          {label:'Deployment',     value:'deployment'},
        ]} value={tab} onChange={setTab}/>
      </div>

      {tab === 'general' && <SettingsGeneral settings={settings} setSettings={setSettings}/>}
      {tab === 'staticpages' && <SettingsStaticPages settings={settings} setSettings={setSettings} navigate={navigate}/>}
      {tab === 'password' && <SettingsPassword settings={settings} setSettings={setSettings}/>}
      {tab === 'adsense' && <SettingsAdSense settings={settings} setSettings={setSettings}/>}
      {tab === 'premium' && <SettingsPremium settings={settings} setSettings={setSettings}/>}
      {tab === 'payments' && <SettingsPayments settings={settings} setSettings={setSettings}/>}
      {tab === 'referral' && <SettingsReferral settings={settings} setSettings={setSettings}/>}
      {tab === 'deployment' && <SettingsDeployment/>}
    </AdminShell>
    </RequireAdmin>
  );
};

// === Unterseiten (static page content) editor ===
const STATIC_PAGE_TABS = [
  { id: 'impressum',   label: 'Impressum',       view: 'impressum' },
  { id: 'datenschutz', label: 'Datenschutz',     view: 'datenschutz' },
  { id: 'lizenz',      label: 'Lizenz & Nutzung', view: 'lizenz' },
  { id: 'about',       label: 'Über uns',        view: 'about' },
];
const STATIC_BLOCK_TYPES = [
  { v: 'h2',   label: 'Überschrift H2' },
  { v: 'h3',   label: 'Überschrift H3' },
  { v: 'p',    label: 'Absatz' },
  { v: 'ul',   label: 'Liste' },
  { v: 'note', label: 'Hinweis-Box' },
];
const genBlockId = () => 'b' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6);

const SettingsStaticPages = ({ settings, setSettings, navigate }) => {
  const [pageId, setPageId] = React.useState('impressum');
  const [draft, setDraft] = React.useState(() => JSON.parse(JSON.stringify(settings.staticPages[pageId])));
  const [saved, setSaved] = React.useState(false);

  React.useEffect(() => {
    setDraft(JSON.parse(JSON.stringify(settings.staticPages[pageId])));
    setSaved(false);
  }, [pageId]);

  const setField = (k, v) => setDraft(d => ({ ...d, [k]: v }));
  const setBlock = (id, partial) => setDraft(d => ({ ...d, blocks: d.blocks.map(b => b.id === id ? { ...b, ...partial } : b) }));
  const addBlock = (type) => setDraft(d => ({ ...d, blocks: [...d.blocks, { id: genBlockId(), type, text: '' }] }));
  const removeBlock = (id) => setDraft(d => ({ ...d, blocks: d.blocks.filter(b => b.id !== id) }));
  const move = (id, dir) => setDraft(d => {
    const i = d.blocks.findIndex(b => b.id === id); const j = i + dir;
    if (i < 0 || j < 0 || j >= d.blocks.length) return d;
    const bs = [...d.blocks]; const [x] = bs.splice(i, 1); bs.splice(j, 0, x); return { ...d, blocks: bs };
  });
  const save = () => {
    setSettings(s => ({ ...s, staticPages: { ...s.staticPages, [pageId]: draft } }));
    setSaved(true); setTimeout(() => setSaved(false), 2000);
  };
  const resetDefault = () => {
    if (!window.confirm('Diese Unterseite auf den Standardtext zurücksetzen? Deine Änderungen gehen verloren.')) return;
    setDraft(JSON.parse(JSON.stringify(DEFAULT_SETTINGS.staticPages[pageId])));
  };

  const dirty = JSON.stringify(draft) !== JSON.stringify(settings.staticPages[pageId]);
  const viewName = STATIC_PAGE_TABS.find(t => t.id === pageId).view;

  return (
    <div className="grid gap-5">
      <div className="flex flex-wrap items-center gap-2">
        {STATIC_PAGE_TABS.map(t => (
          <button key={t.id} onClick={() => setPageId(t.id)}
            className={cls("px-4 py-2 rounded-full text-sm font-bold ring-1 transition",
              pageId === t.id ? 'bg-ink text-white ring-ink' : 'bg-paper ring-line hover:ring-ink/30')}>
            {t.label}
          </button>
        ))}
      </div>

      <div className="grid lg:grid-cols-2 gap-5 items-start">
        {/* Editor */}
        <Card title={`Inhalt: ${draft.title || ''}`} subtitle="Überschriften, Texte, Listen und Hinweis-Boxen frei bearbeiten.">
          <div className="grid gap-4">
            <Field label="Seitentitel (H1)"><Input value={draft.title || ''} onChange={(v) => setField('title', v)}/></Field>
            <Field label="Kicker (kleine Zeile über dem Titel)" hint="Leer lassen, um ihn auszublenden">
              <Input value={draft.kicker || ''} onChange={(v) => setField('kicker', v)}/>
            </Field>

            <div className="text-[13px] font-bold mt-1">Abschnitte</div>
            <div className="grid gap-3">
              {draft.blocks.map((b, i) => (
                <div key={b.id} className="rounded-xl ring-1 ring-line bg-cream p-3">
                  <div className="flex items-center gap-2 mb-2">
                    <select value={b.type} onChange={(e) => setBlock(b.id, { type: e.target.value })}
                      className="bg-paper ring-1 ring-line rounded-lg px-2.5 py-1.5 text-[13px] font-semibold outline-none focus:ring-2 focus:ring-coral">
                      {STATIC_BLOCK_TYPES.map(t => <option key={t.v} value={t.v}>{t.label}</option>)}
                    </select>
                    <div className="ml-auto flex items-center gap-1">
                      <button onClick={() => move(b.id, -1)} disabled={i === 0} title="Nach oben"
                        className="w-8 h-8 rounded-lg ring-1 ring-line bg-paper hover:bg-ink/5 disabled:opacity-30 font-bold">↑</button>
                      <button onClick={() => move(b.id, 1)} disabled={i === draft.blocks.length - 1} title="Nach unten"
                        className="w-8 h-8 rounded-lg ring-1 ring-line bg-paper hover:bg-ink/5 disabled:opacity-30 font-bold">↓</button>
                      <button onClick={() => removeBlock(b.id)} title="Abschnitt löschen"
                        className="w-8 h-8 rounded-lg ring-1 ring-coral/40 bg-coral/10 text-coralDeep hover:bg-coral/20 font-bold">✕</button>
                    </div>
                  </div>
                  <textarea value={b.text} onChange={(e) => setBlock(b.id, { text: e.target.value })}
                    rows={b.type === 'ul' ? 4 : (b.type === 'h2' || b.type === 'h3') ? 1 : 3}
                    placeholder={b.type === 'ul' ? 'Ein Listenpunkt pro Zeile' : (b.type === 'h2' || b.type === 'h3') ? 'Überschrift' : 'Text …'}
                    className="w-full bg-paper ring-1 ring-line rounded-lg px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-coral resize-y leading-relaxed"/>
                </div>
              ))}
              {draft.blocks.length === 0 && (
                <div className="text-[13px] text-inkSoft rounded-xl bg-cream ring-1 ring-line p-4 text-center">Noch keine Abschnitte. Füge unten welche hinzu.</div>
              )}
            </div>

            <div className="flex flex-wrap gap-2">
              {STATIC_BLOCK_TYPES.map(t => (
                <button key={t.v} onClick={() => addBlock(t.v)}
                  className="px-3 py-2 rounded-lg ring-1 ring-line bg-paper hover:border-coral hover:ring-coral text-[13px] font-bold transition">
                  + {t.label}
                </button>
              ))}
            </div>

            <div className="rounded-xl bg-sky/30 ring-1 ring-sky text-[12px] leading-relaxed p-3">
              <strong>Formatierung:</strong> <code className="bg-paper px-1 rounded">**fett**</code> für fetten Text,
              <code className="bg-paper px-1 rounded mx-1">[Linktext](https://… oder mailto:…)</code> für Links,
              Zeilenumbruch = neue Zeile. Bei „Über uns" zusätzlich die Platzhalter
              <code className="bg-paper px-1 rounded mx-1">{'{bilder}'}</code>
              <code className="bg-paper px-1 rounded">{'{kategorien}'}</code>
              <code className="bg-paper px-1 rounded mx-1">{'{profile}'}</code>
              <code className="bg-paper px-1 rounded">{'{auszeichnungen}'}</code> (werden live ersetzt).
            </div>

            <div className="flex flex-wrap items-center gap-3">
              <Btn size="lg" leading={<IconCheck size={18}/>} onClick={save} className={dirty ? '' : 'opacity-60'}>Speichern</Btn>
              <Btn size="md" variant="ghost" onClick={resetDefault}>Auf Standard zurücksetzen</Btn>
              <Btn size="md" variant="ghost" onClick={() => navigate({ view: viewName })}>Seite ansehen</Btn>
              {saved && <span className="text-emerald-700 font-bold text-sm">Gespeichert ✓</span>}
              {!saved && dirty && <span className="text-amber-700 font-semibold text-sm">Nicht gespeicherte Änderungen</span>}
            </div>
          </div>
        </Card>

        {/* Live preview */}
        <Card title="Vorschau" subtitle="So sieht die Unterseite aus.">
          <div className="prose-static-preview">
            <h2 className="!mt-0 font-display text-2xl font-extrabold tracking-tight mb-3">{draft.title}</h2>
            <StaticBlocks blocks={draft.blocks} vars={null}/>
          </div>
          <style>{`
            .prose-static-preview h2 { font-family:'Baloo 2',system-ui,sans-serif; font-weight:800; font-size:1.4rem; margin-top:1.5rem; margin-bottom:0.5rem; letter-spacing:-0.01em; }
            .prose-static-preview h3 { font-family:'Baloo 2',system-ui,sans-serif; font-weight:700; font-size:1.1rem; margin-top:1.1rem; margin-bottom:0.35rem; }
            .prose-static-preview p { color:#465069; line-height:1.7; margin-bottom:0.8rem; }
            .prose-static-preview strong { color:#1F2A44; }
            .prose-static-preview ul { margin:0.4rem 0 0.9rem 1.25rem; }
            .prose-static-preview li { color:#465069; line-height:1.6; margin-bottom:0.25rem; list-style:disc; }
            .prose-static-preview a { color:#F26A56; font-weight:700; }
            .prose-static-preview .note { background:#F2F4F9; border-radius:12px; padding:12px 16px; font-size:13px; color:#465069; margin:0.9rem 0; }
          `}</style>
        </Card>
      </div>
    </div>
  );
};

const SettingsGeneral = ({ settings, setSettings }) => {
  const [title, setTitle] = React.useState(settings.siteTitle);
  const [lang, setLang] = React.useState(settings.language);
  const [freeMappen, setFreeMappen] = React.useState(settings.limits.freeMappenLimit);
  const [logo, setLogo] = React.useState(settings.logo || '');
  const [logoSizeHeader, setLogoSizeHeader] = React.useState(settings.logoSizeHeader || 48);
  const [logoSizeFooter, setLogoSizeFooter] = React.useState(settings.logoSizeFooter || 64);
  const [favicon, setFavicon] = React.useState(settings.favicon || '');
  const [saved, setSaved] = React.useState(false);
  const save = () => {
    setSettings(s => ({ ...s, siteTitle: title, language: lang, logo, favicon,
      logoSizeHeader: Number(logoSizeHeader) || 48, logoSizeFooter: Number(logoSizeFooter) || 64,
      limits: { ...s.limits, freeMappenLimit: Number(freeMappen) || 0 } }));
    setSaved(true); setTimeout(() => setSaved(false), 2000);
  };
  const pick = (setter, maxKB) => (file) => {
    if (!file) return;
    if (file.size > maxKB * 1024) { alert(`Bild zu groß (max ${maxKB} KB).`); return; }
    const r = new FileReader(); r.onload = () => setter(r.result); r.readAsDataURL(file);
  };
  return (
    <div className="grid lg:grid-cols-2 gap-5">
      <Card title="Seite">
        <div className="grid gap-4">
          <Field label="Seitentitel"><Input value={title} onChange={setTitle}/></Field>
          <Field label="Standard-Sprache">
            <select value={lang} onChange={(e) => setLang(e.target.value)}
              className="w-full bg-paper ring-1 ring-line rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-coral">
              <option value="de">Deutsch</option>
              <option value="en">English</option>
            </select>
          </Field>
        </div>
      </Card>

      <Card title="Logo & Favicon" subtitle="Logo erscheint im Header und auf dem Ausdruck.">
        <div className="grid gap-4">
          <LogoUpload label="Logo" hint="PNG/SVG, transparenter Hintergrund · max 400 KB"
            value={logo} onChange={pick(setLogo, 400)} onClear={() => setLogo('')} wide/>
          {logo && (
            <div className="grid gap-4">
              <LogoSizeControl label="Größe in der Navigation (Header)" value={logoSizeHeader}
                onChange={setLogoSizeHeader} min={28} max={96} logo={logo} bg="bg-cream"/>
              <LogoSizeControl label="Größe im Footer" value={logoSizeFooter}
                onChange={setLogoSizeFooter} min={32} max={140} logo={logo} bg="bg-ink"/>
            </div>
          )}
          <LogoUpload label="Favicon" hint="Quadratisch (PNG), z. B. 64×64 · max 100 KB"
            value={favicon} onChange={pick(setFavicon, 100)} onClear={() => setFavicon('')}/>
        </div>
      </Card>

      <Card title="Free-Plan Limits" subtitle="Limit für kostenlose Nutzer">
        <Field label="Maximale Anzahl Sammelmappen (Free)" hint="0 = unbegrenzt">
          <Input value={String(freeMappen)} onChange={setFreeMappen} type="number"/>
        </Field>
        <div className="mt-4 p-3 rounded-xl bg-sky/30 text-[12px]">
          Free-Nutzer können dann höchstens {freeMappen === '0' || freeMappen === 0 ? '∞' : freeMappen} Sammelmappe{Number(freeMappen) === 1 ? '' : 'n'} anlegen.
        </div>
      </Card>
      <div className="lg:col-span-2 flex items-center gap-3">
        <Btn size="lg" leading={<IconCheck size={18}/>} onClick={save}>Speichern</Btn>
        {saved && <span className="text-emerald-700 font-bold text-sm">Gespeichert ✓</span>}
      </div>
    </div>
  );
};

const LogoSizeControl = ({ label, value, onChange, min, max, logo, bg }) => (
  <div>
    <div className="flex items-baseline justify-between mb-1.5">
      <span className="text-[13px] font-semibold">{label}</span>
      <span className="text-[12px] font-bold text-inkSoft">{value}px Höhe</span>
    </div>
    <input type="range" min={min} max={max} step="2" value={value}
      onChange={(e) => onChange(Number(e.target.value))}
      className="w-full accent-coral cursor-pointer"/>
    <div className="flex items-center justify-between text-[11px] text-inkSoft mt-0.5">
      <span>klein</span><span>groß</span>
    </div>
    <div className={cls("mt-3 rounded-xl ring-1 ring-line px-4 py-3 flex items-center justify-center overflow-hidden", bg)}>
      <img src={logo} alt="Vorschau" className="max-w-full object-contain" style={{ height: value + 'px' }}/>
    </div>
  </div>
);

const LogoUpload = ({ label, hint, value, onChange, onClear, wide }) => {
  const ref = React.useRef();
  return (
    <div>
      <div className="text-[13px] font-semibold mb-1.5">{label}</div>
      <div className="flex items-stretch gap-3">
        <div className={cls("rounded-xl ring-1 ring-line bg-cream overflow-hidden flex items-center justify-center shrink-0",
          wide ? 'w-40 h-16' : 'w-16 h-16')}>
          {value ? <img src={value} alt="" className="max-w-full max-h-full object-contain"/>
                 : <span className="text-inkSoft text-[11px] px-2 text-center">keins</span>}
        </div>
        <div className="flex-1 grid gap-2 min-w-0">
          <button type="button" onClick={() => ref.current?.click()}
            className="w-full flex items-center justify-center gap-2 px-3 py-2.5 rounded-xl border-2 border-dashed border-line bg-paper hover:border-coral hover:bg-coral/5 transition text-sm font-semibold">
            <IconUpload size={16}/> {value ? 'Ersetzen' : 'Hochladen'}
          </button>
          {value && <button type="button" onClick={onClear} className="text-[12px] font-bold text-coralDeep hover:underline">Entfernen</button>}
          {hint && <div className="text-[11px] text-inkSoft">{hint}</div>}
        </div>
      </div>
      <input ref={ref} type="file" accept="image/*" className="hidden" onChange={(e) => onChange(e.target.files?.[0])}/>
    </div>
  );
};

const SettingsPassword = ({ settings, setSettings }) => {
  const [oldPw, setOldPw] = React.useState('');
  const [newPw, setNewPw] = React.useState('');
  const [confirmPw, setConfirmPw] = React.useState('');
  const [msg, setMsg] = React.useState({ kind:'', text:'' });
  const save = () => {
    if (oldPw !== settings.adminPassword) return setMsg({ kind:'err', text:'Altes Passwort stimmt nicht.' });
    if (newPw.length < 6) return setMsg({ kind:'err', text:'Neues Passwort: mind. 6 Zeichen.' });
    if (newPw !== confirmPw) return setMsg({ kind:'err', text:'Bestätigung stimmt nicht überein.' });
    setSettings(s => ({ ...s, adminPassword: newPw }));
    setOldPw(''); setNewPw(''); setConfirmPw('');
    setMsg({ kind:'ok', text:'Passwort geändert.' });
    setTimeout(() => setMsg({ kind:'', text:'' }), 3000);
  };
  return (
    <Card title="Admin-Passwort ändern" subtitle="Schützt den Zugang zum Admin-Bereich.">
      <div className="grid gap-4 max-w-md">
        <Field label="Aktuelles Passwort"><Input type="password" value={oldPw} onChange={setOldPw}/></Field>
        <Field label="Neues Passwort" hint="Mind. 6 Zeichen"><Input type="password" value={newPw} onChange={setNewPw}/></Field>
        <Field label="Neues Passwort bestätigen"><Input type="password" value={confirmPw} onChange={setConfirmPw}/></Field>
        {msg.text && (
          <div className={cls("text-[13px] rounded-xl px-3 py-2 ring-1",
            msg.kind === 'ok' ? 'bg-mint/40 ring-mint text-emerald-900' : 'bg-coral/10 ring-coral/30 text-coralDeep')}>
            {msg.text}
          </div>
        )}
        <Btn size="lg" leading={<IconShield size={18}/>} onClick={save}>Passwort speichern</Btn>
      </div>
    </Card>
  );
};

const SettingsAdSense = ({ settings, setSettings }) => {
  const ad = settings.adsense;
  const set = (partial) => setSettings(s => ({ ...s, adsense: { ...s.adsense, ...partial } }));
  return (
    <div className="grid lg:grid-cols-2 gap-5">
      <Card title="Google AdSense" subtitle="Werbung auf der Webseite einblenden.">
        <Toggle checked={ad.enabled} onChange={(v) => set({ enabled: v })}
          label="AdSense aktivieren"
          sub="Schaltet Werbeplätze auf Start-, Kategorie- und Detailseiten frei."/>
        <div className="grid gap-4 mt-4">
          <Field label="Publisher-ID" hint="z. B. ca-pub-1234567890">
            <Input value={ad.publisherId} onChange={(v) => set({ publisherId: v })} placeholder="ca-pub-…"/>
          </Field>
          <Field label="Slot: Header (Leaderboard)"><Input value={ad.slotHeader} onChange={(v) => set({ slotHeader: v })} placeholder="0000000000"/></Field>
          <Field label="Slot: Inline (Content)"><Input value={ad.slotInline} onChange={(v) => set({ slotInline: v })} placeholder="0000000000"/></Field>
          <Field label="Slot: Sidebar"><Input value={ad.slotSidebar} onChange={(v) => set({ slotSidebar: v })} placeholder="0000000000"/></Field>
        </div>
      </Card>
      <Card title="Vorschau" subtitle="So sieht der Werbeplatz aus.">
        {ad.enabled ? (
          <AdBanner slot="inline" size="medium"/>
        ) : (
          <div className="rounded-2xl bg-cream ring-1 ring-line p-6 text-center">
            <div className="text-3xl mb-2">📢</div>
            <div className="font-display text-lg font-bold mb-1">AdSense ist deaktiviert</div>
            <div className="text-[13px] text-inkSoft">Aktiviere AdSense links, um die Vorschau zu sehen.</div>
          </div>
        )}
        <div className="mt-4 p-3 rounded-xl bg-sun/30 text-[12px]">
          <strong>Hinweis:</strong> In Produktion würde hier das echte Google-AdSense-Script geladen.
        </div>
      </Card>
    </div>
  );
};

const SettingsPremium = ({ settings, setSettings }) => {
  const p = settings.premium;
  const set = (partial) => setSettings(s => ({ ...s, premium: { ...s.premium, ...partial } }));
  const setFeature = (key, val) => setSettings(s => ({ ...s,
    premium: { ...s.premium, features: { ...s.premium.features, [key]: val } } }));

  return (
    <div className="grid lg:grid-cols-2 gap-5">
      <Card title="Premium-Plan" subtitle={'Konfiguration für „Malfino Plus".'}>        <Toggle checked={p.enabled} onChange={(v) => set({ enabled: v })}
          label="Premium-Plan aktivieren"
          sub="Erst wenn aktiv, sehen Nutzer den Upgrade-Button."/>
        <div className="grid sm:grid-cols-2 gap-4 mt-4">
          <Field label="Plan-Name"><Input value={p.name} onChange={(v) => set({ name: v })}/></Field>
          <Field label="Währung">
            <select value={p.currency} onChange={(e) => set({ currency: e.target.value })}
              className="w-full bg-paper ring-1 ring-line rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-coral">
              <option value="EUR">EUR (€)</option>
              <option value="USD">USD ($)</option>
              <option value="CHF">CHF</option>
            </select>
          </Field>
          <Field label="Preis monatlich (Abo)"><Input value={String(p.priceMonthly)} onChange={(v) => set({ priceMonthly: parseFloat(v) || 0 })} type="number"/></Field>
          <Field label="Preis jährlich (Abo)"><Input value={String(p.priceYearly)} onChange={(v) => set({ priceYearly: parseFloat(v) || 0 })} type="number"/></Field>
          <Field label="Einmalkauf (1 Monat)"><Input value={String(p.priceOnce ?? 0)} onChange={(v) => set({ priceOnce: parseFloat(v) || 0 })} type="number"/></Field>
        </div>
        <div className="mt-4">
          <div className="text-[13px] font-bold mb-2">Welche Bezahlmodelle anbieten?</div>
          <div className="grid sm:grid-cols-3 gap-2">
            <Toggle checked={p.billing?.monthly !== false} onChange={(v) => set({ billing: { ...p.billing, monthly: v } })}
              label="Monats-Abo" sub={`${p.priceMonthly} ${p.currency}/Monat`}/>
            <Toggle checked={p.billing?.yearly !== false} onChange={(v) => set({ billing: { ...p.billing, yearly: v } })}
              label="Jahres-Abo" sub={`${p.priceYearly} ${p.currency}/Jahr`}/>
            <Toggle checked={!!p.billing?.once} onChange={(v) => set({ billing: { ...p.billing, once: v } })}
              label="Einmalkauf (1 Monat)" sub={`${p.priceOnce ?? 0} ${p.currency} für 1 Monat`}/>
          </div>
          <div className="mt-3 p-3 rounded-xl bg-sky/30 ring-1 ring-sky text-[12px] leading-relaxed">
            <strong>Abo (monatlich/jährlich):</strong> zieht über PayPal automatisch wiederkehrend ab (PayPal-Subscriptions).
            <strong className="ml-1">Einmalkauf (1 Monat):</strong> einmalige Zahlung, schaltet Premium für genau einen Monat frei – verlängert sich <strong>nicht</strong> automatisch. Danach muss erneut gekauft werden.
          </div>
        </div>
      </Card>

      <Card title="Premium-Features" subtitle="Welche Vorteile haben Premium-Nutzer?">
        <div className="grid gap-2">
          {Object.keys(p.features).map(key => (
            <Toggle key={key}
              checked={p.features[key]}
              onChange={(v) => setFeature(key, v)}
              label={p.descriptions[key] || key}
              sub={`Schlüssel: ${key}`}/>
          ))}
        </div>
      </Card>

      <div className="lg:col-span-2">
        <Card title="Vorschau" subtitle="So sieht der Upgrade-Plan für Nutzer aus.">
          <PremiumCard premium={p} sample/>
        </Card>
      </div>
    </div>
  );
};

// === Payments settings (Stripe + PayPal, with sandbox) ===
// === Go-Live readiness checklist ===
const GoLiveChecklist = ({ settings }) => {
  const p = settings.premium || {};
  const pay = settings.payments || {};
  const ppl = pay.paypal || {};
  const sandbox = pay.sandboxMode !== false;
  const clientId = (sandbox ? ppl.testClientId : ppl.clientId) || '';
  const billing = p.billing || {};
  const anyModel = billing.monthly !== false || billing.yearly !== false || !!billing.once;
  const pricesOk =
    (billing.monthly === false || Number(p.priceMonthly) > 0) &&
    (billing.yearly === false || Number(p.priceYearly) > 0) &&
    (!billing.once || Number(p.priceOnce) > 0);

  const items = [
    { ok: !!p.enabled, label: 'Premium ist aktiviert', hint: 'Premium-Bereich → „Premium aktiv".' },
    { ok: anyModel, label: 'Mindestens ein Bezahlmodell ausgewählt', hint: 'Monats-/Jahres-Abo oder Einmalkauf.' },
    { ok: pricesOk, label: 'Preise für alle aktiven Modelle gesetzt', hint: 'Preis muss größer als 0 sein.' },
    { ok: !!ppl.enabled, label: 'PayPal ist als Anbieter aktiviert', hint: 'Karte „PayPal" → „Aktiv".' },
    { ok: clientId.trim().length > 10, label: `PayPal-${sandbox ? 'Sandbox' : 'Live'}-Client-ID hinterlegt`, hint: 'Aus dem PayPal-Developer-Dashboard.' },
    { ok: !sandbox, label: 'Live-Modus aktiv (echte Zahlungen)', hint: 'Für den echten Verkauf Sandbox ausschalten.', warnOnly: true },
  ];
  const done = items.filter(i => i.ok).length;
  const blockers = items.filter(i => !i.ok && !i.warnOnly).length;
  const ready = blockers === 0;

  return (
    <Card title="Go-Live Checkliste" subtitle={`${done}/${items.length} erledigt, was vor dem Scharfschalten erfüllt sein muss.`}>
      <div className={cls('mb-4 p-3 rounded-xl ring-1 text-[13px] font-bold',
        ready ? 'bg-mint/40 ring-mint text-emerald-900' : 'bg-sun/30 ring-sun text-amber-900')}>
        {ready
          ? '✓ Bereit für Go-Live, du kannst echte Zahlungen entgegennehmen.'
          : `Noch ${blockers} Punkt${blockers === 1 ? '' : 'e'} offen, bevor echte Zahlungen funktionieren.`}
      </div>
      <ul className="grid gap-2">
        {items.map((it, i) => (
          <li key={i} className="flex items-start gap-3">
            <span className={cls('mt-0.5 inline-flex w-5 h-5 rounded-full items-center justify-center text-[12px] font-extrabold shrink-0',
              it.ok ? 'bg-emerald-600 text-white' : it.warnOnly ? 'bg-sun text-ink' : 'bg-coral/20 text-coralDeep ring-1 ring-coral/40')}>
              {it.ok ? '✓' : it.warnOnly ? '!' : '×'}
            </span>
            <div className="leading-tight">
              <div className={cls('text-[14px] font-bold', it.ok ? 'text-ink' : 'text-inkSoft')}>{it.label}</div>
              <div className="text-[12px] text-inkSoft">{it.hint}</div>
            </div>
          </li>
        ))}
      </ul>
      <div className="mt-4 p-3 rounded-xl bg-sky/30 ring-1 ring-sky text-[12px] leading-relaxed">
        <strong>Server (CapRover):</strong> Damit Zahlungen für <em>alle</em> Besucher laufen, setze zusätzlich die Umgebungsvariablen
        <code className="bg-paper px-1.5 py-0.5 rounded mx-1">PAYPAL_CLIENT_ID</code>,
        <code className="bg-paper px-1.5 py-0.5 rounded mx-1">PAYPAL_SECRET</code> und
        <code className="bg-paper px-1.5 py-0.5 rounded mx-1">PAYPAL_ENV=live</code>. Das Backend liefert die (öffentliche) Client-ID dann automatisch an die Kasse.
      </div>
    </Card>
  );
};

const SettingsPayments = ({ settings, setSettings }) => {
  const pay = settings.payments;
  const set = (partial) => setSettings(s => ({ ...s, payments: { ...s.payments, ...partial } }));
  const setStripe = (partial) => setSettings(s => ({ ...s, payments: { ...s.payments, stripe: { ...s.payments.stripe, ...partial } } }));
  const setPayPal = (partial) => setSettings(s => ({ ...s, payments: { ...s.payments, paypal: { ...s.payments.paypal, ...partial } } }));
  const sandbox = pay.sandboxMode;
  const [stripeOk, setStripeOk] = React.useState(null);
  const [paypalOk, setPaypalOk] = React.useState(null);

  const testStripe = () => {
    const key = sandbox ? pay.stripe.testPublishableKey : pay.stripe.publishableKey;
    const ok = sandbox
      ? /^pk_test_[A-Za-z0-9]{8,}$/.test(key)
      : /^pk_live_[A-Za-z0-9]{8,}$/.test(key);
    setStripeOk(ok ? 'ok' : 'err');
  };
  const testPaypal = () => {
    const id = sandbox ? pay.paypal.testClientId : pay.paypal.clientId;
    setPaypalOk(id && id.length > 30 ? 'ok' : 'err');
  };

  return (
    <div className="grid gap-5">
      <GoLiveChecklist settings={settings}/>
      <Card title="Zahlungsumgebung">
        <Toggle checked={pay.sandboxMode} onChange={(v) => set({ sandboxMode: v })}
          label={`Modus: ${pay.sandboxMode ? 'Sandbox / Test' : 'Live / Produktion'}`}
          sub={pay.sandboxMode
            ? 'Es werden nur Test-Buchungen ausgeführt, keine echten Zahlungen.'
            : 'Achtung: Im Live-Modus werden echte Kreditkarten belastet.'}/>
        <div className={cls("mt-4 p-3 rounded-xl text-[12px] ring-1",
          sandbox ? 'bg-sun/30 ring-sun text-amber-900' : 'bg-coral/10 ring-coral/30 text-coralDeep')}>
          {sandbox
            ? '🧪 Sandbox aktiv, alle Premium-Käufe werden simuliert. Perfekt zum Testen.'
            : '⚠️ Live-Modus aktiv. Stripe & PayPal verarbeiten echte Zahlungen.'}
        </div>
      </Card>

      <Card title="Stripe" subtitle="Kreditkarten, SEPA, Apple/Google Pay." action={
        <Toggle checked={pay.stripe.enabled} onChange={(v) => setStripe({ enabled: v })}
          label="Aktiv" sub=""/>
      }>
        {sandbox ? (
          <div className="grid sm:grid-cols-2 gap-4">
            <Field label="Test-Publishable-Key" hint="beginnt mit pk_test_">
              <Input value={pay.stripe.testPublishableKey} onChange={(v) => setStripe({ testPublishableKey: v })} placeholder="pk_test_…"/>
            </Field>
            <Field label="Test-Secret-Key" hint="beginnt mit sk_test_">
              <Input type="password" value={pay.stripe.testSecretKey} onChange={(v) => setStripe({ testSecretKey: v })} placeholder="sk_test_…"/>
            </Field>
          </div>
        ) : (
          <div className="grid sm:grid-cols-2 gap-4">
            <Field label="Live-Publishable-Key" hint="beginnt mit pk_live_">
              <Input value={pay.stripe.publishableKey} onChange={(v) => setStripe({ publishableKey: v })} placeholder="pk_live_…"/>
            </Field>
            <Field label="Live-Secret-Key" hint="beginnt mit sk_live_">
              <Input type="password" value={pay.stripe.secretKey} onChange={(v) => setStripe({ secretKey: v })} placeholder="sk_live_…"/>
            </Field>
            <div className="sm:col-span-2">
              <Field label="Webhook-Secret" hint="aus Stripe Dashboard → Webhooks">
                <Input type="password" value={pay.stripe.webhookSecret} onChange={(v) => setStripe({ webhookSecret: v })} placeholder="whsec_…"/>
              </Field>
            </div>
          </div>
        )}
        <div className="mt-4 flex items-center gap-3">
          <Btn size="sm" variant="soft" onClick={testStripe} leading={<IconCheck size={14}/>}>Verbindung testen</Btn>
          {stripeOk === 'ok' && <span className="text-emerald-700 font-bold text-sm">✓ Schlüssel ist gültig</span>}
          {stripeOk === 'err' && <span className="text-coralDeep font-bold text-sm">✗ Schlüssel ungültig oder leer</span>}
        </div>
      </Card>

      <Card title="PayPal" subtitle="PayPal & Lastschrift via PayPal." action={
        <Toggle checked={pay.paypal.enabled} onChange={(v) => setPayPal({ enabled: v })}
          label="Aktiv" sub=""/>
      }>
        {sandbox ? (
          <div className="grid sm:grid-cols-2 gap-4">
            <Field label="Sandbox-Client-ID"><Input value={pay.paypal.testClientId} onChange={(v) => setPayPal({ testClientId: v })} placeholder="AYpQbX…"/></Field>
            <Field label="Sandbox-Secret"><Input type="password" value={pay.paypal.testSecret} onChange={(v) => setPayPal({ testSecret: v })} placeholder="ELdKlmn…"/></Field>
          </div>
        ) : (
          <div className="grid sm:grid-cols-2 gap-4">
            <Field label="Live-Client-ID"><Input value={pay.paypal.clientId} onChange={(v) => setPayPal({ clientId: v })} placeholder="AYpQbX…"/></Field>
            <Field label="Live-Secret"><Input type="password" value={pay.paypal.secret} onChange={(v) => setPayPal({ secret: v })} placeholder="ELdKlmn…"/></Field>
          </div>
        )}
        <div className="mt-4 flex items-center gap-3">
          <Btn size="sm" variant="soft" onClick={testPaypal} leading={<IconCheck size={14}/>}>Verbindung testen</Btn>
          {paypalOk === 'ok' && <span className="text-emerald-700 font-bold text-sm">✓ Client-ID sieht gut aus</span>}
          {paypalOk === 'err' && <span className="text-coralDeep font-bold text-sm">✗ Client-ID zu kurz/leer</span>}
        </div>
      </Card>

      <Card title="Rückleitungs-URLs" subtitle="Wohin der Nutzer nach dem Bezahlen geleitet wird.">
        <div className="grid sm:grid-cols-2 gap-4">
          <Field label="Erfolg"><Input value={pay.successUrl} onChange={(v) => set({ successUrl: v })} prefix="/"/></Field>
          <Field label="Abbruch"><Input value={pay.cancelUrl} onChange={(v) => set({ cancelUrl: v })} prefix="/"/></Field>
        </div>
      </Card>
    </div>
  );
};

// === Referral settings ===
const SettingsReferral = ({ settings, setSettings }) => {
  const r = settings.referral;
  const set = (partial) => setSettings(s => ({ ...s, referral: { ...s.referral, ...partial } }));
  return (
    <div className="grid lg:grid-cols-2 gap-5">
      <Card title="Freundeswerbung" subtitle="Nutzer erhalten Premium-Tage, wenn sie Freunde einladen.">
        <Toggle checked={r.enabled} onChange={(v) => set({ enabled: v })}
          label="Werbe-Programm aktivieren"
          sub="Nutzer sehen dann einen Einladungslink in ihrem Profil."/>
        <div className="grid sm:grid-cols-2 gap-4 mt-4">
          <Field label="Bonus-Tage pro Freund">
            <Input type="number" value={String(r.rewardDays)} onChange={(v) => set({ rewardDays: parseInt(v) || 0 })}/>
          </Field>
          <Field label="Max. Bonus-Monate pro Nutzer">
            <Input type="number" value={String(r.maxRewardsPerUser)} onChange={(v) => set({ maxRewardsPerUser: parseInt(v) || 0 })}/>
          </Field>
        </div>
        <div className="grid gap-2 mt-4">
          <Toggle checked={r.rewardForBoth} onChange={(v) => set({ rewardForBoth: v })}
            label="Beide bekommen den Bonus"
            sub="Werber und Eingeladener erhalten je 1 Monat Premium."/>
          <Toggle checked={r.minPurchaseRequired} onChange={(v) => set({ minPurchaseRequired: v })}
            label="Premium-Kauf erforderlich"
            sub="Bonus zählt erst, wenn der Eingeladene Premium kauft."/>
        </div>
      </Card>
      <Card title="Vorschau">
        <div className="rounded-2xl bg-gradient-to-br from-sun/40 to-coral/20 ring-2 ring-sun p-6 text-center">
          <div className="text-5xl mb-3">🎁</div>
          <div className="font-display text-2xl font-extrabold mb-2">Freund einladen, {r.rewardDays} Tage gratis!</div>
          <p className="text-inkSoft text-sm mb-4">
            {r.rewardForBoth
              ? 'Du und dein Freund bekommen je 1 Monat Premium gratis.'
              : 'Du bekommst 1 Monat Premium gratis.'}
          </p>
          <div className="bg-paper rounded-xl px-3 py-2 font-mono text-sm break-all ring-1 ring-line">
            malfino.de/r/dein-code
          </div>
        </div>
      </Card>
    </div>
  );
};

// === Deployment: generate a fresh frontend TAR in the browser ===
const SettingsDeployment = () => {
  const [busy, setBusy] = React.useState(false);
  const [msg, setMsg] = React.useState('');

  // Frontend files (fetched live so the TAR reflects the current UI).
  const FRONTEND_FILES = [
    'index.html',
    'src/icons.jsx','src/illustrations.jsx','src/painting-art.jsx','src/hero-art.jsx',
    'src/data.jsx','src/deploy-bundle.jsx','src/settings.jsx','src/awapi.jsx','src/referral.jsx','src/achievements.jsx',
    'src/comments.jsx','src/cookie.jsx','src/components.jsx','src/checkout.jsx','src/print-utils.jsx',
    'src/profile.jsx','src/pages-public.jsx','src/pages-static.jsx','src/pages-admin.jsx',
    'src/mappe.jsx','src/app.jsx',
  ];

  const buildTar = async () => {
    setBusy(true); setMsg('Baue Paket …');
    try {
      const enc = new TextEncoder();
      const octal = (n, len) => { let s = n.toString(8); while (s.length < len-1) s = '0'+s; return s+'\0'; };
      const header = (name, size) => {
        const buf = new Uint8Array(512);
        const w = (str, off, len) => { const b = enc.encode(str); for (let i=0;i<b.length&&i<len;i++) buf[off+i]=b[i]; };
        w(name,0,100); w('0000644\0',100,8); w('0000000\0',108,8); w('0000000\0',116,8);
        w(octal(size,12),124,12); w(octal(Math.floor(Date.now()/1000),12),136,12);
        for (let i=148;i<156;i++) buf[i]=0x20; w('0',156,1); w('ustar\0',257,6); w('00',263,2);
        let sum=0; for (let i=0;i<512;i++) sum+=buf[i]; w(octal(sum,7),148,7); buf[155]=0x20;
        return buf;
      };
      const chunks = [];
      const addFile = (path, text) => {
        const data = enc.encode(text);
        chunks.push(header(path, data.length));
        chunks.push(data);
        const pad = (512 - (data.length % 512)) % 512;
        if (pad) chunks.push(new Uint8Array(pad));
      };

      // 1) Backend files (embedded) at the TAR root, captain-definition, Dockerfile, src/server.js …
      const backend = window.DEPLOY_BACKEND_FILES || {};
      const backendCount = Object.keys(backend).length;
      if (backendCount === 0) throw new Error('Backend-Dateien fehlen (deploy-bundle.jsx nicht geladen).');
      for (const [path, text] of Object.entries(backend)) addFile(path, text);

      // 2) Frontend files under public/
      for (const rel of FRONTEND_FILES) {
        const res = await fetch(rel, { cache: 'no-store' });
        if (!res.ok) throw new Error('Konnte nicht laden: ' + rel);
        addFile('public/' + rel, await res.text());
      }

      chunks.push(new Uint8Array(1024));
      let total = 0; chunks.forEach(c => total += c.length);
      const out = new Uint8Array(total); let off = 0; chunks.forEach(c => { out.set(c, off); off += c.length; });
      const url = URL.createObjectURL(new Blob([out], { type: 'application/x-tar' }));
      const a = document.createElement('a');
      a.href = url; a.download = `malfino-caprover-${new Date().toISOString().slice(0,10)}.tar`;
      document.body.appendChild(a); a.click(); a.remove();
      setTimeout(() => URL.revokeObjectURL(url), 1000);
      setMsg(`Fertig! Komplettes Paket: ${backendCount} Backend- + ${FRONTEND_FILES.length} Frontend-Dateien · ${(total/1024).toFixed(0)} KB. Direkt bei CapRover als Tarball hochladbar.`);
    } catch (e) {
      setMsg('Fehler: ' + e.message);
    } finally { setBusy(false); }
  };

  return (
    <div className="grid lg:grid-cols-2 gap-5">
      <Card title="CapRover-Paket erzeugen" subtitle="Komplette TAR (Backend + Frontend) zum direkten Hochladen.">
        <p className="text-sm text-inkSoft leading-relaxed mb-4">
          Erstellt die vollständige <strong>malfino-caprover.tar</strong>, mit Node-Backend, Datenbank-Schema,
          PayPal- &amp; E-Mail-Anbindung, <code className="bg-cream px-1.5 py-0.5 rounded">Dockerfile</code>,
          <code className="bg-cream px-1.5 py-0.5 rounded mx-1">captain-definition</code> und dem aktuellen Frontend
          unter <code className="bg-cream px-1.5 py-0.5 rounded">public/</code>. Nichts selbst zusammenbauen.
        </p>
        <Btn size="lg" leading={<IconDownload size={18}/>} onClick={buildTar}
          className={busy ? 'opacity-60 pointer-events-none' : ''}>
          {busy ? 'Wird erstellt …' : 'Komplettes CapRover-TAR herunterladen'}
        </Btn>
        {msg && (
          <div className={cls("mt-4 text-[13px] rounded-xl px-3 py-2 ring-1",
            msg.startsWith('Fehler') ? 'bg-coral/10 ring-coral/30 text-coralDeep' : 'bg-mint/40 ring-mint text-emerald-900')}>
            {msg}
          </div>
        )}
      </Card>

      <Card title="So deployst du">
        <ol className="text-sm text-inkSoft leading-relaxed grid gap-2 list-decimal pl-5">
          <li>TAR herunterladen (Button links).</li>
          <li>CapRover → App <code className="bg-cream px-1.5 py-0.5 rounded">ausmalbilder</code> → Tab <strong>Deployment</strong> → <strong>Method 2: Tarball</strong>.</li>
          <li>TAR hochladen, CapRover baut &amp; startet automatisch.</li>
          <li>Env-Variablen (DATABASE_URL, JWT_SECRET, PayPal, SMTP) müssen gesetzt sein.</li>
        </ol>
        <div className="mt-3 p-3 rounded-xl bg-sky/30 ring-1 ring-sky text-[12px]">
          Die Datei enthält <code className="bg-paper px-1.5 py-0.5 rounded">captain-definition</code> im Wurzelverzeichnis, also direkt von CapRover deploybar.
        </div>
      </Card>
    </div>
  );
};

const PremiumCard = ({ premium, sample = false, onPurchase, currentPlan }) => {
  const sym = premium.currency === 'EUR' ? '€' : premium.currency === 'USD' ? '$' : premium.currency;
  return (
    <div className="rounded-3xl bg-gradient-to-br from-sun/60 via-paper to-coral/20 ring-2 ring-sun p-6 sm:p-8 relative overflow-hidden">
      <span className="absolute top-3 right-4 text-4xl twinkle">⭐</span>
      <span className="absolute bottom-4 left-6 text-3xl twinkle" style={{animationDelay:'0.4s'}}>✨</span>
      <div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-ink text-white text-[11px] font-extrabold tracking-wide mb-3">
        ⭐ {premium.name}
      </div>
      <h3 className="font-display text-3xl font-extrabold tracking-tight mb-2">Mehr Sammelmappen, kein Schnickschnack.</h3>
      <p className="text-inkSoft mb-5 max-w-md">Unterstütze uns und schalte alle Komfort-Funktionen frei.</p>
      <div className="flex items-end gap-2 mb-5">
        <span className="font-display text-5xl font-extrabold leading-none">{sym}{premium.priceMonthly.toFixed(2)}</span>
        <span className="text-inkSoft text-sm pb-1">/Monat</span>
        <span className="ml-2 text-[12px] text-inkSoft pb-1">oder {sym}{premium.priceYearly.toFixed(2)} /Jahr</span>
      </div>
      <ul className="grid gap-2 mb-6">
        {Object.entries(premium.features).filter(([, on]) => on).map(([key]) => (
          <li key={key} className="flex items-center gap-2 text-sm font-semibold">
            <span className="inline-flex w-5 h-5 rounded-full bg-emerald-600 text-white items-center justify-center"><IconCheck size={12}/></span>
            {premium.descriptions[key] || key}
          </li>
        ))}
      </ul>
      {sample ? (
        <Btn size="lg" disabled className="opacity-60 cursor-not-allowed">Vorschau (nicht klickbar)</Btn>
      ) : currentPlan === 'active' ? (
        <div className="inline-flex items-center gap-2 px-4 py-3 rounded-full bg-emerald-600 text-white font-bold">
          <IconCheck size={16}/> Du bist Premium-Mitglied
        </div>
      ) : (
        <div className="flex flex-wrap gap-2">
          {premium.billing?.monthly !== false && (
            <Btn size="lg" leading={<span aria-hidden="true">⭐</span>}
              onClick={() => onPurchase && onPurchase('monthly')}>
              Abo {sym}{premium.priceMonthly.toFixed(2)}/Mon.
            </Btn>
          )}
          {premium.billing?.yearly !== false && (
            <Btn size="lg" variant="secondary"
              onClick={() => onPurchase && onPurchase('yearly')}>
              Abo {sym}{premium.priceYearly.toFixed(2)}/Jahr <span className="ml-2 text-[10px] font-bold bg-sun text-ink px-2 py-0.5 rounded-full">sparen</span>
            </Btn>
          )}
          {premium.billing?.once && (
            <Btn size="lg" variant="soft"
              onClick={() => onPurchase && onPurchase('once')}>
              1 Monat {sym}{(premium.priceOnce ?? 0).toFixed(2)} <span className="ml-2 text-[10px] font-bold bg-cream ring-1 ring-line text-ink px-2 py-0.5 rounded-full">einmalig</span>
            </Btn>
          )}
        </div>
      )}
    </div>
  );
};

Object.assign(window, {
  AdminShell, RequireAdmin, AdminLoginGate,
  AdminDashboardPage, AdminUploadPage, AdminCategoriesPage,
  AdminColoringsPage, AdminProfilesPage, AdminStatsPage, AdminSettingsPage,
  SettingsGeneral, SettingsPassword, SettingsAdSense, SettingsPremium,
  SettingsPayments, SettingsReferral, SettingsDeployment, SettingsStaticPages,
  Card, Field, Input, FileDrop, Toggle, PremiumCard,
  CoverUpload, CoverPickerButton,
});
