1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"/> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"/> 6 <title>Dashboard â MetaShot.io</title> 7 <meta name="description" content="Manage your OG image API keys, view usage, and build templates."/> 8 <meta name="robots" content="noindex"/> 9 <link rel="icon" href="/favicon.svg" type="image/svg+xml"/> 10 <link rel="icon" href="/favicon-32.png" sizes="32x32" type="image/png"/> 11 <link rel="icon" href="/favicon-16.png" sizes="16x16" type="image/png"/> 12 <link rel="apple-touch-icon" href="/apple-touch-icon.png"/> 13 <link rel="manifest" href="/site.webmanifest"/> 14 <meta name="theme-color" content="#7F77DD"/> 15 <link rel="preconnect" href="https://fonts.googleapis.com"/> 16 <link href="https://fonts.googleapis.com/css2?family=Inter:opsz,[email protected],300;14..32,400;14..32,500;14..32,600;14..32,700;14..32,800&family=Fira+Code:wght@400;500;700&display=swap" rel="stylesheet"/> 17 <link rel="stylesheet" href="/style.css"/> 18 <style> 19 /* ââ Combined Template Editor ââ */ 20 .te-editor-root{position:fixed;inset:0;background:var(--bg);z-index:10000;display:flex;flex-direction:column;overflow:hidden} 21 .te-header{padding:12px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;gap:16px;background:rgba(11,14,26,0.98);flex-shrink:0} 22 .te-mode-toggle{display:flex;background:rgba(255,255,255,0.04);border:1px solid var(--border);border-radius:8px;padding:3px;gap:2px} 23 .te-mode-btn{padding:5px 18px;border-radius:6px;font-size:0.78rem;font-weight:600;cursor:pointer;color:var(--muted);transition:all 0.15s;background:none;border:none} 24 .te-mode-btn.active{background:linear-gradient(135deg,var(--accent),#818cf8);color:white} 25 .te-body{flex:1;display:grid;grid-template-columns:340px 1fr;overflow:hidden} 26 .te-left-pane{display:flex;flex-direction:column;border-right:1px solid var(--border);overflow-y:auto;background:rgba(11,14,26,0.95);scrollbar-width:thin;scrollbar-color:rgba(255,255,255,0.1) transparent} 27 .te-preview-pane{display:flex;flex-direction:column;overflow:hidden} 28 .te-toolbar{padding:10px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;gap:12px;background:rgba(11,14,26,0.8);flex-shrink:0} 29 .te-canvas-wrap{flex:1;display:flex;align-items:center;justify-content:center;padding:32px;overflow:hidden;background:radial-gradient(ellipse at center,rgba(99,102,241,0.04) 0%,transparent 70%)} 30 .te-canvas-outer{width:100%;max-width:760px} 31 .te-canvas-frame{width:100%;aspect-ratio:1200/630;border-radius:12px;overflow:hidden;border:1px solid rgba(255,255,255,0.08);box-shadow:0 32px 80px rgba(0,0,0,0.6),0 0 0 1px rgba(255,255,255,0.04);background:#000} 32 .te-canvas-frame svg{width:100%;height:100%;display:block} 33 .te-infobar{padding:10px 20px;border-top:1px solid var(--border);background:rgba(11,14,26,0.8);display:flex;align-items:center;gap:12px;flex-shrink:0} 34 .te-pills{display:flex;gap:4px;background:rgba(255,255,255,0.04);border:1px solid var(--border);border-radius:var(--radius-sm);padding:3px} 35 .te-pill{padding:4px 10px;border-radius:5px;font-size:0.7rem;font-weight:600;cursor:pointer;color:var(--muted);transition:all 0.15s;background:none;border:none} 36 .te-pill.active{background:linear-gradient(135deg,var(--accent),#818cf8);color:white} 37 /* Visual editor sections */ 38 .ve-section{border-bottom:1px solid var(--border)} 39 .ve-section-toggle{width:100%;display:flex;align-items:center;justify-content:space-between;padding:11px 18px;font-size:0.68rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);cursor:pointer;transition:color .15s;background:none;border:none;text-align:left} 40 .ve-section-toggle:hover{color:var(--fg)} 41 .ve-chevron{font-size:.6rem;transition:transform .2s;opacity:.5} 42 .ve-section-toggle.open .ve-chevron{transform:rotate(180deg)} 43 .ve-section-body{padding:4px 18px 14px;display:none} 44 .ve-section-body.open{display:block} 45 .ve-field{margin-bottom:10px} 46 .ve-field:last-child{margin-bottom:0} 47 .ve-field label{display:block;font-size:.68rem;font-weight:500;color:var(--muted);margin-bottom:4px;letter-spacing:.02em} 48 .ve-input{width:100%;background:rgba(255,255,255,0.04);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--fg);font-size:.8rem;padding:7px 10px;outline:none;font-family:var(--font-sans);transition:border-color .15s;box-sizing:border-box} 49 .ve-input:focus{border-color:rgba(99,102,241,0.5)} 50 select.ve-input option{background:#0f0e1a} 51 .ve-row2{display:grid;grid-template-columns:1fr 1fr;gap:8px} 52 .ve-color-row{display:flex;gap:6px;align-items:center} 53 .ve-color-swatch{width:32px;height:32px;border-radius:6px;border:1px solid var(--border);cursor:pointer;padding:3px;background:rgba(255,255,255,0.04);flex-shrink:0} 54 .ve-color-swatch::-webkit-color-swatch-wrapper{padding:0} 55 .ve-color-swatch::-webkit-color-swatch{border-radius:4px;border:none} 56 .ve-color-hex{flex:1;font-family:var(--font-mono);font-size:.72rem} 57 .ve-tabs{display:flex;background:rgba(255,255,255,0.03);border:1px solid var(--border);border-radius:var(--radius-sm);padding:3px;gap:2px;margin-bottom:10px} 58 .ve-tab{flex:1;padding:5px 0;text-align:center;font-size:.7rem;font-weight:600;color:var(--muted);border-radius:5px;cursor:pointer;transition:all .15s;background:none;border:none} 59 .ve-tab.active{background:linear-gradient(135deg,var(--accent),#818cf8);color:white} 60 .ve-range-row{display:flex;align-items:center;gap:8px} 61 .ve-range-row input[type=range]{flex:1;accent-color:var(--accent);height:4px;cursor:pointer} 62 .ve-range-val{font-size:.7rem;color:var(--muted);font-family:var(--font-mono);min-width:30px;text-align:right} 63 .ve-shape-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:6px} 64 .ve-shape-btn{aspect-ratio:1;background:rgba(255,255,255,0.03);border:1px solid var(--border);border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .15s;color:var(--muted);font-size:1rem} 65 .ve-shape-btn:hover{border-color:rgba(99,102,241,0.4);color:var(--fg)} 66 .ve-shape-btn.active{border-color:var(--accent);background:rgba(99,102,241,0.12);color:var(--fg)} 67 .ve-toggle-row{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px} 68 .ve-toggle-row:last-child{margin-bottom:0} 69 .ve-toggle-label{font-size:.78rem;color:var(--fg)} 70 .ve-switch{width:36px;height:20px;background:rgba(255,255,255,0.1);border-radius:99px;position:relative;cursor:pointer;transition:background .2s;flex-shrink:0;border:none} 71 .ve-switch::after{content:'';position:absolute;width:14px;
71height:14px;background:white;border-radius:50%;top:3px;left:3px;transition:transform .2s} 72 .ve-switch.on{background:var(--accent)} 73 .ve-switch.on::after{transform:translateX(16px)} 74 .ve-upload{border:1.5px dashed var(--border);border-radius:var(--radius-sm);padding:14px;text-align:center;cursor:pointer;transition:all .15s;color:var(--muted);font-size:.75rem;line-height:1.5} 75 .ve-upload:hover{border-color:var(--accent);background:rgba(99,102,241,0.05);color:var(--fg)} 76 .ve-upload-icon{font-size:1.1rem;margin-bottom:3px} 77 .ei-chips{display:flex;gap:6px;flex-shrink:0} 78 .ei-chip{font-size:.65rem;padding:3px 8px;border-radius:99px;background:rgba(255,255,255,0.04);color:var(--muted);border:1px solid var(--border)} 79 .ei-chip .hi{color:var(--success)} 80 .ei-url{flex:1;font-size:.65rem;font-family:var(--font-mono);color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap} 81 .canvas-live-dot{display:flex;align-items:center;gap:5px;font-size:.6rem;color:var(--success);font-weight:600;letter-spacing:.06em} 82 .canvas-live-dot::before{content:'';width:5px;height:5px;border-radius:50%;background:var(--success);animation:dot-pulse 2s infinite} 83 @keyframes dot-pulse{0%,100%{opacity:1}50%{opacity:.4}} 84 /* ââ Images page ââ */ 85 .img-toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap} 86 .img-toolbar-search{position:relative;flex:1;min-width:160px;max-width:260px} 87 .img-toolbar-search::before{content:'â';position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--muted);font-size:1.1rem;pointer-events:none;line-height:1} 88 .img-toolbar-search input{width:100%;padding:7px 10px 7px 32px;border-radius:8px;border:1px solid var(--border);background:rgba(255,255,255,0.04);color:var(--fg);font-size:0.82rem;outline:none;box-sizing:border-box;transition:border-color .15s} 89 .img-toolbar-search input:focus{border-color:rgba(99,102,241,0.5)} 90 .img-filter-select{padding:7px 10px;border-radius:8px;border:1px solid var(--border);background:rgba(255,255,255,0.04);color:var(--muted);font-size:0.8rem;cursor:pointer;outline:none;transition:border-color .15s} 91 .img-filter-select:focus{border-color:rgba(99,102,241,0.5)} 92 .img-filter-select option{background:#0f0e1a} 93 .img-fav-toggle{padding:7px 12px;border-radius:8px;border:1px solid var(--border);background:rgba(255,255,255,0.04);color:var(--muted);font-size:0.8rem;cursor:pointer;transition:all .15s;white-space:nowrap} 94 .img-fav-toggle:hover{border-color:rgba(251,191,36,0.4);color:#fbbf24} 95 .img-fav-toggle.active{border-color:rgba(251,191,36,0.5);background:rgba(251,191,36,0.08);color:#fbbf24} 96 .img-summary-bar{display:flex;align-items:center;gap:20px;padding:12px 16px;background:rgba(255,255,255,0.02);border:1px solid var(--border);border-radius:10px;margin-bottom:20px} 97 .img-summary-stat{display:flex;flex-direction:column;gap:1px} 98 .img-summary-val{font-size:1.05rem;font-weight:700;color:var(--fg);line-height:1} 99 .img-summary-label{font-size:0.6rem;color:var(--muted);letter-spacing:.07em;text-transform:uppercase} 100 .img-summary-divider{width:1px;height:30px;background:var(--border);flex-shrink:0} 101 .img-progress-wrap{display:flex;align-items:center;gap:8px;margin-left:auto} 102 .img-progress-track{width:110px;height:5px;background:rgba(255,255,255,0.07);border-radius:99px;overflow:hidden} 103 .img-progress-bar{height:100%;border-radius:99px;transition:width .4s,background .4s;width:0%} 104 .img-card{background:var(--bg-card);border:1px solid var(--border);border-radius:12px;overflow:hidden;position:relative;transition:border-color .2s,transform .15s,box-shadow .15s} 105 .img-card:hover{border-color:rgba(99,102,241,0.35);transform:translateY(-2px);box-shadow:0 8px 32px rgba(0,0,0,0.3)} 106 .img-card-thumb{aspect-ratio:1200/630;background:#0d1117;position:relative;overflow:hidden;cursor:pointer} 107 .img-card-thumb img{width:100%;height:100%;object-fit:cover;display:block} 108 .img-card-fallback{display:none;position:absolute;inset:0;align-items:center;justify-content:center;flex-direction:column;gap:6px;color:var(--muted);font-size:0.75rem} 109 .img-card-badge{position:absolute;top:8px;left:8px;padding:2px 8px;border-radius:20px;font-size:0.63rem;font-weight:700;font-family:monospace;backdrop-filter:blur(6px)} 110 .img-card-overlay{position:absolute;inset:0;background:rgba(0,0,0,0.55);display:flex;align-items:center;justify-content:center;gap:10px;opacity:0;transition:opacity .2s} 111 .img-card-thumb:hover .img-card-overlay{opacity:1} 112 .img-ov-btn{width:40px;height:40px;border-radius:50%;background:rgba(255,255,255,0.12);border:1px solid rgba(255,255,255,0.2);color:white;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:1rem;transition:background .15s,transform .15s;flex-shrink:0} 113 .img-ov-btn:hover{background:rgba(255,255,255,0.22);transform:scale(1.1)} 114 .img-ov-btn.danger:hover{background:rgba(239,68,68,0.3);border-color:rgba(239,68,68,0.5)} 115 .img-card-body{padding:10px 12px 6px} 116 .img-card-title{font-weight:600;font-size:0.85rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--fg);line-height:1.3} 117 .img-card-sub{font-size:0.75rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px} 118 .img-card-footer{padding:6px 12px 10px;display:flex;align-items:center;justify-content:space-between} 119 .img-card-date{font-size:0.68rem;color:var(--muted)} 120 .img-card-fav{background:none;border:none;cursor:pointer;font-size:1rem;padding:2px;line-height:1;transition:transform .15s} 121 .img-card-fav:hover{transform:scale(1.25)}
122 @keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}} 123 .img-card-skeleton{pointer-events:none} 124 .qs-theme-pill{background:rgba(255,255,255,0.05);border:1px solid var(--border);border-radius:999px;padding:3px 11px;font-size:0.72rem;font-family:var(--font-mono);cursor:pointer;color:var(--muted);transition:background .15s,color .15s,border-color .15s} 125 .qs-theme-pill:hover{background:rgba(99,102,241,0.1);border-color:rgba(99,102,241,0.3);color:var(--fg)} 126 .qs-theme-pill.active{background:rgba(99,102,241,0.15);border-color:rgba(99,102,241,0.45);color:#a5b4fc;font-weight:600} 127 .img-card-skeleton .img-card-thumb{background:linear-gradient(90deg,rgba(255,255,255,0.03) 25%,rgba(255,255,255,0.07) 50%,rgba(255,255,255,0.03) 75%);background-size:200% 100%;animation:shimmer 1.5s infinite} 128 .img-card-skeleton .img-skel-line{height:11px;border-radius:4px;background:rgba(255,255,255,0.06);margin-bottom:7px;animation:shimmer 1.5s infinite;background-size:200% 100%} 129 .img-card-skeleton .img-skel-line.short{width:55%} 130 </style> 131</head> 132<body class="auth-body"> 133 134 <!-- Initial Loader --> 135 <div id="dash-loader" class="dash-loader"> 136 <div class="spinner"></div> 137 </div> 138 139 <!-- Auth gate --> 140 <div id="auth-gate" class="auth-page hidden"> 141 <div class="auth-left"> 142 <a href="/" class="logo auth-logo"><img src="/favicon.svg" class="logo-icon" alt="MetaShot logo"/> MetaShot.io</a> 143 <div class="auth-left-content"> 144 <h2>Generate OG images<br/>from a single URL</h2> 145 <p>Join developers who use MetaShot.io to automatically generate social preview images â no headless browsers, no infrastructure.</p> 146 <ul class="auth-perks"> 147 <li><span class="perk-icon">â¦</span> 1,000 images/month free</li> 148 <li><span class="perk-icon">â¦</span> Edge-cached, globally</li> 149 <li><span class="perk-icon">â¦</span> 5 built-in themes</li> 150 <li><span class="perk-icon">â¦</span> REST API, no SDK needed</li> 151 </ul> 152 <!-- Mini OG preview --> 153 <div class="auth-preview-wrap"> 154 <div class="auth-preview-badge">Preview</div> 155 <div class="auth-og-mini" id="auth-og-mini"></div> 156 </div> 157 </div> 158 </div> 159 160 <div class="auth-right"> 161 <div class="auth-card"> 162 <!-- Tab switcher --> 163 <div class="auth-card-tabs" id="auth-tabs"> 164 <button class="auth-tab active" id="tab-login" onclick="showLogin()">Sign in</button> 165 <button class="auth-tab" id="tab-register" onclick="showRegister()">Create account</button> 166 </div> 167 168 <!-- Login form --> 169 <form id="login-form" class="auth-form" onsubmit="login(event)" novalidate> 170 <div class="auth-form-header"> 171 <h3>Welcome back</h3> 172 <p class="auth-form-sub">Sign in to your MetaShot.io account</p> 173 </div> 174 175 <div class="field"> 176 <label for="login-email">Email address</label> 177 <div class="input-wrap"> 178 <svg class="input-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="1" y="3" width="14" height="10" rx="2" stroke="currentColor" stroke-width="1.4"/><path d="M1 5.5l6.47 4.26a1 1 0 0 0 1.06 0L15 5.5" stroke="currentColor" stroke-width="1.4"/></svg> 179 <input type="email" id="login-email" placeholder="[email protected]" required autocomplete="email"/> 180 </div> 181 </div> 182 183 <div class="field"> 184 <div style="display:flex; justify-content:space-between; align-items:flex-end; margin-bottom: 4px;"> 185 <label for="login-password" style="margin-bottom:0;">Password</label> 186 <button type="button" class="link-btn" style="font-size:0.8rem;" onclick="showForgot()">Forgot password?</button> 187 </div> 188 <div class="input-wrap"> 189 <svg class="input-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="7" width="10" height="8" rx="2" stroke="currentColor" stroke-width="1.4"/><path d="M5 7V5a3 3 0 0 1 6 0v2" stroke="currentColor" stroke-width="1.4"/></svg> 190 <input type="password" id="login-password" placeholder="â¢â¢â¢â¢â¢â¢â¢â¢" required autocomplete="current-password"/> 191 </div> 192 </div> 193 194 <div class="form-error hidden" id="login-error"></div> 195 196 <button type="submit" class="btn-primary w-full" id="login-submit">
197 <span>Sign in</span> 198 <svg class="btn-arrow" width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8h10M9 4l4 4-4 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg> 199 </button> 200 201 <p class="auth-switch">No account? <button type="button" class="link-btn" onclick="showRegister()">Create one free â</button></p> 202 </form> 203 204 <!-- Register form --> 205 <form id="register-form" class="auth-form hidden" onsubmit="register(event)" novalidate> 206 <div class="auth-form-header"> 207 <h3>Create your account</h3> 208 <p class="auth-form-sub">Free forever, no credit card required</p> 209 </div> 210 211 <div class="field"> 212 <label for="reg-name">Name <span class="field-optional">optional</span></label> 213 <div class="input-wrap"> 214 <svg class="input-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="5" r="3" stroke="currentColor" stroke-width="1.4"/><path d="M2 14c0-3.31 2.69-6 6-6s6 2.69 6 6" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg> 215 <input type="text" id="reg-name" placeholder="Your name" autocomplete="name"/> 216 </div> 217 </div> 218 219 <div class="field"> 220 <label for="reg-email">Email address</label> 221 <div class="input-wrap"> 222 <svg class="input-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="1" y="3" width="14" height="10" rx="2" stroke="currentColor" stroke-width="1.4"/><path d="M1 5.5l6.47 4.26a1 1 0 0 0 1.06 0L15 5.5" stroke="currentColor" stroke-width="1.4"/></svg> 223 <input type="email" id="reg-email" placeholder="[email protected]" required autocomplete="email"/> 224 </div> 225 </div> 226 227 <div class="field"> 228 <label for="reg-password">Password <span class="field-optional">min. 8 characters</span></label> 229 <div class="input-wrap"> 230 <svg class="input-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="7" width="10" height="8" rx="2" stroke="currentColor" stroke-width="1.4"/><path d="M5 7V5a3 3 0 0 1 6 0v2" stroke="currentColor" stroke-width="1.4"/></svg> 231 <input type="password" id="reg-password" placeholder="â¢â¢â¢â¢â¢â¢â¢â¢" required minlength="8" autocomplete="new-password"/> 232 </div> 233 <div class="password-strength" id="password-strength"></div> 234 </div> 235 236 <div class="form-error hidden" id="reg-error"></div> 237 238 <button type="submit" class="btn-primary w-full" id="reg-submit"> 239 <span>Create free account</span> 240 <svg class="btn-arrow" width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8h10M9 4l4 4-4 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg> 241 </button> 242 243 <p class="auth-switch">Already have an account? <button type="button" class="link-btn" onclick="showLogin()">Sign in â</button></p> 244 </form> 245 246 <!-- Register Success view --> 247 <div id="register-success" class="auth-form hidden"> 248 <div class="auth-form-header" style="text-align: center;"> 249 <div style="width:64px; height:64px; background:rgba(74, 222, 128, 0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 24px;"> 250 <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="var(--success)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg> 251 </div> 252 <h3>Check your email!</h3> 253 <p class="auth-form-sub">We've sent a verification link to <strong id="sent-email-display">your email</strong>. Please click the link to verify your account and unlock all features.</p> 254 </div> 255 <button type="button" class="btn-primary w-full" onclick="showLogin()">Back to login</button> 256 <p class="auth-switch">Didn't get the email? <button type="button" class="link-btn" id="resend-btn" onclick="resendVerification()">Resend link</button></p> 257 </div> 258 259 <!-- Forgot Password form --> 260 <form id="forgot-form" class="auth-form hidden" onsubmit="submitForgotPassword(event)" novalidate> 261 <div class="auth-form-header"> 262 <h3>Reset password</h3> 263 <p class="auth-form-sub">We'll send you a link to reset your password</p> 264 </div> 265 <div class="field"> 266 <label for="forgot-email">Email address</label> 267 <div class="input-wrap"> 268 <input type="email" id="forgot-email" placeholder="[email protected]" required autocomplete="email"/> 269 </div> 270 </div> 271 <div class="form-error hidden" id="forgot-error"></div> 272 <div class="form-success hidden" id="forgot-success" style="color:var(--success); font-size: 0.9rem; margin-bottom: 24px;"></div> 273 <button type="submit" class="btn-primary w-full" id="forgot-submit">Send reset link</button> 274 <p class="auth-switch"><button type="button" class="link-btn" onclick="showLogin()">â Back to sign in</button></p> 275 </form> 276 277 <!-- Reset Password form --> 278 <form id="reset-form" class="auth-form hidden" onsubmit="submitResetPassword(event)" novalidate> 279 <div class="auth-form-header"> 280 <h3>New password</h3> 281 <p class="auth-form-sub">Enter your new password below</p> 282 </div> 283 <div class="field"> 284 <label for="reset-password-input">New Password</label> 285 <div class="input-wrap"> 286 <input type="password" id="reset-password-input" placeholder="â¢â¢â¢â¢â¢â¢â¢â¢" required minlength="8" autocomplete="new-password"/> 287 </div> 288 </div> 289 <div class="form-error hidden" id="reset-error"></div> 290 <div class="form-success hidden" id="reset-success" style="color:var(--success); font-size: 0.9rem; margin-bottom: 24px;"></div> 291 <button type="submit" class="btn-primary w-full" id="reset-submit">Update password</button> 292 </form> 293 294 <!-- Verify Email form --> 295 <div id="verify-form" class="auth-form hidden" style="text-align: center;"> 296 <div class="auth-form-header"> 297 <h3>Email Verification</h3> 298 </div> 299 <div id="verify-loading" style="margin-bottom: 24px; color: var(--muted);">Verifying your email...</div> 300 <div class="form-error hidden" id="verify-error" style="text-align: left; margin-bottom: 24px;"></div> 301 <div class="form-success hidden" id="verify-success" style="color:var(--success); font-size: 1.1rem; margin-bottom: 24px; font-weight: 500;">Email verified successfully!</div> 302 <button class="btn-primary w-full hidden" id="verify-login-btn" onclick="showLogin()">Continue to Sign in</button> 303 </div> 304 </div> 305 </div> 306 </div> 307 308 <!-- Dashboard App (shown after login) --> 309 <div id="app" class="hidden"> 310 <!-- Verification Banner --> 311 <div id="verify-banner" class="verify-banner hidden"> 312 <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line>
312<line x1="12" y1="17" x2="12.01" y2="17"></line></svg> 313 <span>Please verify your email address to ensure account security and receive usage alerts.</span> 314 <button onclick="resendVerification()" class="link-btn" style="color: white; text-decoration: underline; font-weight: 700; padding: 0; background: none; border: none; cursor: pointer;">Resend link</button> 315 </div> 316 <nav class="nav"> 317 <div class="nav-inner"> 318 <a href="/" class="logo"><img src="/favicon.svg" class="logo-icon" alt="MetaShot logo"/> MetaShot.io</a> 319 <div class="nav-links"> 320 <div class="user-menu-wrap"> 321 <button class="user-menu-btn" onclick="toggleUserMenu()" id="user-menu-btn" aria-label="User Menu"> 322 <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg> 323 </button> 324 <div class="user-dropdown hidden" id="user-dropdown"> 325 <div class="user-dropdown-header"> 326 <div style="display:flex; align-items:center; gap:8px;"> 327 <span class="muted" id="user-email-display"></span> 328 <span id="user-plan-badge" class="hidden" style="font-size:0.6rem; font-weight:800; background:var(--accent); color:white; padding:2px 6px; border-radius:4px; text-transform:uppercase; letter-spacing:0.05em;">PRO</span> 329 </div> 330 </div> 331 <div class="user-dropdown-body"> 332 <button class="dropdown-item danger" onclick="logout()"> 333 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg> 334 Sign out 335 </button> 336 </div> 337 </div> 338 </div> 339 </div> 340 </div> 341 </nav> 342 343 <div class="dash-layout"> 344 <aside class="dash-sidebar"> 345 <nav class="dash-nav" style="gap: 8px;"> 346 <a href="#keys" class="dash-navlink active" id="nav-keys" onclick="showSection('keys')">ð API Keys</a> 347 <a href="#templates" class="dash-navlink" id="nav-templates" onclick="showSection('templates')">ð Templates</a> 348 <a href="#images" class="dash-navlink" id="nav-images" onclick="showSection('images')">ð¼ï¸ Images</a> 349 <a href="#usage" class="dash-navlink" id="nav-usage" onclick="showSection('usage')">ð Usage</a> 350 <a href="#admin" class="dash-navlink hidden" id="nav-admin" onclick="showSection('admin')">ð¡ï¸ Admin</a> 351 <div class="dash-nav-divider" style="margin: 16px 0;"></div> 352 <a href="/docs" class="dash-navlink">ð Documentation</a> 353 </nav> 354 <div class="dash-plan-box" id="plan-box" style="opacity:0; transition:opacity 0.2s;"> 355 <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom: 12px;"> 356 <div class="plan-label" id="plan-name">Free Plan</div> 357 <div id="plan-badge-v1" style="font-size:0.65rem; background:rgba(255,255,255,0.05); padding:2px 6px; border-radius:4px; color:var(--muted)">v1.0</div> 358 </div> 359 <div class="plan-meter-wrap" style="height: 6px;"> 360 <div class="plan-meter-bar" id="plan-bar" style="width:0%"></div> 361 </div> 362 <div class="plan-usage" id="plan-usage" style="margin-top: 8px;">0 / 1,000 requests this month</div> 363 <button id="upgrade-btn" class="btn-primary w-full" style="margin-top: 16px; padding: 10px; font-size: 0.85rem;" onclick="upgradeToPro()">Upgrade to Pro</button> 364 <button id="manage-billing-btn" class="btn-ghost w-full hidden" style="margin-top: 16px; padding: 10px; font-size: 0.85rem;" onclick="manageSubscription()">Manage Billing</button> 365 </div> 366 </aside> 367 368 <main class="dash-main"> 369 <section id="section-keys" class="dash-section hidden"> 370 <div class="section-header"> 371 <h2>API Keys</h2> 372 <div style="display:flex;gap:8px;align-items:center;"> 373 <button class="btn-ghost" id="quickstart-toggle-btn" onclick="toggleQu
373ickstart()" style="font-size:0.82rem; padding:7px 14px;">ð Quick Start</button> 374 <button class="btn-primary" id="create-key-btn" onclick="openCreateKey()">+ New key</button> 375 </div> 376 </div> 377 <div id="keys-list" class="keys-list"><div class="loading">Loading keysâ¦</div></div> 378 379 <!-- Quick Start modal --> 380 <div id="quickstart-modal" class="modal-overlay hidden" onclick="dismissQuickstart()"> 381 <div onclick="event.stopPropagation()" style="background:var(--bg-card); border:1px solid var(--border); border-radius:16px; width:90vw; max-width:780px; box-shadow:0 32px 80px rgba(0,0,0,0.6); overflow:hidden;"> 382 383 <!-- Welcome banner (first-login only) --> 384 <div id="qs-welcome-banner" class="hidden" style="padding:20px 28px 0;"> 385 <div style="background:linear-gradient(135deg,rgba(99,102,241,0.15),rgba(167,139,250,0.1)); border:1px solid rgba(99,102,241,0.25); border-radius:10px; padding:16px 20px; display:flex; align-items:center; gap:14px;"> 386 <div style="font-size:1.8rem; line-height:1; flex-shrink:0;">ð</div> 387 <div> 388 <div style="font-weight:700; font-size:0.95rem; margin-bottom:2px;">Your API is ready!</div> 389 <div style="font-size:0.8rem; color:var(--muted);">Your first key was created automatically. The URL below is ready to use â copy it straight into your <code style="font-size:0.75rem;"><meta></code> tag.</div> 390 </div> 391 </div> 392 </div> 393 394 <!-- Header --> 395 <div style="padding:20px 28px 0; display:flex; justify-content:space-between; align-items:flex-start;"> 396 <div> 397 <div id="qs-modal-title" style="font-size:1.15rem; font-weight:800; letter-spacing:-0.3px;">Quick Start</div> 398 <div style="font-size:0.82rem; color:var(--muted); margin-top:3px;">Build your URL, pick a theme, copy and go.</div> 399 </div> 400 <button onclick="dismissQuickstart()" style="background:rgba(255,255,255,0.06);border:1px solid var(--border);cursor:pointer;color:var(--muted);font-size:0.9rem;line-height:1;padding:6px 10px;border-radius:6px;transition:background .15s;" onmouseover="this.style.background='rgba(255,255,255,0.1)'" onmouseout="this.style.background='rgba(255,255,255,0.06)'">â</button> 401 </div> 402 403 <!-- Body: two columns --> 404 <div style="display:grid; grid-template-columns:1fr 1fr; gap:0; padding:20px 28px 24px;"> 405 406 <!-- Left: controls --> 407 <div style="padding-right:24px; border-right:1px solid var(--border); display:flex; flex-direction:column; gap:14px;"> 408 409 <!-- Inputs --> 410 <div style="display:flex; flex-direction:column; gap:10px;"> 411 <div> 412 <label style="font-size:0.68rem; font-weight:600; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); display:block; margin-bottom:5px;">Title</label> 413 <input id="qs-title" type="text" value="Hello MetaShot" maxlength="70" oninput="updateQuickstartCard()" placeholder="Your page title" style="width:100%; box-sizing:border-box; background:var(--bg); border:1px solid var(--border); border-radius:8px; padding:9px 12px; font-size:0.85rem; color:var(--fg); outline:none; transition:border-color .15s;" onfocus="this.style.borderColor='rgba(99,102,241,0.6)'" onblur="this.style.borderColor=''"/> 414 </div> 415 <div> 416 <label style="font-size:0.68rem; font-weight:600; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); display:block; margin-bottom:5px;">Subtitle <span style="font-weight:400; text-transform:none; letter-spacing:0;">(optional)</span></label> 417 <input id="qs-subtitle" type="text" placeholder="A short description" maxlength="100" oninput="updateQuickstartCard()" style="width:100%; box-sizing:border-box; background:var(--bg); border:1px solid var(--border); border-radius:8px; padding:9px 12px; font-size:0.85rem; color:var(--fg); outline:none; transition:border-color .15s;" onfocus="this.style.borderColor='rgba(99,102,241,0.6)'" onblur
417="this.style.borderColor=''"/> 418 </div> 419 <div> 420 <label style="font-size:0.68rem; font-weight:600; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); display:block; margin-bottom:5px;">Author <span style="font-weight:400; text-transform:none; letter-spacing:0;">(optional)</span></label> 421 <input id="qs-author" type="text" placeholder="Your name" maxlength="40" oninput="updateQuickstartCard()" style="width:100%; box-sizing:border-box; background:var(--bg); border:1px solid var(--border); border-radius:8px; padding:9px 12px; font-size:0.85rem; color:var(--fg); outline:none; transition:border-color .15s;" onfocus="this.style.borderColor='rgba(99,102,241,0.6)'" onblur="this.style.borderColor=''"/> 422 </div> 423 </div> 424 425 <!-- Theme picker --> 426 <div> 427 <label style="font-size:0.68rem; font-weight:600; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); display:block; margin-bottom:8px;">Theme</label> 428 <div style="display:flex; flex-wrap:wrap; gap:6px;"> 429 <button class="qs-theme-pill active" onclick="setQuickstartTheme('gradient')" data-theme="gradient">gradient</button> 430 <button class="qs-theme-pill" onclick="setQuickstartTheme('dark')" data-theme="dark">dark</button> 431 <button class="qs-theme-pill" onclick="setQuickstartTheme('light')" data-theme="light">light</button> 432 <button class="qs-theme-pill" onclick="setQuickstartTheme('code')" data-theme="code">code</button> 433 <button class="qs-theme-pill" onclick="setQuickstartTheme('article')" data-theme="article">article</button> 434 <button class="qs-theme-pill" onclick="setQuickstartTheme('profile')" data-theme="profile">profile</button> 435 <button class="qs-theme-pill" onclick="setQuickstartTheme('minimal')" data-theme="minimal">minimal</button> 436 </div> 437 </div> 438 </div> 439 440 <!-- Right: live preview --> 441 <div style="padding-left:24px; display:flex; flex-direction:column; gap:14px;"> 442 <div> 443 <label style="font-size:0.68rem; font-weight:600; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); display:block; margin-bottom:8px;">Preview</label> 444 <div style="border-radius:8px; overflow:hidden; border:1px solid var(--border); aspect-ratio:1200/630; background:#0d1117;"> 445 <div id="qs-preview" style="width:100%; height:100%; display:flex; align-items:center; justify-content:center;"></div> 446 </div> 447 </div> 448 449 <!-- URL output --> 450 <div> 451 <label style="font-size:0.68rem; font-weight:600; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); display:block; margin-bottom:8px;">Your URL</label> 452 <div style="background:rgba(0,0,0,0.35); border:1px solid var(--border); border-radius:8px; padding:11px 14px;"> 453 <code id="quickstart-url" style="font-size:0.72rem; word-break:break-all; color:#a5b4fc; display:block; line-height:1.7; font-family:var(--font-mono);">https://api.metashot.io/og?key=YOUR_KEY&title=Hello+MetaShot&theme=gradient</code> 454 </div> 455 </div> 456 </div> 457 </div> 458 459 <!-- Footer --> 460 <div style="padding:14px 28px 20px; border-top:1px solid var(--border); display:flex; align-items:center; gap:10px;"> 461 <a href="/docs" class="btn-ghost" style="font-size:0.82rem; padding:8px 16px;">ð Docs</a> 462 <div style="margin-left:auto; display:flex; gap:8px;"> 463 <button id="qs-close-btn" class="btn-ghost" onclick="dismissQuickstart()" style="font-size:0.85rem; padding:9px 20px; display:none;">Go to my keys</button> 464 <button class="btn-primary" onclick="copyQuickstartUrl()" style="font-size:0.85rem; padding:9px 20px;">Copy URL</button> 465 </div> 466 </div> 467 </div> 468 </div> 469 470 <!-- Image preview lightbox --> 471 <div id="image-preview-modal" class="modal-overlay hidden" onclick="closeImagePreview()"> 472 <div onclick="event.stopPropagation()" style="position:relative; max-width:900px; width:90vw; margin:auto;"> 473 <button onclick="closeImagePreview()" style="position:absolute; top:-40px; right:0; background:none; border:none;
473 color:#fff; font-size:1.5rem; cursor:pointer; opacity:0.8; line-height:1;">â</button> 474 <img id="image-preview-img" src="" alt="OG Image Preview" style="width:100%; border-radius:10px; display:block; box-shadow:0 20px 60px rgba(0,0,0,0.5);"/> 475 <div style="text-align:center; margin-top:16px;"> 476 <a id="image-preview-download" href="#" download="og-image.png" class="btn-ghost" style="font-size:0.85rem; padding:8px 20px; color:#fff; border-color:rgba(255,255,255,0.3);">⬠Download</a> 477 </div> 478 </div> 479 </div> 480 481 <div id="create-key-modal" class="modal-overlay hidden"> 482 <div class="modal-card"> 483 <h3>New API Key</h3> 484 <div class="field"> 485 <label>Key name</label> 486 <input type="text" id="key-name-input" placeholder="e.g. blog-prod" maxlength="80"/> 487 </div> 488 <div class="field" id="key-domains-field"> 489 <label> 490 Allowed Domains 491 <span class="field-optional">(comma separated)</span> 492 <span id="key-domains-pro-badge" style="font-size:0.65rem; font-weight:700; padding:2px 7px; border-radius:999px; background:rgba(99,102,241,0.15); color:#818cf8; border:1px solid rgba(99,102,241,0.3); margin-left:4px; vertical-align:middle;">PRO</span> 493 </label> 494 <input type="text" id="key-domains-input" placeholder="e.g. example.com, test.com" maxlength="255"/> 495 <div id="key-domains-pro-notice" class="hidden" style="margin-top:8px; padding:10px 14px; background:rgba(99,102,241,0.07); border:1px solid rgba(99,102,241,0.2); border-radius:8px; font-size:0.82rem; color:#a5b4fc;"> 496 Domain restrictions are a <strong>Pro feature</strong>. <a href="#" onclick="upgradeToPro(); return false;" style="color:var(--accent); font-weight:600;">Upgrade to enable â</a> 497 </div> 498 </div> 499 <div class="form-error hidden" id="create-key-error"></div> 500 <div class="modal-actions"> 501 <button class="btn-ghost" onclick="closeCreateKey()">Cancel</button> 502 <button class="btn-primary" id="create-key-submit" onclick="createKey()">Create key</button> 503 </div> 504 </div> 505 </div> 506 507 <div id="key-reveal-modal" class="modal-overlay hidden"> 508 <div class="modal-card"> 509 <h3>ð Your new API key</h3> 510 <p class="muted" style="margin-bottom:1rem">You can reveal this key again any time from the API Keys page.</p> 511 512 <div class="field"> 513 <label>API Key</label> 514 <div class="key-reveal-box"> 515 <code id="revealed-key"></code> 516 <button class="copy-btn" onclick="copyValue('revealed-key')" title="Copy API Key"> 517 <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> 518 </button> 519 </div> 520 </div> 521 522 <div class="field" id="revealed-secret-field" style="margin-top:20px"> 523 <label>Signing Secret <span class="field-optional">(For Signed URLs)</span></label> 524 <div class="key-reveal-box" id="revealed-secret-box"> 525 <code id="revealed-secret"></code> 526 <button class="copy-btn" onclick="copyValue('revealed-secret')" title="Copy Signing Secret"> 527 <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> 528 </button> 529 </div> 530 <div id="revealed-secret-pro-notice" class="hidden" style="padding:10px 14px; background:rgba(99,102,241,0.07); border:1px solid rgba(99,102,241,0.2); border-radius:8px; font-size:0.82rem; color:#a5b4fc;"> 531 ⬠Signed URLs are a <strong>Pro feature</strong>. <a href="#" onclick="closeReveal(); upgradeToPro(); return false;" style="color:var(--accent); font-weight:600;">Upgrade to enable â</a> 532 </div> 533 </div> 534 535 <button class="btn-primary w-full" style="margin-top:1.5rem" onclick="closeReveal()">Done</button> 536 </div> 537 </div> 538 539 </section> 540 541 <section id="section-usage" class="dash-section hidden"> 542 <div class="section-header"><h2>Usage</h2></div> 543 <div class="usage-cards" id="usage-cards"><div class="loading">Loadingâ¦</div></div> 544 </section> 545 546 <section id="section-templates" class="dash-section hidden"> 547 <div id="templates-list-view"> 548 <div class="section-header"> 549 <h2>Templates</h2> 550 <button class="btn-primary" id="create-template-btn" onclick="openCreateTemplate()">+ New</button> 551 </div> 552 <div id="templates-list" class="keys-list"><div class="loading">Loading templatesâ¦</div></div> 553 </div> 554 555 <!-- Combined Editor View --> 556 <div id="template-editor-view" class="te-editor-root hidden"> 557 558 <!-- Header --> 559 <div class="te-header"> 560 <div style="display:flex;align-items:center;gap:8px;flex-shrink:0;"> 561 <button class="btn-ghost" onclick="closeTemplateEditor()" style="padding:8px;" title="Back"> 562 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
562<line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg> 563 </button> 564 <div class="te-mode-toggle"> 565 <button id="te-mode-visual" class="te-mode-btn active" onclick="switchEditorMode('visual')">Visual</button> 566 <button id="te-mode-code" class="te-mode-btn" onclick="switchEditorMode('code')">Code</button> 567 </div> 568 </div> 569 <div style="display:flex;align-items:center;gap:10px;flex-shrink:0;"> 570 <input type="text" id="template-name-input" placeholder="Template nameâ¦" maxlength="80" 571 style="background:rgba(255,255,255,0.05);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--fg);padding:7px 12px;font-size:0.85rem;outline:none;width:200px;font-family:var(--font-sans);"/> 572 <label style="display:flex;align-items:center;gap:6px;font-size:0.78rem;color:var(--muted);cursor:pointer;white-space:nowrap;margin:0;"> 573 <button class="ve-switch" id="sw-te-public" onclick="veSwitchToggle(this,'te-public')"></button> 574 Public 575 </label> 576 <button class="btn-ghost" onclick="closeTemplateEditor()" style="padding:8px;" title="Cancel"> 577 <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg> 578 </button> 579 <button class="btn-primary" id="template-editor-submit" onclick="saveTemplate()" style="padding:8px 10px;" title="Save template"> 580 <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg> 581 </button> 582 </div> 583 </div> 584 585 <!-- Error bar --> 586 <div class="form-error hidden" id="template-editor-error" style="margin:0;border-radius:0;border-left:none;border-right:none;"></div> 587 588 <!-- Body --> 589 <div class="te-body"> 590 591 <!-- Visual pane --> 592 <div id="te-visual-pane" class="te-left-pane"> 593 <div class="ve-section"> 594 <button class="ve-section-toggle open" onclick="veToggleSection(this)">Content <span class="ve-chevron">â¼</span></button> 595 <div class="ve-section-body open" id="ve-sec-content"> 596 <div class="ve-field"><label>Title preview</label><input class="ve-input" id="ve-title" type="text" value="Hello, World!" oninput="renderVisual()"/></div> 597 <div class="ve-field"><label>Subtitle preview</label><input class="ve-input" id="ve-subtitle" type="text" placeholder="Optional" oninput="renderVisual()"/></div> 598 <div class="ve-field"><label>Author / tag preview</label><input class="ve-input" id="ve-author" type="text" placeholder="Optional" oninput="renderVisual()"/></div> 599 </div> 600 </div> 601 <div class="ve-section"> 602 <button class="ve-section-toggle open" onclick="veToggleSection(this)">Theme <span class="ve-chevron">â¼</span></button> 603 <div class="ve-section-body open"> 604 <!-- Background fill mode --> 605 <div class="ve-field" id="ve-bg-mode-row"> 606 <label>Background fill</label> 607 <div class="ve-tabs" id="ve-bg-tabs"> 608 <button class="ve-tab active" onclick="veSetBgMode('solid',this)">Solid</button> 609 <button class="ve-tab" onclick="veSetBgMode('gradient',this)">Gradient</button> 610 </div> 611 </div> 612 <div id="ve-bg-solid"> 613 <div class="ve-field"><label>Color</label> 614 <div class="ve-color-row"> 615 <input type="color" class="ve-color-swatch" id="ve-bg" value="#302b63" oninput="veSyncColor('ve-bg','ve-bg-hex');veMarkCustom();renderVisual()"/> 616 <input type="text" class="ve-input ve-color-hex" id="ve-bg-hex" value="#302b63" maxlength="7" onin
616put="veSyncHex('ve-bg','ve-bg-hex');veMarkCustom();renderVisual()"/> 617 </div> 618 </div> 619 </div> 620 <div id="ve-bg-gradient" style="display:none"> 621 <div class="ve-row2"> 622 <div class="ve-field"><label>From</label> 623 <div class="ve-color-row"> 624 <input type="color" class="ve-color-swatch" id="ve-g1" value="#0f0c29" oninput="veSyncColor('ve-g1','ve-g1-hex');veMarkCustom();renderVisual()"/> 625 <input type="text" class="ve-input ve-color-hex" id="ve-g1-hex" value="#0f0c29" maxlength="7" oninput="veSyncHex('ve-g1','ve-g1-hex');veMarkCustom();renderVisual()"/> 626 </div> 627 </div> 628 <div class="ve-field"><label>To</label> 629 <div class="ve-color-row"> 630 <input type="color" class="ve-color-swatch" id="ve-g2" value="#24243e" oninput="veSyncColor('ve-g2','ve-g2-hex');veMarkCustom();renderVisual()"/> 631 <input type="text" class="ve-input ve-color-hex" id="ve-g2-hex" value="#24243e" maxlength="7" oninput="veSyncHex('ve-g2','ve-g2-hex');veMarkCustom();renderVisual()"/> 632 </div> 633 </div> 634 </div> 635 </div> 636 </div> 637 </div> 638 <div class="ve-section"> 639 <button class="ve-section-toggle" onclick="veToggleSection(this)">Typography <span class="ve-chevron">â¼</span></button> 640 <div class="ve-section-body"> 641 <div class="ve-field"><label>Font family</label> 642 <select class="ve-input" id="ve-font" onchange="veSelectFont(this.value)"> 643 <option value="Inter">Inter (sans-serif)</option> 644 <option value="Fira Code">Fira Code (monospace)</option> 645 </select> 646 </div> 647 <div class="ve-field"><label>Title size</label> 648 <div class="ve-range-row"> 649 <input type="range" id="ve-size" min="40" max="120" value="88" oninput="document.getElementById('ve-size-display').textContent=this.value;renderVisual()"/> 650 <span class="ve-range-val" id="ve-size-display">88</span> 651 </div> 652 </div> 653 <div class="ve-field"><label>Text color</label> 654 <div class="ve-color-row"> 655 <input type="color" class="ve-color-swatch" id="ve-fg" value="#ffffff" oninput="veSyncColor('ve-fg','ve-fg-hex');veMarkCustom();renderVisual()"/> 656 <input type="text" class="ve-input ve-color-hex" id="ve-fg-hex" value="#ffffff" maxlength="7" oninput="veSyncHex('ve-fg','ve-fg-hex');veMarkCustom();renderVisual()"/> 657 </div> 658 </div> 659 </div> 660 </div> 661 <div class="ve-section"> 662 <button class="ve-section-toggle" onclick="veToggleSection(this)">Colors <span class="ve-chevron">â¼</span></button> 663 <div class="ve-section-body"> 664 <div class="ve-field"><label>Accent color</label> 665 <div class="ve-color-row"> 666 <input type="color" class="ve-color-swatch" id="ve-accent" value="#a78bfa" oninput="veSyncColor('ve-accent','ve-accent-hex');veMarkCustom();renderVisual()"/> 667 <input type="text" class="ve-input ve-color-hex" id="ve-accent-hex" value="#a78bfa" maxlength="7" oninput="veSyncHex('ve-accent','ve-accent-hex');veMarkCustom();renderVisual()"/> 668 </div> 669 </div> 670 </div> 671 </div> 672 <div class="ve-section"> 673 <button class="ve-section-toggle" onclick="veToggleSection(this)">Layout <span class="ve-chevron">â¼</span></button> 674 <div class="ve-section-body"> 675 <div class="ve-field"><label>Text position</label> 676 <select class="ve-input" id="ve-pos" onchange="renderVisual()"> 677 <option value="bottom">Bottom left</option> 678 <option value="center">Center</option> 679 <option value="top">Top left</option> 680 </select> 681 </div> 682 <div style="margin-top:8px"> 683 <div class="ve-toggle-row"><span class="ve-toggle-label">Left accent bar</span><button class="ve-switch on" id="sw-ve-bar" onclick="veSwitchToggle(this,'ve-bar')"></button></div> 684 <div class="ve-toggle-row"><span class="ve-toggle-label">Bottom stripe</span><button class="ve-switch on" id="sw-ve-stripe" onclick="veSwitchToggle(this,'ve-stripe')"></button></div> 685 </div> 686 </div> 687 </div> 688 <div class="ve-section"> 689 <button class="ve-section-toggle" onclick="veToggleSection(this)">Decorations <span class="ve-chevron">â¼</span></button> 690 <div class="ve-section-body"> 691 <div class="ve-shape-grid"> 692 <div class="ve-shape-btn active" title="Circles" onclick="veSetShape('circles',this)">â¯</div> 693 <div class="ve-shape-btn" title="Grid" onclick="veSetShape('grid',this)">â</div> 694 <div class="ve-shape-btn" title="Dots" onclick="veSetShape('dots',this)">â</div> 695 <div class="ve-shape-btn" title="None" onclick="veSetShape('none',this)">â </div> 696 </div> 697 </div> 698 </div> 699 <div class="ve-section"> 700 <button class="ve-section-toggle" onclick="veToggleSection(this)">Logo <span class="ve-chevron">â¼</span></button> 701 <div class="ve-section-body"> 702 <div class="ve-upload" id="ve-upload-zone" onclick="document.getElementById('ve-logo-input').click()"> 703 <div class="ve-upload-icon">â</div> 704 Click to upload PNG, SVG, JPEG, or WebP 705 </div> 706 <input type="file" id="ve-logo-input" accept="image/png,image/svg+xml,image/jpeg,image/webp" style="display:none" onchange="veHandleLogo(this)"/> 707 <button id="ve-logo-clear" onclick="veClearLogo()" style="display:none;margin-top:6px;font-size:0.7rem;color:var(--danger);cursor:pointer;background:none;border:none;padding:0;">â Remove logo</button> 708 </div> 709 </div> 710 <div class="ve-section" style="border-bottom:none;"> 711 <button class="ve-section-toggle" onclick="veToggleSection(this)">Signed URL <span style="font-size:0.52rem;background:linear-gradient(135deg,var(--accent),#818cf8);color:white;padding:1px 6px;border-radius:99px;margin-left:4px;vertical-align:middle;font-weight:700;letter-spacing:.04em;">PRO</span> <span class="ve-chevron">â¼</span></button> 712 <div class="ve-section-body"> 713 <div id="ve-sign-pro-gate" style="display:none;padding:10px;background:rgba(99,102,241,0.08);border:1px solid rgba(99,102,241,0.2);border-radius:8px;font-size:0.68rem;color:var(--muted);line-height:1.5;"> 714 Signed URLs are a Pro feature. <button onclick="closeTemplateEditor();upgradeToPro()" style="background:none;border:none;color:var(--accent);cursor:pointer;padding:0;font-size:inherit;font-weight:600;">Upgrade â</button> 715 </div> 716 <div id="ve-sign-body"> 717 <div class="ve-field"> 718 <label>API key</label> 719 <select class="ve-input" id="ve-sign-key" onchange="veUpdateSignedUrl()"> 720 <option value="">â select a key with a signing secret â</option> 721 </select> 722 </div> 723 <p id="ve-sign-no-keys" style="font-size:0.63rem;color:var(--muted);line-height:1.55;margin:0;display:none;">No keys have a signing secret yet. Add one in <button onclick="closeTemplateEditor();showSection('keys')" style="background:none;border:none;color:var(--accent);cursor:pointer;padding:0;font-size:inherit;">API Keys</button>.</p> 724 <div id="ve-signed-url-out" style="display:none;margin-top:8px;"> 725 <label style="font-size:0.63rem;color:var(--muted);display:block;margin-bottom:4px;">Signed URL</label> 726 <div style="display:flex;align-items:flex-start;gap:6px;"> 727 <code id="ve-signed-url-val" style="flex:1;font-size:0.57rem;font-family:var(--font-mono);color:var(--accent);background:rgba(0,0,0,0.3);padding:5px 8px;border-radius:6px;word-break:break-all;line-height:1.6;"></code> 728 <button class="btn-ghost btn-xs" onclick="copyText(document.getElementById('ve-signed-url-val').textContent,this)" style="padding:4px 6px;background:rgba(255,255,255,0.03);border:1px solid var(--border);border-radius:4px;flex-shrink:0;margin-top:1px;" title="Copy"> 729 <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> 730 </button> 731 </div> 732 </div> 733 </div> 734 </div> 735 </div> 736 </div><!-- /te-visual-pane --> 737 738 <!-- Code pane --> 739 <div id="te-code-pane" class="te-left-pane hidden"> 740 <!-- Brand Fonts --> 741 <div class="ve-section"> 742 <button class="ve-section-toggle" onclick="veToggleSection(this)">Brand Fonts <span class="ve-chevron">â¼</span></button> 743 <div class="ve-section-body"> 744 <div class="ve-field" style="position:relative;"> 745 <input type="text" id="font-search-input" class="ve-input" placeholder="Search fonts (e.g. Roboto, Poppinsâ¦)"
746 oninput="onFontSearchInput(this.value)" onkeydown="onFontSearchKey(event)" autocomplete="off"/> 747 <div id="font-search-results" style="display:none;position:absolute;top:100%;left:0;right:0;background:#0b1121;border:1px solid var(--border);border-radius:8px;max-height:200px;overflow-y:auto;z-index:9999;margin-top:4px;box-shadow:0 8px 24px rgba(0,0,0,0.5);"></div> 748 </div> 749 <div id="brand-fonts-list" style="display:flex;flex-direction:column;gap:6px;"></div> 750 <p style="font-size:0.63rem;color:var(--muted);margin:6px 0 0;line-height:1.5;">Reference in SVG with <code style="color:var(--accent)">font-family="Roboto"</code>. Loads at render time.</p> 751 </div> 752 </div> 753 754 <textarea id="template-code-input" 755 style="flex:1;min-height:0;font-family:var(--font-mono);font-size:12px;line-height:1.65;padding:14px 18px;background:transparent;color:#e2e8f0;border:none;border-top:1px solid var(--border);border-bottom:1px solid var(--border);resize:none;outline:none;" 756 oninput="updateTemplatePreview()" spellcheck="false" 757 placeholder='<svg width="1200" height="630" xmlns="http://www.w3.org/2000/svg"> <rect width="1200" height="630" fill="#0f172a"/> <text x="80" y="300" font-family="Inter" font-size="72" fill="white">{{title}}</text> </svg>'></textarea> 758 759 <!-- Images --> 760 <div class="ve-section"> 761 <button class="ve-section-toggle" onclick="veToggleSection(this)">Images <span class="ve-chevron">â¼</span></button> 762 <div class="ve-section-body"> 763 <div class="ve-upload" id="code-img-upload-zone" onclick="document.getElementById('code-img-input').click()"> 764 <div class="ve-upload-icon">â</div> 765 Upload PNG, SVG, JPEG or WebP 766 </div> 767 <input type="file" id="code-img-input" accept="image/png,image/svg+xml,image/jpeg,image/webp" style="display:none" onchange="veCodeHandleImage(this)"/> 768 <div id="code-img-list" style="display:flex;flex-direction:column;gap:6px;margin-top:6px;"></div> 769 <p style="font-size:0.63rem;color:var(--muted);line-height:1.55;margin:6px 0 0;">Embedded as base64 at render time â images appear in the generated PNG.</p> 770 </div> 771 </div> 772 773 <!-- Default values --> 774 <div class="ve-section"> 775 <button class="ve-section-toggle" onclick="veToggleSection(this)">Default Content <span class="ve-chevron">â¼</span></button> 776 <div class="ve-section-body"> 777 <div class="ve-field"> 778 <label>Default title</label> 779 <input class="ve-input" id="code-default-title" type="text" maxlength="70" placeholder="Hello, World!" oninput="updateTemplatePreview()"/> 780 </div> 781 <div class="ve-field"> 782 <label>Default subtitle</label> 783 <input class="ve-input" id="code-default-subtitle" type="text" maxlength="100" placeholder="Optional subtitle" oninput="updateTemplatePreview()"/> 784 </div> 785 <div class="ve-field"> 786 <label>Default author</label> 787 <input class="ve-input" id="code-default-author" type="text" maxlength="40" placeholder="Optional author" oninput="updateTemplatePreview()"/> 788 </div> 789 <p style="font-size:0.63rem;color:var(--muted);line-height:1.55;margin:2px 0 0;">Used when <code style="color:var(--accent)">title</code>, <code style="color:var(--accent)">subtitle</code>, or <code style="color:var(--accent)">author</code> are not passed as URL params.</p> 790 </div> 791 </div> 792 793 <!-- Data URL --> 794 <div class="ve-section"> 795 <button class="ve-section-toggle" onclick="veToggleSection(this)">Data URL <span class="ve-chevron">â¼</span></button> 796 <div class="ve-section-body"> 797 <div class="ve-field"> 798 <label>JSON endpoint</label> 799 <input class="ve-input" id="code-data-url" type="url" placeholder="https://api.example.com/data.json" oninput="veOnDataUrl(this.value)"/> 800 </div> 801 <p style="font-size:0.63rem;color:var(--muted);line-height:1.55;margin:2px 0 0;">Fetched at render time. Any JSON field becomes a <code style="color:var(--accent)">{{key}}</code> variable in your SVG.</p> 802 </div> 803 </div> 804 805 <!-- Signed URL --> 806 <div class="ve-section"> 807 <button class="ve-section-toggle" onclick="veToggleSection(this)">Signed URL <span style="font-size:0.52rem;background:linear-gradient(135deg,var(--accent),#818cf8);color:white;padding:1px 6px;border-radius:99px;margin-left:4px;vertical-align:middle;font-weight:700;letter-spacing:.04em;">PRO</span> <span class="ve-chevron">â¼</span></button> 808 <div class="ve-section-body"> 809 <div id="code-sign-pro-gate" style="display:none;padding:10px;background:rgba(99,102,241,0.08);border:1px solid rgba(99,102,241,0.2);border-radius:8px;font-size:0.68rem;color:var(--muted);line-height:1.5;"> 810 Signed URLs are a Pro feature. <button onclick="closeTemplateEditor();upgradeToPro()" style="background:none;border:none;color:var(--accent);cursor:pointer;padding:0;font-size:inherit;font-weight:600;">Upgrade â</button> 811 </div> 812 <div id="code-sign-body"> 813 <div class="ve-field"> 814 <label>API key</label> 815 <select class="ve-input" id="code-sign-key" onchange="veUpdateSignedUrl()"> 816 <option value="">â select a key with a signing secret â</option> 817 </select> 818 </div> 819 <p id="code-sign-no-keys" style="font-size:0.63rem;color:var(--muted);line-height:1.55;margin:0;display:none;">No keys have a signing secret yet. Add one in <button onclick="closeTemplateEditor();showSection('keys')" style="background:none;border:none;color:var(--accent);cursor:pointer;padding:0;font-size:inherit;">API Keys</button>.</p> 820 <div id="code-signed-url-out" style="display:none;margin-top:8px;"> 821 <label style="font-size:0.63rem;color:var(--muted);display:block;margin-bottom:4px;">Signed URL</label> 822 <div style="display:flex;align-items:flex-start;gap:6px;"> 823 <code id="code-signed-url-val" style="flex:1;font-size:0.57rem;font-family:var(--font-mono);color:var(--accent);background:rgba(0,0,0,0.3);padding:5px 8px;border-radius:6px;word-break:break-all;line-height:1.6;"></code> 824 <button class="btn-ghost btn-xs" onclick="copyText(document.getElementById('code-signed-url-val').textContent,this)" style="padding:4px 6px;background:rgba(255,255,255,0.03);border:1px solid var(--border);border-radius:4px;flex-shrink:0;margin-top:1px;" title="Copy"> 825 <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> 826 </button> 827 </div> 828 </div> 829 </div> 830 </div> 831 </div> 832 </div><!-- /te-code-pane --> 833 834 <!-- Shared preview pane --> 835 <div class="te-preview-pane"> 836 <div class="te-toolbar">
837 <span style="font-size:0.65rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);">Preview</span> 838 <div class="te-pills"> 839 <button class="te-pill active" id="ve-pill-current" onclick="veSetPreviewTheme('current',this)">Current</button> 840 <button class="te-pill" id="ve-preset-gradient" onclick="veApplyPreset('gradient',this)">Gradient</button> 841 <button class="te-pill" id="ve-preset-dark" onclick="veApplyPreset('dark',this)">Dark</button> 842 <button class="te-pill" id="ve-preset-light" onclick="veApplyPreset('light',this)">Light</button> 843 <button class="te-pill" id="ve-preset-code" onclick="veApplyPreset('code',this)">Code</button> 844 <button class="te-pill" id="ve-preset-article" onclick="veApplyPreset('article',this)">Article</button> 845 <button class="te-pill" id="ve-preset-profile" onclick="veApplyPreset('profile',this)">Profile</button> 846 <button class="te-pill" id="ve-preset-minimal" onclick="veApplyPreset('minimal',this)">Minimal</button> 847 <button class="te-pill" id="ve-preset-custom" onclick="veApplyPreset('custom',this)">Custom</button> 848 </div> 849 <span style="font-size:0.7rem;color:var(--muted);font-family:var(--font-mono);">1200 à 630 px</span> 850 </div> 851 <div class="te-canvas-wrap"> 852 <div class="te-canvas-outer"> 853 <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;"> 854 <span style="font-size:0.6rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);">Live preview</span> 855 <span class="canvas-live-dot">Live</span> 856 </div> 857 <div class="te-canvas-frame" id="te-canvas-frame"></div> 858 </div> 859 </div> 860 <div class="te-infobar"> 861 <div class="ei-chips"> 862 <div class="ei-chip">SVG <span class="hi">â</span></div> 863 <div class="ei-chip">PNG ready <span class="hi">â</span></div> 864 <div class="ei-chip" id="te-chip-size">~4 KB</div> 865 </div> 866 <div class="ei-url" id="te-url-bar">api.metashot.io/og?key=msk_...&theme=custom_...</div> 867 <button class="btn-ghost" onclick="copyText(document.getElementById('te-url-bar').textContent, this)" title="Copy URL" style="padding:5px;background:rgba(255,255,255,0.04);border:1px solid var(--border);border-radius:6px;flex-shrink:0;color:var(--muted);"> 868 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> 869 </button> 870 </div> 871 </div><!-- /te-preview-pane --> 872 873 </div><!-- /te-body --> 874 </div><!-- /template-editor-view --> 875 </section> 876 877 <!-- ââ Images Section âââââââââââââââââââââââââââââââââââââââ --> 878 <section id="section-images" class="dash-section hidden"> 879 <div class="section-header"> 880 <h2>Images</h2> 881 <div class="img-toolbar"> 882 <div class="img-toolbar-search"> 883 <input type="text" id="images-search" placeholder="Search by titleâ¦" maxlength="100" oninput="debounceImageSearch()"/> 884 </div> 885 <select id="images-theme-filter" class="img-filter-select" onchange="loadImages(1)"> 886 <option value="">All themes</option> 887 <option value="gradient">Gradient</option> 888 <option value="dark">Dark</option> 889 <option value="light">Light</option> 890 <option value="code">Code</option> 891 <option value="minimal">Minimal</option> 892 <option value="article">Article</option> 893 <option value="profile">Profile</option> 894 </select> 895 <button class="img-fav-toggle" id="images-fav-btn" onclick="toggleImagesFavFilter()">â Favorites</button> 896 </div> 897 </div> 898 899 <div id="images-summary-bar" class="img-summary-bar" style="display:none;"> 900 <div class="img-summary-stat">
901 <span id="img-stat-total" class="img-summary-val">0</span> 902 <span class="img-summary-label">Stored</span> 903 </div> 904 <div class="img-summary-divider"></div> 905 <div class="img-summary-stat"> 906 <span id="img-stat-limit" class="img-summary-val">50</span> 907 <span class="img-summary-label">Limit</span> 908 </div> 909 <div class="img-progress-wrap"> 910 <div class="img-progress-track"> 911 <div id="img-stat-bar" class="img-progress-bar"></div> 912 </div> 913 <span id="img-stat-pct" style="font-size:0.7rem;color:var(--muted);min-width:34px;text-align:right;"></span> 914 </div> 915 </div> 916 917 <div id="images-grid" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px;"></div> 918 919 <div id="images-pagination" style="display:none;align-items:center;gap:10px;margin-top:28px;justify-content:center;"> 920 <button id="images-prev-btn" class="btn-ghost" onclick="loadImages(imagesPage - 1)" disabled>â Prev</button> 921 <span id="images-page-info" style="font-size:0.82rem;color:var(--muted);min-width:100px;text-align:center;"></span> 922 <button id="images-next-btn" class="btn-ghost" onclick="loadImages(imagesPage + 1)" disabled>Next â</button> 923 </div> 924 </section> 925 926 <section id="section-admin" class="dash-section hidden"> 927 <div class="section-header"><h2>Admin Panel</h2></div> 928 929 <!-- Global Stats --> 930 <div class="usage-cards" id="admin-stats-cards" style="margin-bottom: 32px;"> 931 <div class="loading">Loading statsâ¦</div> 932 </div> 933 934 <!-- Users List --> 935 <h3>Users</h3> 936 <div id="admin-users-list" class="keys-list" style="margin-top: 16px;"> 937 <div class="loading">Loading usersâ¦</div> 938 </div> 939 </section> 940 <footer class="dash-footer"> 941 <div>© 2026 MetaShot.io</div> 942 <a href="/terms.html">Terms</a> 943 <a href="/privacy.html">Privacy</a> 944 <a href="https://github.com/metashot-io/metashot-issues/issues" target="_blank" rel="noopener">Issues</a> 945 </footer> 946 </main> 947 </div> 948 949 <!-- Custom Confirm Modal --> 950 <div id="confirm-modal" class="modal-overlay hidden" style="z-index: 99999;"> 951 <div class="modal-card" style="text-align: center; max-width: 400px; padding: 32px 24px;"> 952 <div id="confirm-icon" style="margin-bottom: 16px; color: var(--danger);"> 953 <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="margin: 0 auto; display: block;"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg> 954 </div> 955 <h3 id="confirm-title" style="margin-bottom: 12px; font-size: 1.25rem;">Are you sure?</h3> 956 <p id="confirm-message" style="color: var(--muted); font-size: 0.95rem; margin-bottom: 32px; line-height: 1.5;">This action cannot be undone.</p> 957 958 <div style="display: flex; gap: 12px; justify-content: center;"> 959 <button class="btn-ghost" onclick="closeConfirmModal()" style="flex: 1;">Cancel</button> 960 <button class="btn-primary" id="confirm-btn" style="flex: 1; background: var(--danger); border-color: var(--danger);">Delete</button> 961 </div> 962 </div> 963 </div> 964 </div> 965 966 <!-- Cookie Banner --> 967 <div id="cookie-banner" class="cookie-banner hidden"> 968 <div class="cookie-content"> 969 We use cookies to improve your experience. By using MetaShot.io, you agree to our <a href="/privacy.html">Privacy Policy</a>. 970 </div> 971 <div class="cookie-actions"> 972 <button class="btn-sm btn-ghost" id="cookie-decline">Decline</button> 973 <button class="btn-sm" id="cookie-accept">Accept</button> 974 </div> 975 </div> 976 977
977<script src="/dashboard.js?v=2"></script>
977 978
978<script src="/compliance.js"></script>
vendor: 1 bytes, line 978
978
979<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"6d2797ef9e944d8da07cc5055a30d17e","r":1,"spa":2}' crossorigin="anonymous"></script>
979 980</body> 981</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.