Base.astro raw
1 ---
2 export interface Props {
3 title: string;
4 titleEn?: string;
5 isHome?: boolean;
6 }
7
8 const { title, titleEn, isHome } = Astro.props;
9 const path = Astro.url.pathname;
10 const active = (href: string) => path === href || (href !== '/' && path.startsWith(href));
11 ---
12
13 <!DOCTYPE html>
14 <html lang="bg">
15 <head>
16 <meta charset="UTF-8" />
17 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
18 <title>{title} | Zlattea</title>
19 <link rel="icon" href="/images/favicon.png" type="image/png" />
20 <link rel="preconnect" href="https://fonts.googleapis.com" />
21 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
22 <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,400i|Roboto+Condensed:400,700&display=swap" rel="stylesheet" />
23 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css" />
24 <link rel="stylesheet" href="/styles/global.css?v=20260727" />
25 </head>
26 <body>
27 <header class={`header${isHome ? ' home' : ''}`}>
28 <div class="container header-inner">
29 <button class="hamburger" id="hamburger-btn" aria-label="Menu">
30 <span></span><span></span><span></span>
31 </button>
32 <a href="/" class="logo">
33 <img src="/images/logo.png" alt="Zlattea" height="50" />
34 </a>
35 {isHome ? (
36 <div class="nav-row">
37 <nav class="main-nav home-nav" id="main-nav">
38 <a href="/" class:list={[{ active: active('/') }]} data-i18n="home">Начало</a>
39 <a href="/about" class:list={[{ active: active('/about') }]} data-i18n="about">За нас</a>
40 <a href="/shop" class:list={[{ active: active('/shop') }]} data-i18n="shop">Магазин</a>
41 <a href="/delivery" class:list={[{ active: active('/delivery') }]} data-i18n="delivery">Доставка</a>
42 <a href="/contact" class:list={[{ active: active('/contact') }]} data-i18n="contact">Контакти</a>
43 <div class="nav-drawer-actions-row">
44 <button class="drawer-login" data-i18n="login">Вход</button>
45 <a href="/cart" class="drawer-cart"><span data-i18n="cart">Количка</span> <span class="drawer-cart-count" style="display:none">0</span></a>
46 </div>
47 <div class="nav-drawer-footer">
48 <button id="lang-btn-drawer" class="lang-switch">BG</button>
49 <button id="logout-btn-drawer" class="drawer-logout" style="display:none">Изход</button>
50 </div>
51 </nav>
52 <div class="header-actions">
53 <button id="lang-btn" class="icon-btn" aria-label="Language">
54 <span id="lang-label">BG</span>
55 </button>
56 <button id="login-btn" class={`icon-btn${path.startsWith('/user') ? ' active' : ''}`} aria-label="Login">
57 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
58 <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
59 <circle cx="12" cy="7" r="4"/>
60 </svg>
61 </button>
62 <button id="logout-btn" class="icon-btn" aria-label="Logout" style="display:none" data-i18n="logout">
63 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
64 <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/>
65 </svg>
66 </button>
67 <a href="/cart" class:list={['icon-btn', 'cart-btn', { active: path.startsWith('/cart') }]} aria-label="Cart">
68 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
69 <circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/>
70 <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/>
71 </svg>
72 <span class="cart-count" id="cart-count">0</span>
73 </a>
74 </div>
75 </div>
76 ) : (
77 <nav class="main-nav" id="main-nav">
78 <a href="/" class:list={[{ active: active('/') }]} data-i18n="home">Начало</a>
79 <a href="/about" class:list={[{ active: active('/about') }]} data-i18n="about">За нас</a>
80 <a href="/shop" class:list={[{ active: active('/shop') }]} data-i18n="shop">Магазин</a>
81 <a href="/delivery" class:list={[{ active: active('/delivery') }]} data-i18n="delivery">Доставка</a>
82 <a href="/contact" class:list={[{ active: active('/contact') }]} data-i18n="contact">Контакти</a>
83 <div class="nav-drawer-actions-row">
84 <button class="drawer-login" data-i18n="login">Вход</button>
85 <a href="/cart" class="drawer-cart"><span data-i18n="cart">Количка</span> <span class="drawer-cart-count" style="display:none">0</span></a>
86 </div>
87 <div class="nav-drawer-footer">
88 <button id="lang-btn-drawer2" class="lang-switch">BG</button>
89 <button id="logout-btn-drawer3" class="drawer-logout" style="display:none">Изход</button>
90 </div>
91 </nav>
92 )}
93 {!isHome && (
94 <div class="header-actions">
95 <button id="lang-btn" class="icon-btn" aria-label="Language">
96 <span id="lang-label">BG</span>
97 </button>
98 <button id="login-btn" class={`icon-btn${path.startsWith('/user') ? ' active' : ''}`} aria-label="Login">
99 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
100 <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
101 <circle cx="12" cy="7" r="4"/>
102 </svg>
103 </button>
104 <button id="logout-btn" class="icon-btn" aria-label="Logout" style="display:none" data-i18n="logout">
105 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
106 <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/>
107 </svg>
108 </button>
109 <a href="/cart" class:list={['icon-btn', 'cart-btn', { active: path.startsWith('/cart') }]} aria-label="Cart">
110 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
111 <circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/>
112 <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/>
113 </svg>
114 <span class="cart-count" id="cart-count">0</span>
115 </a>
116 </div>
117 )}
118 </div>
119 </header>
120
121 <!-- Login Modal -->
122 <div id="login-modal" class="modal-overlay" style="display:none">
123 <div class="modal">
124 <button class="modal-close" id="modal-close">×</button>
125 <h3 data-i18n="loginTitle">Влезте в профила си</h3>
126
127 <!-- Email/Password form -->
128 <form id="login-form">
129 <input type="email" id="login-email" placeholder="Email" required />
130 <input type="password" id="login-password" placeholder="Password" required />
131 <button type="submit" class="btn btn-primary" style="width:100%" data-i18n="emailLogin">Вход с имейл</button>
132 </form>
133
134 <p style="text-align:center;font-size:0.85rem;margin-top:0.5rem">
135 <a href="#" id="register-link" style="color:var(--primary);text-decoration:underline" data-i18n="createAccount">Създай профил</a>
136 </p>
137
138 <div class="modal-divider"><span data-i18n="loginOr">или</span></div>
139
140 <!-- Nostr button -->
141 <button id="nostr-login-btn" class="btn btn-outline" style="width:100%">
142 <span data-i18n="nostrLogin">Вход с Nostr</span>
143 </button>
144
145 <p id="login-error" class="error-msg" style="display:none;color:red;margin-top:1rem"></p>
146 </div>
147 </div>
148
149 <!-- Register Modal -->
150 <div id="register-modal" class="modal-overlay" style="display:none">
151 <div class="modal" style="max-width:460px">
152 <button class="modal-close" id="register-close">×</button>
153 <h3 data-i18n="createAccount">Създай профил</h3>
154 <form id="register-form">
155 <input type="email" id="reg-email" placeholder="Email" required />
156 <input type="password" id="reg-password" placeholder="Парола (поне 6 символа)" minlength="6" required />
157 <input type="text" id="reg-name" placeholder="Име" />
158 <input type="tel" id="reg-phone" placeholder="Телефон" />
159 <input type="text" id="reg-city" list="reg-city-list" placeholder="Град" />
160 <datalist id="reg-city-list"></datalist>
161 <input type="text" id="reg-postcode" placeholder="Пощенски код" />
162 <input type="text" id="reg-address" placeholder="Адрес" />
163 <input type="text" id="reg-address2" placeholder="Адрес 2" />
164 <input type="text" id="reg-suburb" placeholder="Квартал" />
165 <select id="reg-courier">
166 <option value="econt">Econt</option>
167 </select>
168 <div style="display:flex;gap:12px;margin:0.25rem 0">
169 <label style="display:flex;align-items:center;gap:4px;font-size:0.85rem"><input type="radio" name="reg-dtype" value="address" checked /> До адрес</label>
170 <label style="display:flex;align-items:center;gap:4px;font-size:0.85rem"><input type="radio" name="reg-dtype" value="office" /> До офис</label>
171 </div>
172 <div id="reg-office-group" style="display:none">
173 <input type="text" id="reg-office-search" placeholder="Търсене на офис..." />
174 <select id="reg-office" size="3"></select>
175 </div>
176 <button type="submit" class="btn btn-primary" style="width:100%;margin-top:0.5rem">Създай профил</button>
177 </form>
178 <p id="register-error" class="error-msg" style="display:none"></p>
179 </div>
180 </div>
181
182 <slot />
183
184 <footer class="footer site-footer">
185 <div class="container footer-grid">
186 <div class="footer-col" style="text-align:center">
187 <p><strong data-i18n="footerOrdersPickup">За поръчки и вземане от място:</strong></p>
188 <p><a href="tel:+359896214445" style="color:var(--text);text-decoration:none;text-transform:uppercase">+359 896 214 445</a></p>
189 <p>shop@zlattea.com</p>
190 <p><a href="/terms" style="color:var(--text);text-decoration:underline" data-i18n="termsLink">Условия за ползване</a></p>
191 <p><a href="/privacy" style="color:var(--text);text-decoration:underline" data-i18n="privacyLink">Политика за поверителност</a></p>
192 <div class="footer-sep"></div>
193 <div class="footer-social">
194 <a href="https://www.facebook.com/people/zlatteacom/100090331006935/" target="_blank" rel="noopener" aria-label="Facebook"><span class="ion-social-facebook"></span></a>
195 <a href="https://www.instagram.com/zlattea.com_" target="_blank" rel="noopener" aria-label="Instagram"><span class="ion-social-instagram"></span></a>
196 <a href="https://www.tiktok.com/@zlattea.com" target="_blank" rel="noopener" aria-label="TikTok"><span class="ion-social-chrome"></span></a>
197 </div>
198 </div>
199 </div>
200 <div class="footer-built-by">
201 <p>built by <a href="https://nosta.me/npub1fjqqy4a93z5zsjwsfxqhc2764kvykfdyttvldkkkdera8dr78vhsmmleku" target="_blank" rel="noopener">npub1fj...8mleku</a></p>
202 </div>
203 </footer>
204
205 <script>
206 import { cartCount } from '../lib/cart-client';
207 import { applyLang, currentLang } from '../lib/i18n';
208
209 // Cart count
210 function updateCartCount() {
211 const count = cartCount();
212 const el = document.getElementById('cart-count');
213 if (el) {
214 el.textContent = String(count);
215 el.style.display = count > 0 ? 'flex' : 'none';
216 }
217 document.querySelectorAll('.drawer-cart-count').forEach((c: any) => {
218 c.textContent = String(count);
219 c.style.display = count > 0 ? 'inline' : 'none';
220 });
221 }
222 updateCartCount();
223 window.addEventListener('cart-updated', updateCartCount);
224
225 // Login modal
226 const loginBtn = document.getElementById('login-btn');
227 const loginModal = document.getElementById('login-modal');
228 const modalClose = document.getElementById('modal-close');
229 const loginForm = document.getElementById('login-form') as HTMLFormElement;
230 const loginError = document.getElementById('login-error');
231 const nostrBtn = document.getElementById('nostr-login-btn');
232
233 function showError(msg: string) {
234 if (loginError) {
235 loginError.textContent = msg;
236 loginError.style.display = 'block';
237 }
238 }
239 function setLoggedIn() {
240 loginBtn!.innerHTML = '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>';
241 loginBtn!.dataset.loggedIn = '1';
242 const logoutBtn = document.getElementById('logout-btn') as HTMLElement;
243 if (logoutBtn) logoutBtn.style.display = 'flex';
244 }
245 function setLoggedOut() {
246 loginBtn!.innerHTML = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>';
247 delete loginBtn!.dataset.loggedIn;
248 const logoutBtn = document.getElementById('logout-btn') as HTMLElement;
249 if (logoutBtn) logoutBtn.style.display = 'none';
250 }
251
252 if (loginBtn && loginModal) {
253 const logoutBtn = document.getElementById('logout-btn') as HTMLElement;
254 // on load, check if user is already logged in
255 (async () => {
256 try {
257 const token = localStorage.getItem('token');
258 if (!token) return;
259 const r = await fetch('/api/auth/session', { headers: { 'Authorization': 'Bearer ' + token } });
260 const d = await r.json();
261 if (d.user) {
262 setLoggedIn();
263 if (!d.user.has_password && !location.pathname.startsWith('/user')) {
264 location.href = '/user';
265 }
266 }
267 } catch {}
268 })();
269
270 logoutBtn?.addEventListener('click', async () => {
271 try {
272 const token = localStorage.getItem('token');
273 if (token) {
274 await fetch('/api/auth/logout', { method: 'POST', headers: { 'Authorization': 'Bearer ' + token } });
275 }
276 } catch {}
277 localStorage.removeItem('token');
278 localStorage.removeItem('cart');
279 setLoggedOut();
280 if (window.location.pathname === '/user') window.location.href = '/';
281 });
282 loginBtn.addEventListener('click', () => {
283 if (loginBtn.dataset.loggedIn === '1') {
284 window.location.href = '/user';
285 return;
286 }
287 loginModal.style.display = 'flex';
288 });
289 if (modalClose) modalClose.addEventListener('click', () => { loginModal.style.display = 'none'; });
290 loginModal.addEventListener('click', (e) => { if (e.target === loginModal) loginModal.style.display = 'none'; });
291 }
292
293 if (loginForm) {
294 loginForm.addEventListener('submit', async (e) => {
295 e.preventDefault();
296 const email = (document.getElementById('login-email') as HTMLInputElement).value;
297 const password = (document.getElementById('login-password') as HTMLInputElement).value;
298 try {
299 const res = await fetch('/api/auth/login', {
300 method: 'POST',
301 headers: { 'Content-Type': 'application/json' },
302 body: JSON.stringify({ email, password })
303 });
304 const data = await res.json();
305 if (res.ok) {
306 localStorage.setItem('token', data.token);
307 setLoggedIn();
308 loginModal!.style.display = 'none';
309 loginError!.style.display = 'none';
310 } else {
311 showError(data.error || 'Login failed');
312 }
313 } catch {
314 showError('Connection error');
315 }
316 });
317 }
318
319 if (nostrBtn) {
320 nostrBtn.addEventListener('click', async () => {
321 try {
322 const nostr = (window as any).nostr;
323 if (!nostr) {
324 showError('Nostr extension not found. Install nos2x or similar.');
325 return;
326 }
327 // challenge -> NIP-07 signed kind-22242 event -> server verification
328 const chRes = await fetch('/api/auth/nostr-challenge');
329 const { challenge } = await chRes.json();
330 const pubkey = await nostr.getPublicKey();
331 const event = await nostr.signEvent({
332 kind: 22242,
333 created_at: Math.floor(Date.now() / 1000),
334 tags: [['challenge', challenge]],
335 content: '',
336 pubkey,
337 });
338 const res = await fetch('/api/auth/nostr', {
339 method: 'POST',
340 headers: { 'Content-Type': 'application/json' },
341 body: JSON.stringify({ event })
342 });
343 const data = await res.json();
344 if (res.ok) {
345 localStorage.setItem('token', data.token);
346 setLoggedIn();
347 loginModal!.style.display = 'none';
348 loginError!.style.display = 'none';
349 } else {
350 showError(data.error || 'Login failed');
351 }
352 } catch (e: any) {
353 showError(e.message || 'Nostr login failed');
354 }
355 });
356 }
357
358 // register link -> opens register modal
359 const regLink = document.getElementById('register-link');
360 const regModal = document.getElementById('register-modal');
361 const regClose = document.getElementById('register-close');
362 const regForm = document.getElementById('register-form') as HTMLFormElement;
363 const regError = document.getElementById('register-error');
364
365 if (regLink && regModal) {
366 regLink.addEventListener('click', (e) => {
367 e.preventDefault();
368 loginModal!.style.display = 'none';
369 regModal.style.display = 'flex';
370 // load cities
371 fetch('/api/cities').then(r => r.json()).then((cities: string[]) => {
372 document.getElementById('reg-city-list')!.innerHTML = cities.map(c => `<option value="${c}">`).join('');
373 }).catch(() => {});
374 });
375 if (regClose) regClose.addEventListener('click', () => { regModal.style.display = 'none'; });
376 regModal.addEventListener('click', (e) => { if (e.target === regModal) regModal.style.display = 'none'; });
377
378 // office toggle
379 document.querySelectorAll('input[name="reg-dtype"]').forEach(r => {
380 r.addEventListener('change', () => {
381 const o = (document.querySelector('input[name="reg-dtype"]:checked') as HTMLInputElement).value === 'office';
382 document.getElementById('reg-office-group')!.style.display = o ? 'block' : 'none';
383 document.getElementById('reg-address-group')!.style.display = o ? 'none' : 'block';
384 });
385 });
386
387 document.getElementById('reg-city')?.addEventListener('change', loadRegOffices);
388 document.getElementById('reg-courier')?.addEventListener('change', loadRegOffices);
389
390 async function loadRegOffices() {
391 const city = (document.getElementById('reg-city') as HTMLInputElement).value;
392 const courier = (document.getElementById('reg-courier') as HTMLSelectElement).value;
393 if (!city || courier !== 'econt') { (document.getElementById('reg-office') as HTMLSelectElement).innerHTML = ''; return; }
394 try {
395 const r = await fetch(`/api/offices?city=${encodeURIComponent(city)}&courier=econt`);
396 const offs = await r.json();
397 document.getElementById('reg-office')!.innerHTML = offs.map((o: any) => `<option value="${o.id}">${o.name} - ${o.address}</option>`).join('');
398 } catch {}
399 }
400
401 document.getElementById('reg-office-search')?.addEventListener('input', () => {
402 const q = (document.getElementById('reg-office-search') as HTMLInputElement).value.toLowerCase();
403 Array.from((document.getElementById('reg-office') as HTMLSelectElement).options).forEach(o => {
404 o.style.display = o.text.toLowerCase().includes(q) ? '' : 'none';
405 });
406 });
407 }
408
409 if (regForm) {
410 regForm.addEventListener('submit', async (e) => {
411 e.preventDefault();
412 const email = (document.getElementById('reg-email') as HTMLInputElement).value;
413 const password = (document.getElementById('reg-password') as HTMLInputElement).value;
414 try {
415 const isOffice = (document.querySelector('input[name="reg-dtype"]:checked') as HTMLInputElement).value === 'office';
416 const prefs = {
417 name: (document.getElementById('reg-name') as HTMLInputElement).value || undefined,
418 phone: (document.getElementById('reg-phone') as HTMLInputElement).value || undefined,
419 city: (document.getElementById('reg-city') as HTMLInputElement).value || undefined,
420 postCode: (document.getElementById('reg-postcode') as HTMLInputElement).value || undefined,
421 address: isOffice ? undefined : (document.getElementById('reg-address') as HTMLInputElement).value,
422 address2: isOffice ? undefined : (document.getElementById('reg-address2') as HTMLInputElement).value,
423 suburb: isOffice ? undefined : (document.getElementById('reg-suburb') as HTMLInputElement).value,
424 courier: (document.getElementById('reg-courier') as HTMLSelectElement).value,
425 deliveryType: isOffice ? 'office' : 'address',
426 officeId: isOffice ? (document.getElementById('reg-office') as HTMLSelectElement).value : null,
427 };
428 const res = await fetch('/api/auth/register', {
429 method: 'POST',
430 headers: { 'Content-Type': 'application/json' },
431 body: JSON.stringify({ email, password, ...prefs, delivery_prefs: prefs }),
432 });
433 const data = await res.json();
434 if (res.ok) {
435 localStorage.setItem('token', data.token);
436 setLoggedIn();
437 regModal.style.display = 'none';
438 if (regError) { regError.style.display = 'none'; }
439 } else {
440 if (regError) { regError.textContent = data.error || 'Registration failed'; regError.style.display = 'block'; }
441 }
442 } catch {
443 if (regError) { regError.textContent = 'Connection error'; regError.style.display = 'block'; }
444 }
445 });
446 }
447
448 // Language switcher
449 let lang = currentLang();
450 applyLang(lang);
451 window.dispatchEvent(new CustomEvent('lang-changed', { detail: { lang } }));
452 const langBtn = document.getElementById('lang-btn');
453 const toggleLang = () => {
454 lang = lang === 'bg' ? 'en' : 'bg';
455 applyLang(lang);
456 document.querySelectorAll('.lang-switch').forEach((el: any) => el.textContent = lang.toUpperCase());
457 window.dispatchEvent(new CustomEvent('lang-changed', { detail: { lang } }));
458 };
459 if (langBtn) langBtn.addEventListener('click', toggleLang);
460 document.querySelectorAll('.lang-switch').forEach(el => el.addEventListener('click', toggleLang));
461
462 // wire drawer login/logout buttons to existing handlers
463 const syncDrawerAuth = () => {
464 const loggedIn = !!loginBtn?.dataset.loggedIn;
465 document.querySelectorAll('.drawer-login').forEach((b: any) => { b.style.display = ''; });
466 document.querySelectorAll('.drawer-logout').forEach((b: any) => b.style.display = loggedIn ? '' : 'none');
467 };
468 document.querySelectorAll('.drawer-login').forEach(el => el.addEventListener('click', () => {
469 const loginBtnEl = document.getElementById('login-btn') as HTMLElement;
470 if (loginBtnEl?.dataset.loggedIn === '1') {
471 window.location.href = '/user';
472 } else {
473 loginBtnEl?.click();
474 }
475 }));
476 document.querySelectorAll('.drawer-logout').forEach(el => el.addEventListener('click', () => (document.getElementById('logout-btn') as HTMLElement)?.click()));
477 // observe login state changes
478 new MutationObserver(syncDrawerAuth).observe(loginBtn!, { attributes: true, attributeFilter: ['data-logged-in'] });
479 syncDrawerAuth();
480
481 // Hamburger
482 const hamburger = document.getElementById('hamburger-btn');
483 const nav = document.getElementById('main-nav');
484 if (hamburger && nav) {
485 const isHome = nav.classList.contains('home-nav');
486 const navParent = isHome ? nav.parentNode : null;
487 const backdrop = document.createElement('div');
488 backdrop.className = 'nav-backdrop';
489 document.body.appendChild(backdrop);
490 const closeNav = () => {
491 nav.classList.remove('open');
492 hamburger.classList.remove('active');
493 backdrop.classList.remove('open');
494 document.body.style.overflow = '';
495 if (isHome && navParent && nav.parentNode === document.body) {
496 navParent.appendChild(nav);
497 }
498 };
499 hamburger.addEventListener('click', () => {
500 if (isHome && !nav.classList.contains('open') && navParent) {
501 document.body.appendChild(nav);
502 }
503 nav.classList.toggle('open');
504 hamburger.classList.toggle('active');
505 backdrop.classList.toggle('open');
506 document.body.style.overflow = nav.classList.contains('open') ? 'hidden' : '';
507 });
508 backdrop.addEventListener('click', closeNav);
509 nav.querySelectorAll('a').forEach(link => {
510 link.addEventListener('click', closeNav);
511 });
512 }
513 </script>
514
515 <style>
516 .footer-built-by {
517 text-align: center;
518 padding: 0.75rem 1rem;
519 font-size: 0.8rem;
520 color: #888;
521 border-top: 1px solid #eee;
522 }
523 .footer-built-by a {
524 color: #888;
525 text-decoration: none;
526 }
527 .footer-built-by a:hover {
528 color: var(--primary);
529 }
530
531 .nav-drawer-actions { display: none; }
532 .nav-drawer-actions-row { display: none; }
533 .nav-drawer-footer { display: none; }
534 @media (max-width: 768px) {
535 .nav-drawer-actions {
536 display: flex;
537 gap: 8px;
538 padding-bottom: 12px;
539 margin-bottom: 8px;
540 border-bottom: 1px solid #eee;
541 }
542 .nav-drawer-actions-row {
543 display: flex;
544 gap: 8px;
545 padding-bottom: 12px;
546 margin-bottom: 8px;
547 border-bottom: 1px solid #eee;
548 }
549 .nav-drawer-footer {
550 display: flex;
551 flex-direction: column;
552 gap: 8px;
553 padding-top: 12px;
554 margin-top: auto;
555 border-top: 1px solid #eee;
556 }
557 .nav-drawer-footer .lang-switch {
558 align-self: flex-start;
559 }
560 .nav-drawer-footer .drawer-logout {
561 align-self: flex-start;
562 }
563 #lang-btn, #logout-btn, #login-btn, .header-actions .cart-btn { display: none !important; }
564 .lang-switch {
565 background: none;
566 border: 1px solid #ccc;
567 border-radius: 4px;
568 padding: 4px 10px;
569 font-size: 0.85rem;
570 cursor: pointer;
571 }
572 .drawer-login, .drawer-logout {
573 background: none;
574 border: 1px solid #ccc;
575 border-radius: 4px;
576 padding: 4px 10px;
577 font-size: 0.85rem;
578 cursor: pointer;
579 color: var(--text);
580 }
581 .nav-drawer-actions-row .drawer-cart {
582 background: none;
583 border: 1px solid #ccc;
584 border-radius: 4px;
585 padding: 4px 10px;
586 font-size: 0.85rem;
587 cursor: pointer;
588 color: var(--text);
589 text-decoration: none;
590 display: inline-flex;
591 align-items: center;
592 gap: 6px;
593 }
594 }
595 </style>
596 </body>
597 </html>
598