Feat: Add soft-UI landing page with Lucide

This commit is contained in:
2025-09-26 19:37:57 -07:00
parent 3d4bd0879c
commit 8ebebb9d8a

View File

@@ -1,110 +1,177 @@
<!doctype html><html lang=en>
<!doctype html><html lang=en class="h-full">
<head>
<meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1">
<title>4ev.link — Short links. Forever.</title>
<meta name=description content="Create simple, fast, permanent short links on 4ev.link.">
<meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1">
<title>4ev.link — Short links that live 4ev</title>
<meta name=description content="Create durable, fast, privacy-friendly short links. Simple API. Free.">
<link rel=canonical href="https://4ev.link/">
<meta name=color-scheme content="light dark">
<meta property=og:title content="4ev.link — Short links. Forever.">
<meta property=og:description content="Create simple, fast, permanent short links on 4ev.link.">
<meta property=og:type content=website><meta property=og:url content="https://4ev.link/">
<link rel=icon href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='64' height='64' viewBox='0 0 24 24'%3E%3Cpath fill='%23ff6a00' d='M10.59 13.41a1.996 1.996 0 0 1 0-2.82l2-2a2 2 0 1 1 2.82 2.83l-1 1 1.41 1.41 1-1a4 4 0 1 0-5.66-5.65l-2 2a4 4 0 0 0 0 5.65l.43.43 1.41-1.41-.41-.43Zm2.82-2.82a2 2 0 0 1 0 2.83l-2 2a2 2 0 1 1-2.82-2.83l1-1-1.41-1.41-1 1a4 4 0 1 0 5.66 5.65l2-2a4 4 0 0 0 0-5.65l-.43-.43-1.41 1.41.41.43Z'/%3E%3C/svg%3E">
<style>
:root{--bg:#0b0c0f;--card:#0f1116;--muted:#8a93a6;--fg:#e6e7eb;--brand:#ff6a00;--brand2:#ffb35c;--ok:#22c55e;--err:#ef4444;--ring:0 0 0 2px color-mix(in oklab,var(--brand) 40%,transparent)}
:root[data-t=light]{--bg:#ffffff;--card:#f6f7fb;--muted:#5b6476;--fg:#0b0c0f}
*{box-sizing:border-box}html,body{height:100%}body{margin:0;font:500 16px ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Inter,Arial,Apple Color Emoji,Segoe UI Emoji;background:radial-gradient(1200px 600px at 80% -10%,#ff6a0015,transparent 60%),radial-gradient(900px 500px at -10% 10%,#4f46e515,transparent 60%),var(--bg);color:var(--fg)}
a{color:inherit;text-decoration:none}button,input{font:inherit}
.container{max-width:1100px;margin:auto;padding:24px}
.nav{display:flex;gap:16px;align-items:center;justify-content:space-between}
.logo{display:flex;gap:10px;align-items:center}
.badge{font:700 10px/1.6 ui-monospace,monospace;background:#ffffff12;color:#fff;border:1px solid #ffffff22;padding:2px 6px;border-radius:999px;letter-spacing:.06em;text-transform:uppercase}
.nav a{opacity:.8} .nav a:hover{opacity:1}
.btn{display:inline-flex;gap:8px;align-items:center;justify-content:center;border:1px solid #ffffff22;background:linear-gradient(180deg,#ffffff10,#00000000);color:var(--fg);border-radius:10px;padding:10px 14px;cursor:pointer;transition:transform .05s ease,opacity .2s ease;will-change:transform,opacity}
.btn:active{transform:translateY(1px)} .btn[disabled]{opacity:.6;cursor:not-allowed}
.btn.brand{background:linear-gradient(180deg,color-mix(in oklab,var(--brand) 20%,transparent),transparent);border-color:color-mix(in oklab,var(--brand) 40%,transparent)}
.btn.ghost{background:transparent}
.hero{padding:60px 0 36px;text-align:center}
.h1{font-size:44px;line-height:1.08;letter-spacing:-.02em;margin:0 0 12px}
.p{margin:0 auto;color:var(--muted);max-width:740px}
.card{background:linear-gradient(180deg,#ffffff08,#0000),var(--card);border:1px solid #ffffff12;border-radius:14px;box-shadow:0 8px 40px #00000020}
.form{margin:28px auto 0;max-width:760px;display:flex;gap:10px;padding:14px}
.field{flex:1;display:flex;gap:10px;align-items:center;background:#00000025;border:1px solid #ffffff12;border-radius:10px;padding:10px 12px}
input.url{all:unset;color:var(--fg);width:100%} input.url::placeholder{color:#9aa3b2}
.msg{margin:10px auto 0;max-width:760px;color:var(--muted)}
.msg.err{color:var(--err)} .msg.ok{color:var(--ok)}
.result{margin:14px auto 0;max-width:760px;padding:12px;display:none;align-items:center;gap:10px}
.result.show{display:flex}
.code{flex:1;background:#00000025;border:1px dashed #ffffff22;border-radius:10px;padding:12px;font:600 14px ui-monospace,monospace;overflow:auto;white-space:nowrap}
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px;margin:36px 0 10px}
.card.i{padding:16px}
.card h3{margin:8px 0 4px;font-size:16px}
.card p{margin:0;color:var(--muted);font-weight:450}
footer{padding:30px 0 60px;color:var(--muted);text-align:center}
.kbd{font:600 12px ui-monospace,monospace;background:#ffffff10;padding:2px 6px;border-radius:6px;border:1px solid #ffffff22}
:focus-visible{outline:none;box-shadow:var(--ring);border-radius:10px}
@media (max-width:640px){.h1{font-size:34px}.form{flex-direction:column}.result{flex-direction:column;align-items:stretch}}
</style>
<meta name=theme-color content="#0ea5e9">
<link rel="icon" href='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="%230ea5e9"/><stop offset="1" stop-color="%238b5cf6"/></linearGradient></defs><rect width="64" height="64" rx="14" fill="white"/><g fill="url(%23g)"><path d="M17 35a8 8 0 0 1 0-11l7-7a8 8 0 0 1 11 0l2 2-4 4-2-2a3 3 0 0 0-4 0l-7 7a3 3 0 0 0 0 4l2 2-4 4-1-1Z"/><path d="M47 29a8 8 0 0 1 0 11l-7 7a8 8 0 0 1-11 0l-2-2 4-4 2 2a3 3 0 0 0 4 0l7-7a3 3 0 0 0 0-4l-2-2 4-4 1 1Z"/></g></svg>'>
<meta property="og:title" content="4ev.link — Short links that live 4ev">
<meta property="og:description" content="Create durable, fast, privacy-friendly short links.">
<meta property="og:url" content="https://4ev.link/">
<meta name="twitter:card" content="summary_large_image">
<link rel="preconnect" href="https://cdn.tailwindcss.com" crossorigin>
<link rel="preconnect" href="https://unpkg.com" crossorigin>
<script>try{let t=localStorage.theme;if(!t)t=matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light';document.documentElement.classList.toggle('dark',t=='dark')}catch{}</script>
<script src="https://cdn.tailwindcss.com"></script>
<script>tailwind.config={darkMode:'class',theme:{extend:{fontFamily:{sans:['Inter','ui-sans-serif','system-ui','Segoe UI',Arial,'Apple Color Emoji','Segoe UI Emoji']},colors:{brand:{50:'#ecfeff',100:'#cffafe',200:'#a5f3fc',300:'#67e8f9',400:'#22d3ee',500:'#06b6d4',600:'#0891b2',700:'#0e7490',800:'#155e75',900:'#164e63'}},boxShadow:{soft:'0 12px 30px rgba(2,8,23,.08), inset 0 1px 0 rgba(255,255,255,.5)',softd:'0 12px 30px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.06)'}}}}</script>
<style>html,body{height:100%}::selection{background:#22d3ee20}::-moz-selection{background:#22d3ee20}</style>
</head>
<body>
<div class=container>
<nav class=nav>
<div class=logo>
<i data-lucide=link stroke-width=2.6 style="color:var(--brand)"></i>
<strong>4ev.link</strong>
<span class=badge>beta</span>
<body class="h-full text-slate-800 dark:text-slate-100 bg-gradient-to-b from-slate-50 to-white dark:from-slate-900 dark:to-slate-950 antialiased">
<div class="absolute inset-0 -z-10 opacity-70 pointer-events-none [mask-image:radial-gradient(60%_60%_at_50%_0%,#000_30%,transparent_80%)]">
<div class="absolute -top-24 left-1/2 -translate-x-1/2 w-[1200px] h-[600px] bg-gradient-to-r from-cyan-400/30 via-indigo-400/20 to-fuchsia-400/20 blur-3xl"></div>
</div>
<div style="display:flex;gap:8px;align-items:center">
<a class=btn ghost href="https://github.com/4ev-link/4ev.link" target=_blank rel=noopener><i data-lucide=github></i><span>GitHub</span></a>
<button id=theme class="btn ghost" aria-label="Toggle theme"><i data-lucide=sun></i></button>
</div>
</nav>
<header class=hero>
<h1 class=h1>Short links. Forever.</h1>
<p class=p>Generate simple, human-friendly slugs backed by an edge KV. No accounts. No bloat. Built to last.</p>
<form id=f class="card form" autocomplete=off>
<div class=field>
<i data-lucide=globe stroke-width=2></i>
<input class=url id=url name=url type=url placeholder="https://example.com/very/long/url" inputmode=url spellcheck=false required>
</div>
<button id=go class="btn brand" type=submit><i data-lucide=bolt></i><span>Shorten</span></button>
</form>
<div id=msg class=msg role=status aria-live=polite></div>
<div id=res class="card result" aria-live=polite>
<div class=code id=out title="Click to copy"></div>
<button id=copy class=btn><i data-lucide=copy></i><span>Copy</span></button>
<a id=open class="btn ghost" target=_blank rel=noopener><i data-lucide=external-link></i><span>Open</span></a>
<header class="sticky top-0 z-30 backdrop-blur supports-[backdrop-filter]:bg-white/50 dark:supports-[backdrop-filter]:bg-slate-900/40 border-b border-white/60 dark:border-white/5">
<div class="max-w-6xl mx-auto px-4 sm:px-6 py-3 flex items-center justify-between">
<a href="/" class="group inline-flex items-center gap-2 text-slate-900 dark:text-white font-semibold tracking-tight">
<i data-lucide="link-2" class="w-5 h-5 text-brand-500 group-hover:scale-110 transition"></i>
<span>4ev.link</span>
</a>
<nav class="flex items-center gap-2">
<a href="https://github.com/4ev-link/4ev.link" target="_blank" rel="noopener" class="p-2 rounded-xl bg-white/70 dark:bg-white/5 border border-white/80 dark:border-white/10 shadow-soft dark:shadow-softd hover:-translate-y-0.5 transition">
<i data-lucide="github" class="w-5 h-5"></i>
</a>
<button id="theme" class="p-2 rounded-xl bg-white/70 dark:bg-white/5 border border-white/80 dark:border-white/10 shadow-soft dark:shadow-softd hover:-translate-y-0.5 transition" aria-label="Toggle theme">
<i data-lucide="moon" class="w-5 h-5 dark:hidden"></i>
<i data-lucide="sun" class="w-5 h-5 hidden dark:inline"></i>
</button>
</nav>
</div>
</header>
<section class=grid aria-label="Highlights">
<article class="card i"><i data-lucide=zap></i><h3>Edge-fast</h3><p>Served from the edge with 302 redirects.</p></article>
<article class="card i"><i data-lucide=shield-check></i><h3>Safe by default</h3><p>Strict URL validation, CORS-ready API.</p></article>
<article class="card i"><i data-lucide=infinity></i><h3>Forever</h3><p>KV-backed slugs built to outlast tabs.</p></article>
<article class="card i"><i data-lucide=wand-2></i><h3>Nice slugs</h3><p>Clean, 4-char defaults; customize later.</p></article>
<article class="card i"><i data-lucide=palette></i><h3>Light/Dark</h3><p>Auto theme with manual toggle.</p></article>
<article class="card i"><i data-lucide=keyboard></i><h3>Keyboard-first</h3><p><span class=kbd>Enter</span> to shorten, <span class=kbd>⌘/Ctrl+C</span> to copy.</p></article>
</section>
<footer>© <span id=year></span> 4ev.link • Built on Cloudflare • Icons by Lucide</footer>
<main class="max-w-6xl mx-auto px-4 sm:px-6">
<section class="py-14 sm:py-20">
<div class="max-w-3xl">
<div class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full text-sm bg-white/70 dark:bg-white/5 border border-white/80 dark:border-white/10 shadow-soft dark:shadow-softd">
<i data-lucide="zap" class="w-4 h-4 text-brand-500"></i><span>Edge-fast • Privacy-first</span>
</div>
<h1 class="mt-6 text-4xl sm:text-6xl font-extrabold tracking-tight">Short links that live 4ev</h1>
<p class="mt-4 text-lg text-slate-600 dark:text-slate-300">Create durable, fast, privacy-friendly short links. No tracking pixels. Simple API.</p>
</div>
<script src="https://unpkg.com/lucide@0.462.0/dist/umd/lucide.min.js" defer></script>
<div class="mt-8 sm:mt-12 max-w-3xl">
<div class="p-4 sm:p-6 rounded-3xl bg-white/80 dark:bg-white/[.04] border border-white/80 dark:border-white/10 shadow-soft dark:shadow-softd backdrop-blur">
<form id="f" class="grid gap-3 sm:grid-cols-[1fr_auto] items-center" autocomplete="off">
<label for="u" class="sr-only">URL</label>
<div class="relative">
<i data-lucide="link" class="w-5 h-5 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400"></i>
<input id="u" name="url" type="url" inputmode="url" required placeholder="https://example.com/very/long/url" class="w-full pl-10 pr-4 py-3 rounded-2xl bg-white/80 dark:bg-white/5 border border-white/80 dark:border-white/10 shadow-inner outline-none focus:ring-2 focus:ring-brand-400/60" />
</div>
<button id="b" class="inline-flex justify-center items-center gap-2 px-5 py-3 rounded-2xl bg-gradient-to-br from-cyan-500 to-indigo-500 text-white font-semibold shadow-[0_10px_25px_rgba(14,165,233,.35)] hover:shadow-[0_14px_30px_rgba(14,165,233,.45)] active:scale-[.99] transition disabled:opacity-60 disabled:cursor-not-allowed">
<i data-lucide="scissors" class="w-5 h-5"></i><span>Shorten</span>
</button>
</form>
<div id="res" class="mt-4 sm:mt-6 hidden">
<div class="grid gap-3 sm:grid-cols-[1fr_auto_auto] items-center">
<div class="relative">
<i data-lucide="check-circle-2" class="w-5 h-5 absolute left-3 top-1/2 -translate-y-1/2 text-emerald-500"></i>
<input id="o" class="w-full pl-10 pr-4 py-3 rounded-2xl bg-white/80 dark:bg-white/5 border border-white/80 dark:border-white/10 shadow-inner text-emerald-700 dark:text-emerald-300" readonly>
</div>
<button id="cpy" class="inline-flex justify-center items-center gap-2 px-4 py-3 rounded-2xl bg-white/80 dark:bg-white/5 border border-white/80 dark:border-white/10 shadow-soft dark:shadow-softd hover:-translate-y-0.5 transition">
<i data-lucide="copy" class="w-5 h-5"></i><span>Copy</span>
</button>
<a id="go" target="_blank" rel="noopener" class="inline-flex justify-center items-center gap-2 px-4 py-3 rounded-2xl bg-white/80 dark:bg-white/5 border border-white/80 dark:border-white/10 shadow-soft dark:shadow-softd hover:-translate-y-0.5 transition">
<i data-lucide="external-link" class="w-5 h-5"></i><span>Open</span>
</a>
</div>
<p id="msg" class="mt-2 text-sm text-slate-500 dark:text-slate-400" aria-live="polite"></p>
</div>
<p id="e" class="mt-3 text-sm text-rose-600 dark:text-rose-400 hidden">Invalid URL. Please enter a full http(s) link.</p>
</div>
<p class="mt-3 text-xs text-slate-500 dark:text-slate-400">API: POST / with JSON { "url": "https://…" } → { "slug","target","shortUrl" }</p>
</div>
</section>
<section class="py-8 sm:py-10">
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="p-5 rounded-3xl bg-white/80 dark:bg-white/[.04] border border-white/80 dark:border-white/10 shadow-soft dark:shadow-softd">
<div class="flex items-center gap-2 font-semibold"><i data-lucide="zap" class="w-5 h-5 text-brand-500"></i>Edge-fast</div>
<p class="mt-1 text-sm text-slate-600 dark:text-slate-300">Global redirects with 302 from the edge.</p>
</div>
<div class="p-5 rounded-3xl bg-white/80 dark:bg-white/[.04] border border-white/80 dark:border-white/10 shadow-soft dark:shadow-softd">
<div class="flex items-center gap-2 font-semibold"><i data-lucide="hard-drive" class="w-5 h-5 text-brand-500"></i>Durable</div>
<p class="mt-1 text-sm text-slate-600 dark:text-slate-300">Stored in KV, designed to last.</p>
</div>
<div class="p-5 rounded-3xl bg-white/80 dark:bg-white/[.04] border border-white/80 dark:border-white/10 shadow-soft dark:shadow-softd">
<div class="flex items-center gap-2 font-semibold"><i data-lucide="shield" class="w-5 h-5 text-brand-500"></i>Privacy-first</div>
<p class="mt-1 text-sm text-slate-600 dark:text-slate-300">No trackers, minimal logs.</p>
</div>
<div class="p-5 rounded-3xl bg-white/80 dark:bg-white/[.04] border border-white/80 dark:border-white/10 shadow-soft dark:shadow-softd">
<div class="flex items-center gap-2 font-semibold"><i data-lucide="qr-code" class="w-5 h-5 text-brand-500"></i>QR ready</div>
<p class="mt-1 text-sm text-slate-600 dark:text-slate-300">Save/share QR in a click (soon).</p>
</div>
<div class="p-5 rounded-3xl bg-white/80 dark:bg-white/[.04] border border-white/80 dark:border-white/10 shadow-soft dark:shadow-softd">
<div class="flex items-center gap-2 font-semibold"><i data-lucide="code-2" class="w-5 h-5 text-brand-500"></i>Simple API</div>
<p class="mt-1 text-sm text-slate-600 dark:text-slate-300">POST / with JSON body, get JSON back.</p>
</div>
<div class="p-5 rounded-3xl bg-white/80 dark:bg-white/[.04] border border-white/80 dark:border-white/10 shadow-soft dark:shadow-softd">
<div class="flex items-center gap-2 font-semibold"><i data-lucide="tag" class="w-5 h-5 text-brand-500"></i>Custom slugs</div>
<p class="mt-1 text-sm text-slate-600 dark:text-slate-300">Personalized links (soon).</p>
</div>
</div>
</section>
<section class="py-8 sm:py-12">
<div class="grid md:grid-cols-2 gap-6">
<div class="p-5 rounded-3xl bg-white/80 dark:bg-white/[.04] border border-white/80 dark:border-white/10 shadow-soft dark:shadow-softd">
<h2 class="font-semibold flex items-center gap-2"><i data-lucide="workflow" class="w-5 h-5 text-brand-500"></i>How it works</h2>
<ol class="mt-2 space-y-1 text-sm text-slate-600 dark:text-slate-300 list-decimal list-inside">
<li>POST / with { "url": "https://…" }</li>
<li>Receive { "slug","target","shortUrl" }</li>
<li>Visit /:slug → 302 to target</li>
</ol>
</div>
<div class="p-5 rounded-3xl bg-white/80 dark:bg-white/[.04] border border-white/80 dark:border-white/10 shadow-soft dark:shadow-softd">
<h2 class="font-semibold flex items-center gap-2"><i data-lucide="help-circle" class="w-5 h-5 text-brand-500"></i>FAQ</h2>
<ul class="mt-2 space-y-1 text-sm text-slate-600 dark:text-slate-300">
<li><b>What URLs?</b> Any http(s) URL.</li>
<li><b>Free?</b> Yes, fair-use.</li>
<li><b>Data?</b> No tracking, minimal logs.</li>
<li><b>Export?</b> Copy/share instantly.</li>
</ul>
</div>
</div>
</section>
</main>
<footer class="mt-10 border-t border-white/70 dark:border-white/10">
<div class="max-w-6xl mx-auto px-4 sm:px-6 py-6 text-sm text-slate-500 dark:text-slate-400 flex flex-wrap gap-x-4 gap-y-2 items-center justify-between">
<span>© <span id="y"></span> 4ev.link</span>
<div class="flex items-center gap-3">
<a class="inline-flex items-center gap-1 hover:text-slate-700 dark:hover:text-slate-200" href="https://github.com/4ev-link/4ev.link" target="_blank" rel="noopener"><i data-lucide="github" class="w-4 h-4"></i>GitHub</a>
<a class="inline-flex items-center gap-1 hover:text-slate-700 dark:hover:text-slate-200" href="/"><i data-lucide="api" class="w-4 h-4"></i>API</a>
</div>
</div>
</footer>
<script src="https://unpkg.com/lucide@latest"></script>
<script>
(()=>{const d=document,e=(q,o=d)=>o.querySelector(q),A=(q,o=d)=>o.querySelectorAll(q),S=(k,v)=>localStorage.setItem(k,v),G=k=>localStorage.getItem(k),root=d.documentElement,yr=e('#year'),f=e('#f'),url=e('#url'),go=e('#go'),msg=e('#msg'),res=e('#res'),out=e('#out'),cpy=e('#copy'),op=e('#open'),th=e('#theme');yr.textContent=new Date().getFullYear();
const applyTheme=t=>{root.dataset.t=t;th.innerHTML='';th.insertAdjacentHTML('beforeend',`<i data-lucide='${t==='light'?'moon':'sun'}'></i>`);lucide&&lucide.createIcons()}
const initTheme=()=>{const pref=matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light';applyTheme(G('t')||pref)}
th.onclick=()=>{const t=root.dataset.t==='dark'?'light':'dark';applyTheme(t);S('t',t)}
const setMsg=(m,c='')=>{msg.className=`msg ${c}`;msg.textContent=m||''}
const showRes=u=>{out.textContent=u;op.href=u;res.classList.add('show')}
const clearRes=()=>{res.classList.remove('show');out.textContent='';op.removeAttribute('href')}
const valid=v=>{try{return new URL(v),true}catch{return false}}
f.onsubmit=async ev=>{ev.preventDefault();setMsg('');clearRes();let v=url.value.trim();if(!valid(v))return setMsg('Please enter a valid URL', 'err');go.disabled=true;go.style.opacity=.7;
try{const r=await fetch('/',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({url:v})});if(!r.ok){const t=await r.text();throw new Error(t||'Failed')}const j=await r.json();showRes(j.shortUrl);setMsg('Link created!', 'ok')}catch(err){setMsg(err.message||'Something went wrong','err')}finally{go.disabled=false;go.style.opacity=1}}
cpy.onclick=async()=>{let t=out.textContent;if(!t)return;try{await navigator.clipboard.writeText(t);cpy.innerHTML='<i data-lucide=check></i><span>Copied</span>';lucide.createIcons();setTimeout(()=>{cpy.innerHTML='<i data-lucide=copy></i><span>Copy</span>';lucide.createIcons()},1200)}catch{}}
out.onclick=()=>cpy.click()
d.addEventListener('keydown',ev=>{if(ev.key==='Enter'&&d.activeElement===d.body)url.focus()})
d.addEventListener('DOMContentLoaded',()=>{lucide&&lucide.createIcons();initTheme();url.focus()})
})();
(()=>{
const $=q=>document.querySelector(q),I=()=>window.lucide&&lucide.createIcons(),D=m=>{const el=$("#msg");el.textContent=m||""}
const f=$("#f"),u=$("#u"),b=$("#b"),res=$("#res"),o=$("#o"),cpy=$("#cpy"),go=$("#go"),err=$("#e"),y=$("#y"),th=$("#theme")
y.textContent=new Date().getFullYear()
addEventListener("DOMContentLoaded",I,{once:1});document.fonts?.ready?.then(I)
th.onclick=()=>{const d=document.documentElement.classList.toggle("dark");try{localStorage.theme=d?"dark":"light"}catch{};I()}
f.onsubmit=async e=>{
e.preventDefault();err.classList.add("hidden");D("");res.classList.add("hidden");b.disabled=1
let v=u.value.trim()
if(!/^https?:\/\//i.test(v))v="https://"+v
try{
const r=await fetch("/",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:v})})
if(!r.ok)throw 0
const j=await r.json()
o.value=j.shortUrl;go.href=j.shortUrl;go.target="_blank";res.classList.remove("hidden");D("Link created. It redirects with 302.");I()
}catch{err.classList.remove("hidden")}
b.disabled=0
}
cpy.onclick=async()=>{try{await navigator.clipboard.writeText(o.value);D("Copied!")}catch{D("Copy failed")}}
})()
</script>
</body>
</html>