// tnych-admin.jsx — Admin panel, clean redesign
const { useState, useMemo, useEffect } = React;

function useMobile() {
  const [m, setM] = useState(typeof window !== 'undefined' && window.innerWidth < 720);
  useEffect(() => {
    const fn = () => setM(window.innerWidth < 720);
    window.addEventListener('resize', fn);return () => window.removeEventListener('resize', fn);
  }, []);
  return m;
}

/* ── Icons ── */
const ILink = ({ s = 16 }) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" /><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" /></svg>;
const IPlus = ({ s = 16 }) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" /></svg>;
const ISettings = ({ s = 16 }) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="3" /><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" /></svg>;
const IMenu = ({ s = 18 }) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="18" x2="21" y2="18" /></svg>;
const IEdit = ({ s = 14 }) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" /><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" /></svg>;
const ICopy = ({ s = 14 }) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" /><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" /></svg>;
const IQR = ({ s = 14 }) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="5" height="5" /><rect x="16" y="3" width="5" height="5" /><rect x="3" y="16" width="5" height="5" /><path d="M21 16h-3a2 2 0 0 0-2 2v3" /><path d="M21 21v.01" /><path d="M12 7v3a2 2 0 0 1-2 2H7" /><path d="M3 12h.01" /><path d="M12 3h.01" /><path d="M12 16v.01" /><path d="M16 12h1" /><path d="M21 12v.01" /></svg>;
const ITrash = ({ s = 14 }) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6" /><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></svg>;
const ICheck = ({ s = 14 }) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12" /></svg>;
const IChev = ({ s = 13, up = false }) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ transition: 'transform 0.2s', transform: up ? 'rotate(180deg)' : 'none', flexShrink: 0 }}><polyline points="6 9 12 15 18 9" /></svg>;
const IClose = ({ s = 18 }) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></svg>;
const IList = ({ s = 16 }) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="8" y1="6" x2="21" y2="6" /><line x1="8" y1="12" x2="21" y2="12" /><line x1="8" y1="18" x2="21" y2="18" /><line x1="3" y1="6" x2="3.01" y2="6" /><line x1="3" y1="12" x2="3.01" y2="12" /><line x1="3" y1="18" x2="3.01" y2="18" /></svg>;
const IData = ({ s = 14 }) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>;



/* ── Sidebar ── */
function SidebarContent({ nav, setNav, domain, onLogout, onBack, onNew, onClose }) {
  return (
    <>
      <div style={{ padding: '20px 16px 16px', borderBottom: '1px solid var(--border)' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <div style={{ width: 32, height: 32, borderRadius: 9, background: 'var(--accent-dim)', border: '1px solid var(--border)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--accent)', flexShrink: 0 }}>
              <ILink s={15} />
            </div>
            <div>
              <div style={{ fontSize: 13, fontWeight: 700, letterSpacing: '-0.02em', color: 'var(--text)' }}>{domain}</div>
              <div style={{ fontSize: 10, color: 'var(--text-dim)', letterSpacing: '0.08em', textTransform: 'uppercase', fontWeight: 600, marginTop: 1 }}>admin</div>
            </div>
          </div>
          {onClose && <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-dim)', display: 'flex', padding: 4 }}><IClose s={15} /></button>}
        </div>
        <button className="btn btn-primary btn-sm" onClick={() => {onNew();onClose && onClose();}} style={{ width: '100%', justifyContent: 'center', gap: 5 }}>
          <IPlus s={13} /> New Link
        </button>
      </div>

      <nav style={{ flex: 1, padding: '8px' }}>
        {[{ id: 'links', icon: <ILink s={14} />, label: 'Links' }, { id: 'logs', icon: <IList s={14} />, label: 'Logs' }, { id: 'settings', icon: <ISettings s={14} />, label: 'Settings' }].map((item) =>
        <button key={item.id} onClick={() => {setNav(item.id);onClose && onClose();}} style={{
          display: 'flex', alignItems: 'center', gap: 9, width: '100%',
          padding: '9px 11px', borderRadius: 8, border: 'none', cursor: 'pointer',
          fontFamily: 'inherit', fontSize: 13, fontWeight: 500, textAlign: 'left',
          background: nav === item.id ? 'var(--accent-dim)' : 'transparent',
          color: nav === item.id ? 'var(--accent)' : 'var(--text-muted)',
          transition: 'all 0.15s'
        }}>{item.icon}{item.label}</button>
        )}
      </nav>

      <div style={{ padding: '8px', borderTop: '1px solid var(--border)' }}>
        <button onClick={onBack} className="btn btn-ghost btn-sm" style={{ justifyContent: 'flex-start', width: '100%', marginBottom: 2 }}>← back to site</button>
        <button onClick={onLogout} style={{ background: 'none', border: 'none', cursor: 'pointer', fontFamily: 'inherit', fontSize: 12, color: 'var(--text-dim)', padding: '6px 11px', textAlign: 'left', transition: 'color 0.15s', borderRadius: 6, width: '100%' }}
        onMouseEnter={(e) => e.currentTarget.style.color = 'var(--text-muted)'}
        onMouseLeave={(e) => e.currentTarget.style.color = 'var(--text-dim)'}>Log out</button>
      </div>
    </>);

}

/* ── Stats strip — 3 stats, no Traps ── */
function StatsStrip({ links, isMobile }) {
  const total = links.length;
  const clicks = links.reduce((a, l) => a + (l.clicks || 0), 0).toLocaleString();
  const pubCount = links.filter((l) => l.visibility === 'public' && !l.scammerTrap).length;

  const stats = [
  { label: 'Links', value: total, color: 'var(--accent)' },
  { label: 'Clicks', value: clicks, color: 'var(--text)' },
  { label: 'Public', value: pubCount, color: 'var(--green)' }];


  return (
    <div style={{
      display: 'flex', alignItems: 'stretch',
      background: 'var(--surface)', border: '1px solid var(--border)',
      borderRadius: 12, overflow: 'hidden', marginBottom: 24
    }}>
      {stats.map((s, i) =>
      <div key={s.label} style={{
        flex: 1, padding: isMobile ? '14px 16px' : '16px 20px',
        borderRight: i < stats.length - 1 ? '1px solid var(--border)' : 'none'
      }}>
          <div style={{ fontSize: isMobile ? 22 : 26, fontWeight: 700, letterSpacing: '-0.03em', color: s.color, lineHeight: 1 }}>{s.value}</div>
          <div style={{ fontSize: 10, color: 'var(--text-dim)', marginTop: 5, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.07em' }}>{s.label}</div>
        </div>
      )}
    </div>);

}

/* ── Link row ── */
function LinkRow({ link, domain, expanded, onToggle, onEdit, onQR, onDelete, onViewData, isMobile }) {
  const [copied, setCopied] = useState(false);
  const [confirmDel, setConfirmDel] = useState(false);

  const vis = {
    public: { cls: 'badge-green', l: 'public' },
    private: { cls: 'badge-muted', l: 'private' },
    restricted: { cls: 'badge-amber', l: 'restricted' }
  }[link.visibility] || { cls: 'badge-muted', l: link.visibility };

  const copy = (e) => {
    e.stopPropagation();
    navigator.clipboard.writeText(`https://${domain}/${link.shortCode}`).catch(() => {});
    setCopied(true);setTimeout(() => setCopied(false), 1400);
  };
  const tryDel = (e) => {
    e.stopPropagation();
    if (confirmDel) {onDelete(link.id);return;}
    setConfirmDel(true);setTimeout(() => setConfirmDel(false), 2200);
  };

  const hasUtm = link.utmSource || link.utmMedium || link.utmCampaign;
  const fmt = (d) => new Date(d).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });

  return (
    <div style={{ borderBottom: '1px solid var(--border)', background: expanded ? 'var(--surface-hi)' : 'transparent', transition: 'background 0.15s' }}>

      {/* Row */}
      <div onClick={onToggle} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: isMobile ? '11px 14px' : '10px 18px', cursor: 'pointer', userSelect: 'none' }}>
        <span style={{ width: 6, height: 6, borderRadius: '50%', background: link.active ? 'var(--green)' : 'var(--text-dim)', flexShrink: 0 }} />
        <code style={{ fontSize: 12, fontWeight: 700, color: 'var(--accent)', background: 'var(--accent-dim)', borderRadius: 5, padding: '2px 8px', flexShrink: 0, fontFamily: "'Space Grotesk',monospace", letterSpacing: '0.01em' }}>/{link.shortCode}</code>
        {!isMobile && link.label && <span style={{ fontSize: 13, color: 'var(--text-muted)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: '0 1 auto', maxWidth: 180 }}>{link.label}</span>}
        {!isMobile && <span style={{ fontSize: 12, color: 'var(--text-muted)', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', minWidth: 0 }}>{link.dest}</span>}
        {isMobile && <span style={{ flex: 1 }} />}
        <span style={{ fontSize: 12, color: 'var(--text-muted)', flexShrink: 0, minWidth: 36, textAlign: 'right', fontVariantNumeric: 'tabular-nums' }}>{(link.clicks || 0).toLocaleString()}</span>
        {!isMobile && <span className={`badge ${vis.cls}`} style={{ flexShrink: 0 }}>{vis.l}</span>}
        {link.scammerTrap && <span className="badge badge-amber" style={{ flexShrink: 0 }}>trap</span>}
        {link.trackDetails && !link.scammerTrap && !isMobile && <span className="badge badge-accent">tracking</span>}
        <IChev up={expanded} />
      </div>

      {/* Expanded detail */}
      <div style={{ overflow: 'hidden', maxHeight: expanded ? 440 : 0, opacity: expanded ? 1 : 0, transition: 'max-height 0.28s ease, opacity 0.22s ease' }}>
        <div style={{ padding: '2px 18px 16px', display: 'flex', flexDirection: 'column', gap: 12 }}>
          <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: 12, fontSize: 12, lineHeight: 1.75 }}>
            <div>
              <div style={{ fontSize: 10, color: 'var(--text-dim)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 4 }}>Destination</div>
              <div style={{ color: 'var(--text-muted)', wordBreak: 'break-all' }}>{link.dest}</div>
            </div>
            <div>
              <div style={{ fontSize: 10, color: 'var(--text-dim)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 4 }}>Details</div>
              <div style={{ color: 'var(--text-muted)' }}>
                {link.label && <><strong style={{ color: 'var(--text)', fontWeight: 600 }}>{link.label}</strong><br /></>}
                Added {fmt(link.created)}<br />
                Embed <span style={{ color: link.embedEnabled ? 'var(--green)' : 'var(--text-dim)' }}>{link.embedEnabled ? 'on' : 'off'}</span>
                {' · '}Logging <span style={{ color: link.trackDetails ? 'var(--accent)' : 'var(--text-dim)' }}>{link.trackDetails ? 'on' : 'off'}</span>
              </div>
            </div>
            {hasUtm &&
            <div style={{ gridColumn: isMobile ? '1' : 'span 2' }}>
                <div style={{ fontSize: 10, color: 'var(--text-dim)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 4 }}>UTM Parameters</div>
                <code style={{ fontSize: 12, color: 'var(--text-muted)', background: 'var(--surface-2)', borderRadius: 6, padding: '6px 10px', display: 'block', fontFamily: "'Space Grotesk',monospace" }}>
                  {[link.utmSource && `src=${link.utmSource}`, link.utmMedium && `med=${link.utmMedium}`, link.utmCampaign && `cam=${link.utmCampaign}`].filter(Boolean).join(' · ')}
                </code>
              </div>
            }
          </div>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
            <button className="btn btn-surface btn-sm" onClick={(e) => {e.stopPropagation();onEdit(link);}} style={{ gap: 5 }}><IEdit s={13} />Edit</button>
            <button className="btn btn-surface btn-sm" onClick={copy} style={{ gap: 5, color: copied ? 'var(--green)' : undefined }}>
              {copied ? <ICheck s={13} /> : <ICopy s={13} />}{isMobile ? copied ? '✓' : 'Copy' : copied ? 'Copied!' : 'Copy URL'}
            </button>
            <button className="btn btn-surface btn-sm" onClick={(e) => {e.stopPropagation();onQR(link);}} style={{ gap: 5 }}><IQR s={13} />{isMobile ? 'QR' : 'QR Code'}</button>
            {link.scammerTrap && onViewData && (
              <button className="btn btn-sm" onClick={(e) => { e.stopPropagation(); onViewData(link); }}
                style={{ gap: 5, background: 'oklch(72% 0.16 52 / 0.1)', color: 'var(--amber)', border: '1px solid oklch(72% 0.16 52 / 0.28)' }}>
                <IData s={13} />View Data
              </button>
            )}
            <button className="btn btn-danger btn-sm" onClick={tryDel} style={{ gap: 5, marginLeft: 'auto', background: confirmDel ? 'oklch(62% 0.17 25 / 0.25)' : undefined }}>
              <ITrash s={13} />{confirmDel ? 'Sure?' : 'Delete'}
            </button>
          </div>
        </div>
      </div>
    </div>);

}

/* ── Sections ── */
const SECTIONS = [
{ id: 'public', label: 'Public', dot: 'var(--green)', filter: (l) => l.visibility === 'public' && !l.scammerTrap },
{ id: 'private', label: 'Private & Restricted', dot: 'var(--text-muted)', filter: (l) => (l.visibility === 'private' || l.visibility === 'restricted') && !l.scammerTrap },
{ id: 'traps', label: 'Traps', dot: 'oklch(72% 0.16 52)', filter: (l) => l.scammerTrap }];


function LinksView({ links, allLinks, domain, expandedId, setExpandedId, searchQ, setSearchQ, onNew, onEdit, onQR, onDelete, onViewTrap, isMobile }) {
  return (
    <div>
      {/* Header */}
      {!isMobile &&
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 22 }}>
          <div>
            <h1 style={{ fontSize: 20, fontWeight: 700, letterSpacing: '-0.025em', color: 'var(--text)', margin: 0 }}>Links</h1>
          </div>
          <button className="btn btn-primary btn-sm" onClick={onNew} style={{ gap: 5 }}><IPlus s={13} />New Link</button>
        </div>
      }

      {/* Stats */}
      <StatsStrip links={allLinks} isMobile={isMobile} />

      {/* Search */}
      <input
        className="input input-sm"
        value={searchQ}
        onChange={(e) => setSearchQ(e.target.value)}
        placeholder="Search links…"
        style={{ marginBottom: 22, maxWidth: isMobile ? '100%' : 280 }} />
      

      {/* Sections */}
      {SECTIONS.map((section) => {
        const sl = links.filter(section.filter);
        if (!sl.length) return null;
        return (
          <div key={section.id} style={{ marginBottom: 24 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 8 }}>
              <span style={{ width: 6, height: 6, borderRadius: '50%', background: section.dot, flexShrink: 0 }} />
              <span style={{ fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.09em', color: 'var(--text-muted)' }}>{section.label}</span>
              <span style={{ fontSize: 11, color: 'var(--text-dim)', fontVariantNumeric: 'tabular-nums' }}>{sl.length}</span>
            </div>
            <div style={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 10, overflow: 'hidden' }}>
              {sl.map((link) =>
              <LinkRow key={link.id} link={link} domain={domain} isMobile={isMobile}
              expanded={expandedId === link.id}
              onToggle={() => setExpandedId(expandedId === link.id ? null : link.id)}
              onEdit={onEdit} onQR={onQR} onDelete={onDelete} onViewData={onViewTrap} />

              )}
            </div>
          </div>);

      })}

      {links.length === 0 &&
      <div style={{ padding: '48px 20px', textAlign: 'center', color: 'var(--text-dim)', fontSize: 14 }}>
          No links match.{' '}
          <button onClick={onNew} style={{ color: 'var(--accent)', background: 'none', border: 'none', cursor: 'pointer', fontFamily: 'inherit', fontSize: 14, fontWeight: 600 }}>Create one →</button>
        </div>
      }
    </div>);

}

/* LogsView is provided by tnych-logs.jsx via window.LogsView */

/* ── Settings ── */
function SettingsView({ domain, domains, onDomainsChange, onDomainChange }) {
  const [newDom, setNewDom] = useState('');
  const [domErr, setDomErr] = useState('');

  const addDomain = () => {
    const d = newDom.trim().toLowerCase().replace(/^https?:\/\//,'').replace(/\/$/,'');
    if (!d) return;
    if (domains.includes(d)) { setDomErr('Already in list.'); return; }
    onDomainsChange([...domains, d]);
    setNewDom(''); setDomErr('');
  };
  const removeDomain = (d) => {
    if (domains.length <= 1) return;
    onDomainsChange(domains.filter(x => x !== d));
  };

  return (
    <div>
      <h1 style={{ fontSize: 20, fontWeight: 700, letterSpacing: '-0.025em', marginBottom: 22, color: 'var(--text)' }}>Settings</h1>
      <div style={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 12, overflow: 'hidden' }}>
        <div style={{ padding: '18px 22px' }}>
          <div style={{ fontSize: 10, color: 'var(--text-muted)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 12 }}>Domains</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 7, marginBottom: 12 }}>
            {domains.map(d => (
              <div key={d} style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <code style={{ fontSize: 13, fontWeight: 600, color: 'var(--accent)', flex: 1, fontFamily: "'Space Grotesk',monospace" }}>{d}</code>
                {d === domain
                  ? <span className="badge badge-accent" style={{ flexShrink: 0 }}>active</span>
                  : <button onClick={() => onDomainChange(d)} className="btn btn-surface btn-xs" style={{ flexShrink: 0 }}>use</button>
                }
                <button
                  onClick={() => removeDomain(d)}
                  disabled={domains.length <= 1}
                  className="btn btn-danger btn-xs"
                  style={{ flexShrink: 0 }}
                  title={domains.length <= 1 ? 'Cannot remove the last domain' : `Remove ${d}`}
                >remove</button>
              </div>
            ))}
          </div>
          <div style={{ display: 'flex', gap: 6 }}>
            <input
              className="input input-sm"
              value={newDom}
              onChange={e => { setNewDom(e.target.value); setDomErr(''); }}
              onKeyDown={e => e.key === 'Enter' && addDomain()}
              placeholder="newdomain.com"
              style={{ flex: 1 }}
            />
            <button className="btn btn-surface btn-sm" onClick={addDomain} style={{ flexShrink: 0 }}>+ Add</button>
          </div>
          {domErr && <p style={{ fontSize: 12, color: 'var(--danger)', marginTop: 6 }}>{domErr}</p>}
        </div>
      </div>
    </div>);
}

/* ── Admin root ── */
function Admin({ domain, onLogout, onBack, onDomainChange }) {
  const [links, setLinks] = useState([]);
  const [loading, setLoading] = useState(true);
  const [domains, setDomains] = useState(['tnych.cc', 'tnych.dev']);
  const [nav, setNav] = useState('links');
  const [expandedId, setExpandedId] = useState(null);
  const [searchQ, setSearchQ] = useState('');
  const [modalOpen, setModalOpen] = useState(false);
  const [editingLink, setEditingLink] = useState(null);
  const [qrLink, setQrLink] = useState(null);
  const [trapViewLink, setTrapViewLink] = useState(null);
  const [drawer, setDrawer] = useState(false);
  const isMobile = useMobile();

  // any 401 means the session expired — bounce back to login
  const authed = (r) => {
    if (r.status === 401) { onLogout(); throw new Error('Session expired'); }
    return r.json();
  };

  useEffect(() => {
    fetch('/api/links').then(authed).then(setLinks).catch(e => console.error(e)).finally(() => setLoading(false));
    fetch('/api/domains').then(authed).then(setDomains).catch(e => console.error(e));
  }, []);

  const handleDomainsChange = (next) => {
    const added = next.filter(d => !domains.includes(d));
    const removed = domains.filter(d => !next.includes(d));
    setDomains(next);
    added.forEach(d => fetch('/api/domains', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ domain: d }) }).catch(e => console.error(e)));
    removed.forEach(d => fetch(`/api/domains/${encodeURIComponent(d)}`, { method: 'DELETE' }).catch(e => console.error(e)));
  };

  const filtered = useMemo(() => links.filter((l) => {
    if (!searchQ) return true;
    const q = searchQ.toLowerCase();
    return l.shortCode.includes(q) || l.dest.toLowerCase().includes(q) || (l.label || '').toLowerCase().includes(q);
  }), [links, searchQ]);

  const openNew = () => {setEditingLink(null);setModalOpen(true);};
  const openEdit = (link) => {setEditingLink(link);setModalOpen(true);};
  const handleSave = (data) => {
    const method = data.id ? 'PUT' : 'POST';
    const url = data.id ? `/api/links/${data.id}` : '/api/links';
    fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) })
      .then(authed)
      .then((resp) => {
        if (resp && resp.error) { console.error('Save error:', resp.error); return; }
        const norm = { ...data, active: data.active ? 1 : 0, scammerTrap: data.scammerTrap ? 1 : 0, trackDetails: data.trackDetails ? 1 : 0, embedEnabled: data.embedEnabled ? 1 : 0 };
        setLinks(prev => data.id
          ? prev.map(l => l.id === data.id ? { ...l, ...norm } : l)
          : [...prev, { ...norm, id: resp.id, clicks: 0, created: resp.created }]);
        setModalOpen(false);
      })
      .catch(e => console.error('Save error:', e));
  };
  const handleDelete = (id) => {
    fetch(`/api/links/${id}`, { method: 'DELETE' })
      .then(authed)
      .then(() => {
        setLinks(prev => prev.filter(l => l.id !== id));
        if (expandedId === id) setExpandedId(null);
      })
      .catch(e => console.error('Delete error:', e));
  };

  const LinkModal = window.LinkModal;
  const QRModal = window.QRModal;
  const LogsView = window.LogsView;
  const TrapDataModal = window.TrapDataModal;

  const sidebarBase = { display: 'flex', flexDirection: 'column', height: '100%', background: 'var(--surface)', borderRight: '1px solid var(--border)' };

  return (
    <div style={{ display: 'flex', height: '100vh', overflow: 'hidden' }}>

      {/* Desktop sidebar */}
      {!isMobile &&
      <aside style={{ ...sidebarBase, width: 210, minWidth: 210, flexShrink: 0 }}>
          <SidebarContent nav={nav} setNav={setNav} domain={domain} onLogout={onLogout} onBack={onBack} onNew={openNew} />
        </aside>
      }

      {/* Mobile drawer backdrop */}
      {isMobile && drawer &&
      <div onClick={() => setDrawer(false)} style={{ position: 'fixed', inset: 0, background: 'oklch(0% 0 0 / 0.5)', zIndex: 299, backdropFilter: 'blur(2px)' }} />
      }

      {/* Mobile drawer */}
      {isMobile &&
      <aside style={{ ...sidebarBase, position: 'fixed', top: 0, left: 0, bottom: 0, width: 236, zIndex: 300, transform: drawer ? 'translateX(0)' : 'translateX(-100%)', transition: 'transform 0.24s ease', boxShadow: drawer ? '8px 0 40px oklch(0% 0 0 / 0.4)' : 'none' }}>
          <SidebarContent nav={nav} setNav={setNav} domain={domain} onLogout={onLogout} onBack={onBack} onNew={openNew} onClose={() => setDrawer(false)} />
        </aside>
      }

      {/* Main */}
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>

        {/* Mobile topbar */}
        {isMobile &&
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '11px 14px', background: 'var(--surface)', borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
            <button onClick={() => setDrawer((d) => !d)} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text)', display: 'flex', alignItems: 'center', padding: 4, borderRadius: 6 }}>
              <IMenu />
            </button>
            <span style={{ flex: 1, fontSize: 13, fontWeight: 700 }}>{domain}</span>
            <button className="btn btn-primary btn-sm" onClick={openNew} style={{ gap: 4 }}><IPlus s={12} />New</button>
          </div>
        }

        <main style={{ flex: 1, overflowY: 'auto', padding: isMobile ? '20px 14px' : '28px 32px', background: 'var(--bg)' }} data-comment-anchor="4bfc2e1165-main-426-9">
          {nav === 'links' &&
          <LinksView
            links={filtered} allLinks={links} domain={domain} isMobile={isMobile}
            expandedId={expandedId} setExpandedId={setExpandedId}
            searchQ={searchQ} setSearchQ={setSearchQ}
            onNew={openNew} onEdit={openEdit} onQR={setQrLink} onDelete={handleDelete}
            onViewTrap={(link) => setTrapViewLink(link)} />

          }
          {nav === 'logs' && <LogsView isMobile={isMobile} />}
          {nav === 'settings' && <SettingsView domain={domain} domains={domains} onDomainsChange={handleDomainsChange} onDomainChange={onDomainChange} />}
        </main>
      </div>

      {modalOpen && LinkModal && <LinkModal link={editingLink} domain={domain} allLinks={links} onSave={handleSave} onClose={() => setModalOpen(false)} />}
      {qrLink && QRModal && <QRModal link={qrLink} domain={domain} onClose={() => setQrLink(null)} />}
      {trapViewLink && TrapDataModal && <TrapDataModal link={trapViewLink} onClose={() => setTrapViewLink(null)} />}
    </div>);

}

Object.assign(window, { Admin });