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">&times;</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">&times;</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