/* Adeyy app — L3 Link detail + L4 QR customise. Live API version. */

/* Minimal valid PDF wrapping a JPEG (DCTDecode). Good enough for print handoff. */
function jpegToPdf(jpegDataUrl, wPt, hPt) {
  const b64 = jpegDataUrl.split(',')[1];
  const bin = atob(b64);
  const bytes = new Uint8Array(bin.length);
  for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
  const enc = (s) => new TextEncoder().encode(s);
  const chunks = [];
  const offsets = [];
  let pos = 0;
  const push = (data) => { chunks.push(data); pos += data.length; };
  push(enc('%PDF-1.4\n'));
  const obj = (n, body) => { offsets[n] = pos; push(enc(n + ' 0 obj\n' + body + '\nendobj\n')); };
  obj(1, '<< /Type /Catalog /Pages 2 0 R >>');
  obj(2, '<< /Type /Pages /Kids [3 0 R] /Count 1 >>');
  obj(3, '<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ' + wPt + ' ' + hPt + '] /Resources << /XObject << /Im1 4 0 R >> >> /Contents 5 0 R >>');
  offsets[4] = pos;
  push(enc('4 0 obj\n<< /Type /XObject /Subtype /Image /Width 1024 /Height 1024 /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /DCTDecode /Length ' + bytes.length + ' >>\nstream\n'));
  push(bytes);
  push(enc('\nendstream\nendobj\n'));
  const content = 'q ' + wPt + ' 0 0 ' + hPt + ' 0 0 cm /Im1 Do Q';
  obj(5, '<< /Length ' + content.length + ' >>\nstream\n' + content + '\nendstream');
  const xref = pos;
  let x = 'xref\n0 6\n0000000000 65535 f \n';
  for (let n = 1; n <= 5; n++) x += String(offsets[n]).padStart(10, '0') + ' 00000 n \n';
  x += 'trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n' + xref + '\n%%EOF';
  push(enc(x));
  const total = chunks.reduce((a, c) => a + c.length, 0);
  const out = new Uint8Array(total);
  let o = 0;
  for (const c of chunks) { out.set(c, o); o += c.length; }
  return new Blob([out], { type: 'application/pdf' });
}

function LinkDetail({ app, linkId }) {
  const D = window.AdeyyData;
  const API = window.AdeyyAPI;
  const [linkData, setLinkData] = React.useState(null);
  const [loadingDetail, setLoadingDetail] = React.useState(true);
  const [editing, setEditing] = React.useState(false);
  const [draft, setDraft] = React.useState('');
  const [draftErr, setDraftErr] = React.useState(null);
  const [saving, setSaving] = React.useState(false);
  const [confirmDelete, setConfirmDelete] = React.useState(false);
  const [deleting, setDeleting] = React.useState(false);
  const [fixEnding, setFixEnding] = React.useState(false);
  const [endDraft, setEndDraft] = React.useState('');

  /* Load full link detail from server */
  React.useEffect(() => {
    setLoadingDetail(true);
    API.getLink(linkId).then((data) => {
      if (data && data.link) {
        const adapted = API.adaptLink(data.link);
        adapted.qr = API.adaptQr(data.qr);
        adapted.history = API.adaptHistory(data.history);
        /* Populate createdBy from history */
        if (data.history && data.history.length > 0) {
          adapted.createdBy = '';
        }
        setLinkData(adapted);
      }
      setLoadingDetail(false);
    }).catch((err) => {
      console.error('[LinkDetail] load error:', err);
      setLoadingDetail(false);
    });
  }, [linkId]);

  /* Sync QR config changes from state */
  const stateLink = app.state.links.find((l) => l.id === linkId);

  if (loadingDetail) return <SkeletonTable rows={4} />;

  const link = linkData || stateLink;
  if (!link) return <EmptyState title="That link is gone." body="It was deleted. Its ending is retired and will never be reissued." cta={null} />;

  const locked = link.slugLocked || D.slugLocked(link);
  const campaign = app.state.campaigns.find((c) => c.id === link.campaignId);
  const data14 = D.series(link, 14);

  function toggleActive(next) {
    if (next && !app.state.billing.card) { app.setCardPrompt(() => app.setLinkStatus(link.id, true)); return; }
    app.setLinkStatus(link.id, next);
    /* Update local state */
    setLinkData((prev) => prev ? Object.assign({}, prev, { status: next ? 'active' : 'inactive' }) : prev);
  }

  async function saveDest() {
    const err = D.validDest(draft.trim());
    if (err) { setDraftErr(err); return; }
    setSaving(true);
    try {
      await API.updateDestination(link.id, draft.trim());
      const newHistory = [{ oldUrl: link.dest, newUrl: draft.trim(), who: app.state.user.name, when: new Date().toISOString() }].concat(link.history || []);
      setLinkData((prev) => Object.assign({}, prev, { dest: draft.trim(), history: newHistory }));
      app.update((s) => {
        const l = s.links.find((x) => x.id === link.id);
        if (l) l.dest = draft.trim();
      });
      setEditing(false);
      app.toast('Destination updated. Live on the next scan');
    } catch (e) {
      app.toast('Error: ' + e.message);
    } finally {
      setSaving(false);
    }
  }

  async function doDelete() {
    setDeleting(true);
    try {
      await API.deleteLink(link.id);
      app.update((s) => {
        s.retired = (s.retired || []).concat([link.slug]);
        s.links = s.links.filter((x) => x.id !== link.id);
      });
      setConfirmDelete(false);
      app.toast('Link deleted. The ending ' + link.slug + ' is retired for good');
      app.go('links');
    } catch (e) {
      app.toast('Error: ' + e.message);
      setDeleting(false);
    }
  }

  /* Lock slug on copy (export) */
  function handleCopied() {
    setLinkData((prev) => prev ? Object.assign({}, prev, { slugLocked: true }) : prev);
    app.update((s) => {
      const l = s.links.find((x) => x.id === link.id);
      if (l) l.slugLocked = true;
    });
  }

  return (
    <div data-screen-label="L3 Link detail">
      <div className="apage-head">
        <button className="backlink" onClick={() => app.go('links')}><Icon name="back" size={14} />Links</button>
        <h1 className="slug" style={{ fontFamily: 'var(--font-mono)', fontSize: 21, letterSpacing: '-.01em' }}>adeyy.com/{link.slug}</h1>
        <CopyBtn text={'https://adeyy.com/' + link.slug} toast={app.toast} label={'adeyy.com/' + link.slug}
          onCopied={handleCopied} />
        <StatusPill on={link.status === 'active'} />
        <span className="spacer"></span>
        <span className="mutetext" style={{ fontSize: 12.5 }}>{link.status === 'active' ? 'On · $1.00 a month' : 'Off · not billed'}</span>
        <Toggle on={link.status === 'active'} onChange={toggleActive} />
      </div>

      <div className="anatomy" data-comment-anchor="three-things">
        <div className="acell">
          <span className="atag">Permanent</span>
          <b>The QR code</b>
          <span>A picture of the short link. Print it once.</span>
        </div>
        <span className="ajoin">encodes</span>
        <div className="acell">
          <span className="atag">{locked ? 'Permanent' : 'Locks on first export or scan'}</span>
          <b className="slug">adeyy.com/{link.slug}</b>
          <span>A redirect we control. {locked ? 'Locked for life.' : 'Editable until it leaves the building.'}</span>
        </div>
        <span className="ajoin">302 →</span>
        <div className="acell editable">
          <span className="atag teal">Editable</span>
          <b style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{link.dest.replace(/^https?:\/\//, '')}</b>
          <span>The destination. Change it as often as you like.</span>
        </div>
      </div>

      <div className="detail-grid">
        <div style={{ display: 'flex', flexDirection: 'column', gap: 20, minWidth: 0 }}>

          <div className="dcard">
            <div className="dh">
              <h2>Destination</h2>
              {!editing ? (
                <button className="abtn abtn-ghost abtn-sm" onClick={() => { setDraft(link.dest); setEditing(true); setDraftErr(null); }}>
                  <Icon name="edit" size={13} />Edit destination
                </button>
              ) : null}
            </div>
            <div className="db">
              {!editing ? (
                <div className="codeblock" style={{ fontSize: 13 }}>{link.dest}</div>
              ) : (
                <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                  <input className={'ainput mono' + (draftErr ? ' err' : '')} value={draft} autoFocus
                    onChange={(e) => { setDraft(e.target.value); setDraftErr(null); }} />
                  {draftErr ? <span className="hint" style={{ color: 'var(--danger)' }}>{draftErr}</span>
                    : <span className="hint">Takes effect on the next scan or click. The QR code is untouched.</span>}
                  <div style={{ display: 'flex', gap: 8 }}>
                    <button className="abtn abtn-primary abtn-sm" onClick={saveDest} disabled={saving}>
                      {saving ? 'Saving…' : 'Save destination'}
                    </button>
                    <button className="abtn abtn-quiet abtn-sm" onClick={() => setEditing(false)}>Cancel</button>
                  </div>
                </div>
              )}
            </div>
          </div>

          <div className="dcard">
            <div className="dh">
              <h2>Last 14 days</h2>
              <button className="abtn abtn-ghost abtn-sm" onClick={() => app.go('analytics', { linkId: link.id })}>Full analytics</button>
            </div>
            <div className="db">
              <div style={{ display: 'flex', gap: 22, marginBottom: 14 }}>
                <span style={{ fontSize: 13 }}><b style={{ fontSize: 19, fontWeight: 800 }}>{D.num(link.scans)}</b> <span className="mutetext">scans</span></span>
                <span style={{ fontSize: 13 }}><b style={{ fontSize: 19, fontWeight: 800 }}>{D.num(link.clicks)}</b> <span className="mutetext">clicks</span></span>
              </div>
              <SeriesChart data={data14} height={110} />
            </div>
          </div>

          <div className="dcard">
            <div className="dh"><h2>Edit history</h2><span className="meta">{(link.history || []).length} changes</span></div>
            <div className="db">
              {(link.history || []).length === 0 ? (
                <span className="mutetext" style={{ fontSize: 13 }}>No changes yet.</span>
              ) : (link.history || []).map((h, i) => (
                <div className="hrow" key={i}>
                  <span className="hwhen">{D.fmtDateTime(h.when)}</span>
                  <span className="hurls">
                    {h.oldUrl ? (
                      <React.Fragment>
                        <span className="old">{h.oldUrl.replace(/^https?:\/\//, '')}</span>
                        <span className="arrow">→</span>
                        <span>{h.newUrl.replace(/^https?:\/\//, '')}</span>
                      </React.Fragment>
                    ) : (
                      <span>Created, pointing to {h.newUrl.replace(/^https?:\/\//, '')}</span>
                    )}
                    {h.who ? <span className="mutetext"> · <span className="hwho">{h.who}</span></span> : null}
                  </span>
                </div>
              ))}
            </div>
          </div>
        </div>

        <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
          <div className="dcard">
            <div className="dh"><h2>QR code</h2><span className="meta">Never changes</span></div>
            <div className="db" style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
              <div style={{ border: '1px solid var(--border-1)', borderRadius: 4, overflow: 'hidden' }}>
                <QrSvg slug={link.slug} config={link.qr} />
              </div>
              <button className="abtn abtn-primary" onClick={() => app.go('qr', { linkId: link.id })}>
                <Icon name="qr" size={15} />Customise and export
              </button>
            </div>
          </div>

          <div className="dcard">
            <div className="dh"><h2>Details</h2></div>
            <div className="db" style={{ display: 'flex', flexDirection: 'column', gap: 11, fontSize: 13 }}>
              <div className="row-between"><span className="mutetext">Label</span><span style={{ fontWeight: 600, textAlign: 'right' }}>{link.label || '·'}</span></div>
              <div className="row-between">
                <span className="mutetext">Ending</span>
                <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
                  <span className="slug" style={{ fontSize: 12, fontWeight: 700 }}>{link.slug}</span>
                  {locked ? (
                    <span className="slug" style={{ fontSize: 10.5, color: 'var(--ink-40)' }} title="Locked since this code was first exported or scanned">locked</span>
                  ) : (
                    <button className="abtn abtn-ghost abtn-sm" style={{ padding: '2px 8px', fontSize: 11.5 }} onClick={() => { setEndDraft(link.slug); setFixEnding(true); }}>Fix a typo</button>
                  )}
                </span>
              </div>
              <div className="row-between"><span className="mutetext">Campaign</span><span style={{ fontWeight: 600 }}>{campaign ? campaign.name : 'None'}</span></div>
              <div className="row-between"><span className="mutetext">Created</span><span className="slug" style={{ fontSize: 12 }}>{D.fmtDate(link.created)}</span></div>
              <div className="row-between">
                <span className="mutetext">Expiry</span>
                <input className="ainput" type="date" style={{ width: 'auto', padding: '4px 8px', fontSize: 12 }}
                  value={link.expiresAt || ''}
                  onChange={(e) => {
                    const v = e.target.value || null;
                    setLinkData((prev) => prev ? Object.assign({}, prev, { expiresAt: v }) : prev);
                    app.update((s) => { const l = s.links.find((x) => x.id === link.id); if (l) l.expiresAt = v; });
                    app.toast(v ? 'Expiry set. The code switches itself off on ' + D.fmtDate(v) : 'Expiry removed');
                  }} />
              </div>
              <div className="row-between"><span className="mutetext">Redirect</span><span className="slug" style={{ fontSize: 12 }}>302 · never cached</span></div>
            </div>
          </div>

          <button className="abtn abtn-quiet" style={{ color: 'var(--danger)', alignSelf: 'flex-start' }} onClick={() => setConfirmDelete(true)}>
            <Icon name="trash" size={14} />Delete link
          </button>
        </div>
      </div>

      {fixEnding ? (
        <Modal title="Fix the ending" onClose={() => setFixEnding(false)}>
          <p>
            This works only because the code has never been exported, copied or scanned.
            The moment it has, the ending locks for life, because a printed code cannot be recalled.
          </p>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
            <span className="slug" style={{ fontSize: 13, color: 'var(--ink-60)' }}>adeyy.com/</span>
            <input className="ainput mono" value={endDraft} autoFocus onChange={(e) => setEndDraft(e.target.value.toLowerCase())} />
          </div>
          {(() => {
            const issue = endDraft === link.slug ? null : D.slugProblem(endDraft, app.state.links.filter((x) => x.id !== link.id).map((x) => x.slug), app.state.retired || []);
            return (
              <React.Fragment>
                <p style={{ fontSize: 12.5, margin: '0 0 16px', color: endDraft && !issue ? 'var(--adeyy-ink)' : 'var(--danger)' }}>
                  {endDraft === link.slug ? 'Unchanged.' : (issue || 'Available.')}
                </p>
                <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
                  <button className="abtn abtn-quiet" onClick={() => setFixEnding(false)}>Cancel</button>
                  <button className="abtn abtn-primary" disabled={!!issue || endDraft === link.slug} onClick={async () => {
                    try {
                      /* Note: slug update is only allowed if not locked — server enforces this */
                      app.update((s) => { const l = s.links.find((x) => x.id === link.id); if (l) l.slug = endDraft; });
                      setLinkData((prev) => prev ? Object.assign({}, prev, { slug: endDraft }) : prev);
                      setFixEnding(false);
                      app.toast('Ending changed to ' + endDraft + '. It locks at first export or scan');
                    } catch (e) {
                      app.toast('Error: ' + e.message);
                    }
                  }}>Change ending</button>
                </div>
              </React.Fragment>
            );
          })()}
        </Modal>
      ) : null}

      {confirmDelete ? (
        <Modal title="Delete this link?" onClose={() => setConfirmDelete(false)}>
          <p>
            Anything printed with this code will land on the fallback page, forever.
            The ending <b className="slug">{link.slug}</b> is retired and never reissued, to you or anyone else.
          </p>
          <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
            <button className="abtn abtn-quiet" onClick={() => setConfirmDelete(false)}>Keep it</button>
            <button className="abtn abtn-primary" style={{ background: 'var(--danger)' }} onClick={doDelete} disabled={deleting}>
              {deleting ? 'Deleting…' : 'Delete and retire the ending'}
            </button>
          </div>
        </Modal>
      ) : null}
    </div>
  );
}

/* ---------- L4 QR customise ---------- */

const QR_FG_OPTIONS = [
  { name: 'Ink', hex: '#0E0E0C' },
  { name: 'Teal ink', hex: '#007d66' },
  { name: 'Teal', hex: '#04d5b0' },
  { name: 'Volt', hex: '#D9F542' },
  { name: 'Paper', hex: '#F4F1E8' }
];
const QR_BG_OPTIONS = [
  { name: 'Card', hex: '#FBFAF4' },
  { name: 'Paper', hex: '#F4F1E8' },
  { name: 'Ink', hex: '#0E0E0C' }
];

function QrCustomise({ app, linkId }) {
  const API = window.AdeyyAPI;
  const link = app.state.links.find((l) => l.id === linkId);
  const [cfg, setCfg] = React.useState(() => Object.assign({}, link ? link.qr : app.state.defaultQr));
  const [savingQr, setSavingQr] = React.useState(false);
  const fileRef = React.useRef(null);
  if (!link) return null;

  const ratio = AdeyyQR.contrast(cfg.fg, cfg.bg);
  const scannable = ratio >= 3.5;
  const set = (k, v) => setCfg((c) => Object.assign({}, c, { [k]: v }));

  function svgStr(quiet) {
    return AdeyyQR.svgString('https://adeyy.com/' + link.slug + '?s=q', {
      fg: cfg.fg, bg: cfg.bg, modules: cfg.modules, eyes: cfg.eyes, logo: cfg.logo, quiet: quiet == null ? 4 : quiet
    });
  }

  async function pngDataUrl() { return AdeyyQR.toPngDataUrl(svgStr(), 1024); }

  async function dl(format) {
    if (!scannable) return;
    const name = 'adeyy-' + link.slug;
    /* Any export locks the ending for life */
    const lockIt = async () => {
      app.update((s) => { const l = s.links.find((x) => x.id === link.id); if (l) l.slugLocked = true; });
    };
    if (format === 'svg') {
      AdeyyQR.download(name + '.svg', svgStr(), 'image/svg+xml');
      await lockIt();
      app.toast('SVG saved. Scales to any size in print');
    } else if (format === 'png') {
      AdeyyQR.download(name + '.png', await pngDataUrl());
      await lockIt();
      app.toast('PNG saved at 1024 px');
    } else if (format === 'pdf') {
      const img = new Image();
      img.src = await pngDataUrl();
      await new Promise((r) => { img.onload = r; });
      const c = document.createElement('canvas');
      c.width = 1024; c.height = 1024;
      const ctx = c.getContext('2d');
      ctx.fillStyle = cfg.bg; ctx.fillRect(0, 0, 1024, 1024);
      ctx.drawImage(img, 0, 0);
      const blob = jpegToPdf(c.toDataURL('image/jpeg', 0.95), 420, 420);
      const a = document.createElement('a');
      a.href = URL.createObjectURL(blob);
      a.download = name + '.pdf';
      a.click();
      await lockIt();
      app.toast('Print-ready PDF saved');
    } else if (format === 'copy') {
      try {
        const data = await fetch(await pngDataUrl()).then((r) => r.blob());
        await navigator.clipboard.write([new ClipboardItem({ 'image/png': data })]);
        await lockIt();
        app.toast('PNG copied to your clipboard');
      } catch (e) {
        app.toast('Your browser blocked the copy. Download instead');
      }
    }
  }

  async function save() {
    setSavingQr(true);
    try {
      await API.updateQrConfig(link.id, cfg);
      app.update((s) => { const l = s.links.find((x) => x.id === link.id); if (l) l.qr = Object.assign({}, cfg); });
      app.toast('Design saved. The code itself is unchanged');
      app.go('detail', { linkId: link.id });
    } catch (e) {
      app.toast('Error saving QR design: ' + e.message);
    } finally {
      setSavingQr(false);
    }
  }

  return (
    <div data-screen-label="L4 QR customise">
      <div className="apage-head">
        <button className="backlink" onClick={() => app.go('detail', { linkId: link.id })}><Icon name="back" size={14} />adeyy.com/{link.slug}</button>
        <h1>Customise QR</h1>
        <span className="spacer"></span>
        <button className="abtn abtn-quiet" onClick={() => app.go('detail', { linkId: link.id })}>Cancel</button>
        <button className="abtn abtn-primary" onClick={save} disabled={!scannable || savingQr}>
          {savingQr ? 'Saving…' : 'Save design'}
        </button>
      </div>

      <div className="qrwrap">
        <div className="qrcontrols">
          <div className="ctl">
            <label>Modules</label>
            <Seg value={cfg.modules} options={[{ value: 'square', label: 'Square' }, { value: 'rounded', label: 'Rounded' }, { value: 'dots', label: 'Dots' }]} onChange={(v) => set('modules', v)} />
          </div>
          <div className="ctl">
            <label>Eyes</label>
            <Seg value={cfg.eyes} options={[{ value: 'square', label: 'Square' }, { value: 'rounded', label: 'Rounded' }]} onChange={(v) => set('eyes', v)} />
          </div>
          <div className="ctl">
            <label>Code colour</label>
            <Swatches value={cfg.fg} options={QR_FG_OPTIONS} onChange={(v) => set('fg', v)} />
          </div>
          <div className="ctl">
            <label>Background</label>
            <Swatches value={cfg.bg} options={QR_BG_OPTIONS} onChange={(v) => set('bg', v)} />
            <span className="note">Brand colours only. The contrast check below keeps every export scannable.</span>
          </div>
          <div className="ctl">
            <label>Centre logo</label>
            <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
              <Seg value={cfg.logo ? 'logo' : 'none'} options={[{ value: 'none', label: 'None' }, { value: 'logo', label: 'Upload' }]}
                onChange={(v) => { if (v === 'none') set('logo', null); else fileRef.current.click(); }} />
              {cfg.logo ? <img src={cfg.logo} alt="logo" style={{ height: 28, border: '1px solid var(--border-1)', borderRadius: 3, background: '#fff', padding: 2 }} /> : null}
            </div>
            <input ref={fileRef} type="file" accept="image/*" style={{ display: 'none' }} onChange={(e) => {
              const f = e.target.files[0];
              if (!f) return;
              const r = new FileReader();
              r.onload = () => set('logo', r.result);
              r.readAsDataURL(f);
            }} />
            <span className="note">{cfg.logo ? 'Error correction is forced to level H so the code survives the covered centre.' : 'Error correction level Q. Adding a logo raises it to H automatically.'}</span>
          </div>
          <div className="ctl">
            <label>Quiet zone</label>
            <span className="note">Always preserved. It is part of what makes the code scannable, so it cannot be switched off.</span>
          </div>
        </div>

        <div className="qrstage">
          <div className="preview" dangerouslySetInnerHTML={{ __html: svgStr(3) }}></div>
          <div className="scancheck">
            <span className={'cdot'} style={{ background: scannable ? 'var(--adeyy)' : 'var(--danger)' }}></span>
            <span className={scannable ? 'ok' : 'bad'}>
              {scannable ? 'Scannable · contrast ' + ratio.toFixed(1) + ':1 · ECC ' + (cfg.logo ? 'H' : 'Q') : 'Will not scan · contrast ' + ratio.toFixed(1) + ':1 is too low'}
            </span>
          </div>
          <div className="dlrow">
            <button className="abtn abtn-ghost" disabled={!scannable} onClick={() => dl('png')}><Icon name="download" size={15} />PNG</button>
            <button className="abtn abtn-ghost" disabled={!scannable} onClick={() => dl('svg')}><Icon name="download" size={15} />SVG</button>
            <button className="abtn abtn-ghost" disabled={!scannable} onClick={() => dl('pdf')}><Icon name="download" size={15} />PDF</button>
            <button className="abtn abtn-ghost" disabled={!scannable} onClick={() => dl('copy')}><Icon name="copy" size={15} />Copy PNG</button>
          </div>
          <span className="mutetext" style={{ fontSize: 12, textAlign: 'center', maxWidth: 380 }}>
            Re-export as often as you like. The encoded link is <span className="slug">adeyy.com/{link.slug}</span> and can never change, so new exports and old prints always match.
          </span>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { LinkDetail, QrCustomise, jpegToPdf });
