<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Convert between EPUB and Kindle formats instantly in your browser. No upload, no server, 100% private.">
<title>boko - Ebook Converter</title>
<link rel="modulepreload" href="pkg/boko.js">
<link rel="preload" href="pkg/boko_bg.wasm" as="fetch" crossorigin>
<style>
*{margin:0;padding:0;box-sizing:border-box}:root{--primary:#2563eb;--primary-hover:#1d4ed8;--success:#10b981;--error:#ef4444;--bg:#f8fafc;--surface:#fff;--text:#1e293b;--text-muted:#64748b;--border:#e2e8f0;--radius:12px}body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,sans-serif;background:var(--bg);color:var(--text);min-height:100vh;display:flex;flex-direction:column}.container{max-width:480px;margin:0 auto;padding:2rem 1rem;flex:1;display:flex;flex-direction:column}header{text-align:center;margin-bottom:2rem}h1{font-size:2.5rem;font-weight:700;letter-spacing:-.02em}.subtitle{color:var(--text-muted);margin-top:.5rem}main{flex:1}.dropzone{background:var(--surface);border:2px dashed var(--border);border-radius:var(--radius);padding:3rem 2rem;text-align:center;cursor:pointer;transition:border-color .2s,background .2s}.dropzone:hover,.dropzone.dragover{border-color:var(--primary);background:#f0f7ff}.dropzone-content{pointer-events:none}.upload-icon{width:48px;height:48px;color:var(--text-muted);margin-bottom:1rem}.dropzone p{color:var(--text);margin-bottom:.5rem}.browse-link{color:var(--primary);text-decoration:underline;pointer-events:auto;cursor:pointer}.supported-formats{font-size:.875rem;color:var(--text-muted)!important}.file-info{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:1rem 1.25rem;display:flex;align-items:center;justify-content:space-between;margin-top:1rem}.file-details{display:flex;flex-direction:column;gap:.25rem}.file-name{font-weight:500;word-break:break-all}.file-size{font-size:.875rem;color:var(--text-muted)}.clear-btn{background:none;border:none;font-size:1.5rem;color:var(--text-muted);cursor:pointer;padding:.25rem;line-height:1}.clear-btn:hover{color:var(--error)}.conversion-options{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:1.25rem;margin-top:1rem;display:flex;align-items:center;gap:1rem}.conversion-options label{color:var(--text-muted);white-space:nowrap}.conversion-options select{flex:1;padding:.625rem 1rem;border:1px solid var(--border);border-radius:8px;font-size:1rem;background:var(--surface);cursor:pointer}.convert-btn{background:var(--primary);color:#fff;border:none;padding:.625rem 1.5rem;border-radius:8px;font-size:1rem;font-weight:500;cursor:pointer;transition:background .2s}.convert-btn:hover{background:var(--primary-hover)}.convert-btn:disabled{background:var(--border);cursor:not-allowed}.progress{margin-top:1.5rem;text-align:center}.progress-bar{background:var(--border);border-radius:4px;height:8px;overflow:hidden}.progress-fill{background:var(--primary);height:100%;width:0;transition:width .3s;animation:pulse 1.5s ease-in-out infinite}@keyframes pulse{0%,100%{opacity:1}50%{opacity:.7}}.progress-text{margin-top:.75rem;color:var(--text-muted);font-size:.875rem}.result{background:var(--surface);border:1px solid var(--success);border-radius:var(--radius);padding:2rem;margin-top:1.5rem;text-align:center}.check-icon{width:48px;height:48px;color:var(--success);margin-bottom:1rem}.result p{margin-bottom:1rem;color:var(--success);font-weight:500}.download-btn{display:inline-block;background:var(--success);color:#fff;text-decoration:none;padding:.75rem 2rem;border-radius:8px;font-weight:500;transition:opacity .2s}.download-btn:hover{opacity:.9}.error{background:#fef2f2;border:1px solid var(--error);border-radius:var(--radius);padding:2rem;margin-top:1.5rem;text-align:center}.error-icon{width:48px;height:48px;color:var(--error);margin-bottom:1rem}.error p{color:var(--error)}footer{text-align:center;padding:2rem 0;color:var(--text-muted);font-size:.875rem}footer a{color:var(--primary);text-decoration:underline}.hidden{display:none!important}@media(max-width:480px){.conversion-options{flex-direction:column;align-items:stretch}.conversion-options label{text-align:center}}
</style>
</head>
<body>
<div class="container">
<header>
<h1>boko</h1>
<p class="subtitle">Convert ebooks in your browser</p>
</header>
<main>
<div id="dropzone" class="dropzone">
<input type="file" id="file-input" accept=".epub,.azw3,.mobi,.kfx" hidden>
<div class="dropzone-content">
<svg class="upload-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="17 8 12 3 7 8"/>
<line x1="12" y1="3" x2="12" y2="15"/>
</svg>
<p>Drop your ebook here or <span class="browse-link">browse</span></p>
<p class="supported-formats">Supports EPUB, AZW3, MOBI, KFX</p>
</div>
</div>
<div id="file-info" class="file-info hidden">
<div class="file-details">
<span id="file-name" class="file-name"></span>
<span id="file-size" class="file-size"></span>
</div>
<button id="clear-file" class="clear-btn" title="Clear file">×</button>
</div>
<div id="conversion-options" class="conversion-options hidden">
<label for="output-format">Convert to:</label>
<select id="output-format">
<option value="epub">EPUB</option>
<option value="azw3">AZW3</option>
</select>
<button id="convert-btn" class="convert-btn">Convert</button>
</div>
<div id="progress" class="progress hidden">
<div class="progress-bar">
<div id="progress-fill" class="progress-fill"></div>
</div>
<p id="progress-text" class="progress-text">Converting...</p>
</div>
<div id="result" class="result hidden">
<svg class="check-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
<polyline points="22 4 12 14.01 9 11.01"/>
</svg>
<p>Conversion complete!</p>
<a id="download-link" class="download-btn" download>Download</a>
</div>
<div id="error" class="error hidden">
<svg class="error-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<line x1="15" y1="9" x2="9" y2="15"/>
<line x1="9" y1="9" x2="15" y2="15"/>
</svg>
<p id="error-message"></p>
</div>
</main>
<footer>
<p>Powered by <a href="https://github.com/zacharydenton/boko">boko</a> · All conversions happen locally in your browser</p>
</footer>
</div>
<script type="module" src="app.js"></script>
</body>
</html>