PageSourceSearch

https://metashot.io/dashboard

html metashot.io collected 2026-09-25 22:15:54 UTC 76,193 bytes, 981 lines download raw bytes

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;">&lt;meta&gt;</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&amp;title=Hello+MetaShot&amp;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">&#10;  <rect width="1200" height="630" fill="#0f172a"/>&#10;  <text x="80" y="300" font-family="Inter" font-size="72" fill="white">{{title}}</text>&#10;</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_...&amp;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>&copy; 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.