(() => {
  const $ = (sel, root = document) => root.querySelector(sel);
  const $$ = (sel, root = document) => Array.from(root.querySelectorAll(sel));

  $$('[data-tab]').forEach(tab => tab.addEventListener('click', () => {
    const target = tab.dataset.tab;
    $$('.tab').forEach(t => t.classList.toggle('active', t.dataset.tab === target));
    $$('.tab-panel').forEach(panel => panel.classList.toggle('active', panel.dataset.panel === target));
  }));

  let deferredPrompt = null;
  const installBtn = $('#installBtn');
  window.addEventListener('beforeinstallprompt', event => {
    event.preventDefault();
    deferredPrompt = event;
    if (installBtn) installBtn.hidden = false;
  });
  if (installBtn) {
    installBtn.addEventListener('click', async () => {
      if (!deferredPrompt) return;
      deferredPrompt.prompt();
      await deferredPrompt.userChoice;
      deferredPrompt = null;
      installBtn.hidden = true;
    });
  }

  if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => navigator.serviceWorker.register('service-worker.js').catch(() => {}));
  }

  window.setQuickTime = function(minutes) {
    const start = document.querySelector('input[name="start"]');
    if (!start) return;
    const date = new Date(Date.now() + minutes * 60000);
    start.value = toLocalDatetime(date);
  };
  window.setTodayEvening = function() {
    const start = document.querySelector('input[name="start"]');
    if (!start) return;
    const date = new Date();
    date.setHours(19, 0, 0, 0);
    start.value = toLocalDatetime(date);
  };
  window.setTomorrowMorning = function() {
    const start = document.querySelector('input[name="start"]');
    if (!start) return;
    const date = new Date(Date.now() + 24 * 60 * 60000);
    date.setHours(9, 0, 0, 0);
    start.value = toLocalDatetime(date);
  };
  function toLocalDatetime(date) {
    const pad = n => String(n).padStart(2, '0');
    return `${date.getFullYear()}-${pad(date.getMonth()+1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
  }

  $$('.member-filter').forEach(btn => btn.addEventListener('click', () => {
    const member = btn.dataset.memberFilter;
    $$('.member-filter').forEach(b => b.classList.toggle('active', b === btn));
    $$('.filter-targets [data-member]').forEach(card => card.hidden = member !== 'all' && card.dataset.member !== member);
  }));
  $$('.filter').forEach(btn => btn.addEventListener('click', () => {
    const type = btn.dataset.listFilter;
    $$('.filter').forEach(b => b.classList.toggle('active', b === btn));
    $$('.list-targets [data-list-type]').forEach(row => row.hidden = type !== 'all' && row.dataset.listType !== type);
  }));


  function closeMealPickers(except = null) {
    $$('[data-meal-picker].is-open').forEach(picker => {
      if (picker === except) return;
      picker.classList.remove('is-open');
      const trigger = picker.querySelector('[data-meal-trigger]');
      if (trigger) trigger.setAttribute('aria-expanded', 'false');
    });
  }

  $$('[data-meal-picker]').forEach(picker => {
    const trigger = picker.querySelector('[data-meal-trigger]');
    const input = picker.querySelector('[data-meal-input]');
    const img = picker.querySelector('[data-meal-img]');
    const title = picker.querySelector('[data-meal-title]');
    if (!trigger || !input) return;

    trigger.addEventListener('click', event => {
      event.stopPropagation();
      if (trigger.disabled) return;
      const open = picker.classList.contains('is-open');
      closeMealPickers(open ? null : picker);
      picker.classList.toggle('is-open', !open);
      trigger.setAttribute('aria-expanded', String(!open));
    });

    picker.querySelectorAll('[data-meal-id]').forEach(option => {
      option.addEventListener('click', event => {
        event.preventDefault();
        event.stopPropagation();
        input.value = option.dataset.mealId || '';
        if (img && option.dataset.img) img.src = option.dataset.img;
        if (title && option.dataset.title) title.textContent = option.dataset.title;
        picker.querySelectorAll('[data-meal-id]').forEach(o => {
          const selected = o === option;
          o.classList.toggle('active', selected);
          o.setAttribute('aria-selected', String(selected));
        });
        closeMealPickers();
      });
    });
  });
  document.addEventListener('click', () => closeMealPickers());
  document.addEventListener('keydown', event => { if (event.key === 'Escape') closeMealPickers(); });

  const clock = $('#liveClock');
  if (clock) {
    setInterval(() => {
      clock.textContent = new Date().toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'});
    }, 15000);
  }

  let idleTimer;
  function resetIdle() {
    document.body.classList.remove('idle-mode');
    clearTimeout(idleTimer);
    idleTimer = setTimeout(() => document.body.classList.add('idle-mode'), 120000);
  }
  ['mousemove','keydown','touchstart','click'].forEach(type => window.addEventListener(type, resetIdle, {passive:true}));
  resetIdle();
})();
// v6.2 registration Family Hub choice toggle
(() => {
  const modeInputs = Array.from(document.querySelectorAll('[data-family-hub-mode]'));
  const panels = Array.from(document.querySelectorAll('[data-family-hub-panel]'));
  if (!modeInputs.length || !panels.length) return;
  const sync = () => {
    const selected = (modeInputs.find(input => input.checked) || modeInputs[0]).value;
    panels.forEach(panel => {
      const active = panel.dataset.familyHubPanel === selected;
      panel.hidden = !active;
      panel.querySelectorAll('input, select, textarea').forEach(field => {
        if (field.name === 'family_hub_code') field.required = active && selected === 'join';
      });
    });
  };
  modeInputs.forEach(input => input.addEventListener('change', sync));
  sync();
})();

// v6.4 notification bell with 30-minute reminder alarm sound
(() => {
  const bell = document.getElementById('notificationBell');
  const panel = document.getElementById('notificationPanel');
  if (!bell || !panel) return;

  function playAlarm() {
    try {
      const AudioContext = window.AudioContext || window.webkitAudioContext;
      if (!AudioContext) return;
      const ctx = new AudioContext();
      const master = ctx.createGain();
      master.gain.value = 0.08;
      master.connect(ctx.destination);
      [0, 0.18, 0.36].forEach((offset) => {
        const osc = ctx.createOscillator();
        const gain = ctx.createGain();
        osc.type = 'sine';
        osc.frequency.setValueAtTime(880, ctx.currentTime + offset);
        osc.frequency.exponentialRampToValueAtTime(660, ctx.currentTime + offset + 0.12);
        gain.gain.setValueAtTime(0, ctx.currentTime + offset);
        gain.gain.linearRampToValueAtTime(1, ctx.currentTime + offset + 0.02);
        gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + offset + 0.16);
        osc.connect(gain);
        gain.connect(master);
        osc.start(ctx.currentTime + offset);
        osc.stop(ctx.currentTime + offset + 0.18);
      });
      setTimeout(() => ctx.close().catch(() => {}), 900);
    } catch (_) {}
  }


  let armed = false;
  const hasDueSoon = Boolean(bell.querySelector('b'));
  function armReminderAudio() {
    if (armed) return;
    armed = true;
    if (hasDueSoon && panel.hidden) playAlarm();
  }
  ['click', 'touchstart', 'keydown'].forEach(type => window.addEventListener(type, armReminderAudio, { once: true, passive: true }));

  function setOpen(open) {
    panel.hidden = !open;
    panel.classList.toggle('is-open', open);
    bell.setAttribute('aria-expanded', String(open));
    if (open) playAlarm();
  }

  bell.addEventListener('click', (event) => {
    event.stopPropagation();
    setOpen(panel.hidden);
  });
  panel.addEventListener('click', event => event.stopPropagation());
  document.addEventListener('click', () => setOpen(false));
  document.addEventListener('keydown', event => { if (event.key === 'Escape') setOpen(false); });
})();


// v6.5 local login theme toggle + browser privacy restrictions
(() => {
  const KEY = 'homesynchub-local-theme';
  const root = document.documentElement;
  const btn = document.querySelector('[data-local-theme-toggle]');
  const apply = (theme) => {
    if (!theme) return;
    root.setAttribute('data-theme', theme);
    if (btn) btn.textContent = theme === 'light' ? '🌙 Dark' : '☀️ Light';
  };
  apply(localStorage.getItem(KEY) || root.getAttribute('data-theme') || 'dark');
  if (btn) btn.addEventListener('click', () => {
    const next = root.getAttribute('data-theme') === 'light' ? 'dark' : 'light';
    localStorage.setItem(KEY, next);
    apply(next);
  });
})();

(() => {
  document.addEventListener('contextmenu', event => event.preventDefault());
  document.addEventListener('dragstart', event => event.preventDefault());

  let shield = null;
  function getShield() {
    if (shield) return shield;
    shield = document.createElement('div');
    shield.className = 'privacy-screen-shield';
    shield.innerHTML = '<div><strong>Privacy Protected</strong><span>Screenshots, printing, and right-click actions are restricted in this browser session.</span></div>';
    document.body.appendChild(shield);
    return shield;
  }
  function showShield(ms = 900) {
    const node = getShield();
    node.classList.add('is-active');
    window.setTimeout(() => node.classList.remove('is-active'), ms);
  }
  document.addEventListener('keydown', event => {
    const key = String(event.key || '').toLowerCase();
    const blocked = key === 'printscreen' ||
      ((event.ctrlKey || event.metaKey) && ['p','s','u'].includes(key)) ||
      ((event.ctrlKey || event.metaKey) && event.shiftKey && ['i','j','c','s'].includes(key));
    if (!blocked) return;
    event.preventDefault();
    event.stopPropagation();
    try { navigator.clipboard && navigator.clipboard.writeText(''); } catch (_) {}
    showShield(key === 'printscreen' ? 1200 : 900);
  }, true);
  window.addEventListener('beforeprint', () => showShield(1400));
})();

// v6.6 stronger browser-level capture deterrents.
(() => {
  const getShield = () => {
    let shield = document.querySelector('.privacy-screen-shield');
    if (!shield) {
      shield = document.createElement('div');
      shield.className = 'privacy-screen-shield';
      shield.innerHTML = '<div><strong>Privacy Protected</strong><span>Screen capture, recording, printing, right-click, copy, and developer shortcuts are restricted in this browser session.</span></div>';
      document.body.appendChild(shield);
    }
    return shield;
  };
  const flashShield = (ms = 1100) => {
    const shield = getShield();
    document.body.classList.add('privacy-blur');
    shield.classList.add('is-active');
    window.setTimeout(() => {
      shield.classList.remove('is-active');
      document.body.classList.remove('privacy-blur');
    }, ms);
  };
  ['copy', 'cut'].forEach(type => document.addEventListener(type, event => {
    const target = event.target;
    if (target && ['INPUT', 'TEXTAREA'].includes(target.tagName)) return;
    event.preventDefault();
    flashShield(800);
  }, true));
  window.addEventListener('blur', () => {
    document.body.classList.add('privacy-blur');
  });
  window.addEventListener('focus', () => {
    document.body.classList.remove('privacy-blur');
  });
  document.addEventListener('visibilitychange', () => {
    if (document.hidden) document.body.classList.add('privacy-blur');
    else {
      document.body.classList.remove('privacy-blur');
      flashShield(650);
    }
  });
  document.addEventListener('keyup', event => {
    if (String(event.key || '').toLowerCase() === 'printscreen') {
      try { navigator.clipboard && navigator.clipboard.writeText(''); } catch (_) {}
      flashShield(1300);
    }
  }, true);
})();

// v6.8.1 corrected Profile dropdown beside Mobile/Desktop view toggle
(() => {
  const button = document.getElementById('profileTopButton');
  const panel = document.getElementById('profileDropdownPanel');
  if (!button || !panel) return;
  const setOpen = (open) => {
    panel.hidden = !open;
    panel.classList.toggle('is-open', open);
    button.setAttribute('aria-expanded', String(open));
  };
  button.addEventListener('click', (event) => {
    event.stopPropagation();
    setOpen(panel.hidden);
  });
  panel.addEventListener('click', event => event.stopPropagation());
  document.addEventListener('click', () => setOpen(false));
  document.addEventListener('keydown', event => { if (event.key === 'Escape') setOpen(false); });
})();


// v7.0 mobile sidebar toggle for rebuilt dashboard shell
(() => {
  const toggle = document.querySelector('.sidebar-toggle-ref');
  if (!toggle) return;
  toggle.addEventListener('click', () => document.body.classList.toggle('sidebar-open'));
  document.addEventListener('click', (event) => {
    if (!document.body.classList.contains('sidebar-open')) return;
    if (event.target.closest('.dashboard-ref-sidebar') || event.target.closest('.sidebar-toggle-ref')) return;
    document.body.classList.remove('sidebar-open');
  });
})();


// v7.2 Add New modal: type-aware grouping and placeholders.
(() => {
  const form = document.querySelector('[data-add-new-form]');
  if (!form) return;
  const radios = Array.from(form.querySelectorAll('input[name="type"]'));
  const titleInput = form.querySelector('[data-title-input]');
  const categoryInput = form.querySelector('[data-category-input]');
  const notesInput = form.querySelector('[data-notes-input]');
  const mealSlot = form.querySelector('select[name="meal_slot"]');
  const pointInput = form.querySelector('input[name="points"]');
  const starInput = form.querySelector('input[name="stars"]');
  const examples = {
    task: { title: 'Example: Review school bag, Pay bill, Prepare documents', category: 'Home, School, Personal', notes: 'Add checklist details, reminders, or shared instructions.' },
    chore: { title: 'Example: Clean kitchen, Take out trash, Fold laundry', category: 'Home, Cleaning, Routine', notes: 'Add chore rules, approval notes, or reward conditions.' },
    event: { title: 'Example: Family dinner, Soccer practice, Doctor appointment', category: 'Family, School, Appointment', notes: 'Add agenda, invite details, or event notes.' },
    meal: { title: 'Example: Chicken Adobo dinner, Tapsilog breakfast', category: 'Breakfast, Lunch, Dinner, Snacks', notes: 'Add ingredients line by line to build grocery ideas.' }
  };
  function sync() {
    const type = (radios.find(r => r.checked) || radios[0]).value;
    form.dataset.itemType = type;
    form.querySelectorAll('[data-show-for]').forEach(node => {
      const allowed = String(node.dataset.showFor || '').split(/\s+/).filter(Boolean);
      const show = allowed.includes(type);
      node.hidden = !show;
      node.querySelectorAll('input,select,textarea').forEach(control => {
        control.disabled = !show;
      });
    });
    if (titleInput) titleInput.placeholder = examples[type].title;
    if (categoryInput) categoryInput.placeholder = examples[type].category;
    if (notesInput) notesInput.placeholder = examples[type].notes;
    if (mealSlot && type === 'meal' && mealSlot.value === '') mealSlot.value = 'Dinner';
    if (pointInput && !pointInput.value) pointInput.value = type === 'event' ? '0' : '10';
    if (starInput && !starInput.value) starInput.value = type === 'chore' ? '1' : '0';
  }
  radios.forEach(radio => radio.addEventListener('change', sync));
  const dialog = document.getElementById('addModal');
  if (dialog) dialog.addEventListener('close', () => window.setTimeout(sync, 0));
  sync();
})();

// v7.7 Joshleen AI workflow assistant and global search shortcut.
(() => {
  const aiPanel = document.getElementById('joshleenAiPanel');
  const aiToggles = Array.from(document.querySelectorAll('[data-ai-toggle]'));
  const closeButtons = Array.from(document.querySelectorAll('[data-ai-close]'));
  const minimizeButtons = Array.from(document.querySelectorAll('[data-ai-minimize]'));
  const messages = aiPanel ? aiPanel.querySelector('[data-ai-messages]') : null;
  const form = aiPanel ? aiPanel.querySelector('[data-ai-form]') : null;
  const input = form ? form.querySelector('input[name="message"]') : null;
  const fileInput = form ? form.querySelector('[data-ai-file-input]') : null;
  const attachBtn = form ? form.querySelector('[data-ai-attach]') : null;
  const voiceBtn = form ? form.querySelector('[data-ai-voice]') : null;
  const attachmentPreview = form ? form.querySelector('[data-ai-attachment-preview]') : null;
  const prompts = Array.from(document.querySelectorAll('[data-ai-prompt]'));
  const state = { pendingDraft: null, lastCreatedItem: null, awaitingReminder: false };

  const pad = (n) => String(n).padStart(2, '0');
  const toLocalInputValue = (date) => `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
  const displayDateTime = (value) => {
    if (!value) return 'no time set';
    const d = new Date(value);
    if (Number.isNaN(d.getTime())) return value;
    return d.toLocaleString([], { weekday: 'short', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
  };
  const escapeHtml = (text) => String(text || '').replace(/[&<>'"]/g, ch => ({'&':'&amp;','<':'&lt;','>':'&gt;',"'":'&#39;','"':'&quot;'}[ch]));

  const setOpen = (open) => {
    if (!aiPanel) return;
    document.body.classList.toggle('ai-panel-open', open);
    aiPanel.hidden = !open;
    aiPanel.classList.toggle('is-open', open);
    if (!open) aiPanel.classList.remove('is-minimized');
    aiToggles.forEach(btn => btn.setAttribute('aria-expanded', String(open)));
    if (open && input) window.setTimeout(() => input.focus(), 120);
  };

  const appendMessage = (role, text, icon, actions = []) => {
    if (!messages) return null;
    const article = document.createElement('article');
    article.className = `ai-msg ${role}`;
    const avatar = document.createElement('span');
    avatar.textContent = icon || (role === 'user' ? '👤' : '🤖');
    const bubble = document.createElement('div');
    bubble.className = 'ai-bubble-content';
    const p = document.createElement('p');
    p.textContent = text;
    bubble.appendChild(p);
    if (actions.length) {
      const actionWrap = document.createElement('div');
      actionWrap.className = 'ai-action-row';
      actions.forEach(action => {
        const btn = document.createElement('button');
        btn.type = 'button';
        btn.className = `ai-action-btn ${action.variant || ''}`.trim();
        btn.textContent = action.label;
        btn.addEventListener('click', action.onClick);
        actionWrap.appendChild(btn);
      });
      bubble.appendChild(actionWrap);
    }
    article.append(avatar, bubble);
    messages.appendChild(article);
    messages.scrollTop = messages.scrollHeight;
    return article;
  };

  const appendBot = (text, actions = []) => appendMessage('bot', text, '🤖', actions);

  const removeTyping = (typing) => { if (typing && typing.parentNode) typing.remove(); };

  const postAssistantAction = async (payload) => {
    const body = new URLSearchParams();
    Object.entries(payload).forEach(([key, value]) => body.append(key, value == null ? '' : String(value)));
    const response = await fetch('index.php', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest' },
      body,
      credentials: 'same-origin'
    });
    let data = null;
    try { data = await response.json(); } catch (err) { data = { ok: false, message: 'Joshleen AI could not read the server response.' }; }
    if (!response.ok && data && !data.message) data.message = 'The action could not be completed.';
    return data;
  };


  const postAssistantFormData = async (formData) => {
    const response = await fetch('index.php', {
      method: 'POST',
      headers: { 'X-Requested-With': 'XMLHttpRequest' },
      body: formData,
      credentials: 'same-origin'
    });
    let data = null;
    try { data = await response.json(); } catch (err) { data = { ok: false, message: 'Joshleen AI could not read the upload response.' }; }
    if (!response.ok && data && !data.message) data.message = 'The upload could not be completed.';
    return data;
  };

  const nextWeekday = (weekdayIndex) => {
    const now = new Date();
    const target = new Date(now);
    const days = (weekdayIndex - now.getDay() + 7) % 7 || 7;
    target.setDate(now.getDate() + days);
    return target;
  };

  const parseTime = (text) => {
    let match = text.match(/\b(1[0-2]|0?\d)(?::([0-5]\d))?\s*(am|pm)\b/i);
    if (match) {
      let hour = parseInt(match[1], 10);
      const minute = parseInt(match[2] || '0', 10);
      const meridiem = match[3].toLowerCase();
      if (meridiem === 'pm' && hour !== 12) hour += 12;
      if (meridiem === 'am' && hour === 12) hour = 0;
      return { hour, minute, raw: match[0] };
    }
    match = text.match(/\b([01]?\d|2[0-3]):([0-5]\d)\b/);
    if (match) return { hour: parseInt(match[1], 10), minute: parseInt(match[2], 10), raw: match[0] };
    return null;
  };

  const parseDate = (text) => {
    const lower = text.toLowerCase();
    const today = new Date();
    let date = null;
    let raw = '';
    if (/\btoday\b/.test(lower)) { date = new Date(today); raw = 'today'; }
    if (/\btomorrow\b/.test(lower)) { date = new Date(today); date.setDate(date.getDate() + 1); raw = 'tomorrow'; }
    const dayMap = { sunday: 0, sun: 0, monday: 1, mon: 1, tuesday: 2, tue: 2, wednesday: 3, wed: 3, thursday: 4, thu: 4, friday: 5, fri: 5, saturday: 6, sat: 6 };
    const dayMatch = lower.match(/\b(mon(?:day)?|tue(?:sday)?|wed(?:nesday)?|thu(?:rsday)?|fri(?:day)?|sat(?:urday)?|sun(?:day)?)\b/);
    if (dayMatch && !date) {
      const key = dayMatch[1].slice(0, 3);
      const full = Object.keys(dayMap).find(k => k.slice(0, 3) === key) || dayMatch[1];
      date = nextWeekday(dayMap[full]);
      raw = dayMatch[0];
    }
    const numeric = lower.match(/\b(\d{1,2})[\/\-](\d{1,2})(?:[\/\-](\d{2,4}))?\b/);
    if (numeric) {
      const d = parseInt(numeric[1], 10);
      const m = parseInt(numeric[2], 10) - 1;
      const y = numeric[3] ? parseInt(numeric[3].length === 2 ? `20${numeric[3]}` : numeric[3], 10) : today.getFullYear();
      date = new Date(y, m, d);
      raw = numeric[0];
    }
    return date ? { date, raw } : null;
  };

  const parseReminder = (text) => {
    const lower = text.toLowerCase();
    let match = lower.match(/\b(\d+)\s*(minutes?|mins?|m)\s*(?:before|early|prior)?\b/);
    if (match) return parseInt(match[1], 10);
    match = lower.match(/\b(\d+)\s*(hours?|hrs?|h)\s*(?:before|early|prior)?\b/);
    if (match) return parseInt(match[1], 10) * 60;
    if (/\ban hour\b|\bone hour\b/.test(lower)) return 60;
    if (/\b30\s*min/.test(lower)) return 30;
    if (/\bno reminder\b|\bwithout reminder\b/.test(lower)) return 0;
    return null;
  };

  const parseIntent = (text) => {
    const lower = text.toLowerCase();
    if (/\b(event|meeting|appointment|schedule|calendar)\b/.test(lower)) return 'event';
    if (/\b(chore|clean|wash|trash|laundry|sweep|mop|dishes|kitchen)\b/.test(lower)) return 'chore';
    if (/\b(meal|breakfast|lunch|dinner|snack|cook|grocery|groceries)\b/.test(lower)) return 'meal';
    if (/\b(task|todo|to-do|homework|assignment|worksheet|remind me|reminder|call|submit|finish|prepare|pay|send)\b/.test(lower)) return 'task';
    return '';
  };

  const cleanTitle = (text, intent, dateRaw, timeRaw) => {
    let title = ` ${text} `;
    title = title.replace(/\b(please|can you|could you|joshleen ai|joshleen)\b/gi, ' ');
    title = title.replace(/\b(create|add|make|set|schedule|book|put|remind me to|remind me|set a reminder to|i need to|i have to|please notify me to)\b/gi, ' ');
    title = title.replace(/\b(task|todo|to-do|event|calendar entry|meeting|appointment|chore|meal|reminder)\b/gi, ' ');
    title = title.replace(/\b(today|tomorrow|tonight|this morning|this afternoon|this evening)\b/gi, ' ');
    title = title.replace(/\b(on|by|at|for)\s+(mon(?:day)?|tue(?:sday)?|wed(?:nesday)?|thu(?:rsday)?|fri(?:day)?|sat(?:urday)?|sun(?:day)?)\b/gi, ' ');
    if (dateRaw) title = title.replace(new RegExp(dateRaw.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'ig'), ' ');
    if (timeRaw) title = title.replace(new RegExp(timeRaw.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'ig'), ' ');
    title = title.replace(/\b(at|by|on|for|from|to)\b\s*$/gi, ' ');
    title = title.replace(/\s+/g, ' ').trim();
    if (!title) {
      if (intent === 'meal') return 'Meal plan item';
      if (intent === 'chore') return 'New chore';
      if (intent === 'event') return 'New event';
      return 'New task';
    }
    return title.charAt(0).toUpperCase() + title.slice(1);
  };

  const interpretRequest = (text) => {
    const lower = text.toLowerCase().trim();

    if (state.pendingDraft) {
      if (/^(save|confirm|yes|ok|okay)\b/.test(lower)) return { kind: 'savePending', draft: state.pendingDraft };
      if (/^(cancel|discard|no)\b/.test(lower)) { state.pendingDraft = null; return { kind: 'answer', message: 'Draft cancelled.' }; }
      if (/\b(edit|change)\b/.test(lower)) return { kind: 'editPending', draft: state.pendingDraft };
      if (/\b(attendee|invite|participant|shared email)\b/.test(lower)) {
        const email = text.match(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i);
        let person = email ? email[0] : text.replace(/\b(add|invite|as|an|a|attendee|participant|to|the|event|meeting)\b/gi, ' ').replace(/\s+/g, ' ').trim();
        if (!person) person = 'New attendee';
        const existing = state.pendingDraft.invitees ? `${state.pendingDraft.invitees}, ` : '';
        state.pendingDraft.invitees = `${existing}${person}`;
        return { kind: 'draftUpdate', draft: state.pendingDraft, message: `Added ${person} as an attendee to the draft.` };
      }
    }

    const intent = parseIntent(text);

    if (/\b(done|complete|completed|finished|finish)\b/.test(lower) && /\b(homework|assignment|worksheet|chore|dishes|dish|wash|clean|trash|laundry|mop|sweep)\b/.test(lower)) {
      return { kind: 'completeItem', message: text };
    }

    if (state.awaitingReminder && state.lastCreatedItem) {
      if (/^(no|not now|skip|cancel)\b/.test(lower)) {
        state.awaitingReminder = false;
        return { kind: 'answer', message: 'Okay, I will keep the current reminder setting.' };
      }
      const reminder = parseReminder(text) ?? (/\byes\b|\bok\b|\bsure\b/.test(lower) ? 60 : null);
      if (reminder != null) return { kind: 'updateReminder', itemId: state.lastCreatedItem.id, minutes: reminder, title: state.lastCreatedItem.title };
      return { kind: 'clarify', message: 'How early should I remind you? Example: 30 minutes before, 1 hour before, or no reminder.' };
    }

    if (!intent) {
      if (/\bhomework\b/.test(lower)) return { kind: 'answer', message: 'Joshleen AI does not check homework. Upload the homework photo/file under the exact Todo item, then a parent can approve it or request correction.' };
      if (/\bdue|next 30|upcoming|reminder/.test(lower)) return { kind: 'answer', message: 'Open the notification bell to see upcoming reminders. Joshleen AI uses a 30-minute reminder window by default.' };
      if (/\bpoint|star|reward/.test(lower)) return { kind: 'answer', message: 'Rewards are released when assigned Todo List items are marked Done. Homework needs parent approval first; chores complete immediately.' };
      if (/\bfamily hub|workspace|hub/.test(lower)) return { kind: 'answer', message: 'Family Hub data stays isolated. Use the Profile dropdown to access Family Hubs, Rewards, and Sync.' };
      return { kind: 'answer', message: 'I can create tasks, chores, events, and meal items. Try: “Create a task to call John tomorrow at 10 AM.”' };
    }
    const dateInfo = parseDate(text);
    const timeInfo = parseTime(text);
    if ((intent === 'event' || intent === 'task' || intent === 'chore' || intent === 'meal') && !dateInfo) {
      return { kind: 'clarify', message: 'I understood the item type, but I could not find a due date. Please include a date, for example “tomorrow” or “Friday”.' };
    }
    if ((intent === 'event' || /\bdue|by|at|remind/.test(lower)) && !timeInfo) {
      return { kind: 'clarify', message: 'I found the date, but I need the time. Example: “Friday at 5 PM” or “tomorrow at 10 AM”.' };
    }

    const start = new Date(dateInfo.date);
    if (timeInfo) start.setHours(timeInfo.hour, timeInfo.minute, 0, 0);
    else start.setHours(intent === 'meal' ? 18 : 9, 0, 0, 0);
    const end = new Date(start);
    end.setMinutes(end.getMinutes() + (intent === 'event' ? 60 : 30));
    const reminder = parseReminder(text);
    const title = cleanTitle(text, intent, dateInfo.raw, timeInfo ? timeInfo.raw : '');

    return {
      kind: 'draft',
      draft: {
        type: intent,
        title,
        start: toLocalInputValue(start),
        end: toLocalInputValue(end),
        member: 'Family',
        category: intent === 'meal' ? 'Meal' : intent === 'chore' ? 'Home' : intent === 'event' ? 'Family' : 'Personal',
        location: '',
        meal_slot: intent === 'meal' ? (/\bbreakfast\b/.test(lower) ? 'Breakfast' : /\blunch\b/.test(lower) ? 'Lunch' : /\bsnack/.test(lower) ? 'Snacks' : 'Dinner') : '',
        repeat: /\bevery day|daily\b/.test(lower) ? 'daily' : /\bevery week|weekly\b/.test(lower) ? 'weekly' : 'none',
        points: intent === 'event' ? 0 : 10,
        stars: intent === 'chore' || intent === 'meal' ? 1 : 0,
        reminder_minutes: reminder == null ? 30 : reminder,
        sync_provider: 'HomeSync',
        sync_mode: 'local',
        notes: `Created by Joshleen AI from: “${text}”`,
        invitees: ''
      }
    };
  };

  const fillAddModal = (draft) => {
    const dialog = document.getElementById('addModal');
    const addForm = dialog ? dialog.querySelector('[data-add-new-form]') : null;
    if (!dialog || !addForm) return false;
    const set = (name, value) => { const field = addForm.querySelector(`[name="${name}"]`); if (field) field.value = value == null ? '' : value; };
    const radio = addForm.querySelector(`input[name="type"][value="${draft.type}"]`);
    if (radio) { radio.checked = true; radio.dispatchEvent(new Event('change', { bubbles: true })); }
    ['title','start','end','member','category','location','meal_slot','repeat','points','stars','reminder_minutes','sync_provider','sync_mode','notes','invitees'].forEach(k => set(k, draft[k]));
    dialog.showModal();
    return true;
  };

  const saveDraft = async (draft) => {
    const payload = { action: 'ai_create_item', ...draft };
    appendBot('Saving your confirmed draft now…');
    try {
      const result = await postAssistantAction(payload);
      if (!result.ok) { appendBot(result.message || 'I could not save that item.'); return; }
      state.lastCreatedItem = result.item;
      state.pendingDraft = null;
      state.awaitingReminder = true;
      appendBot(`${result.message}. Would you like me to adjust the notification reminder? You can say “yes, 1 hour before”, “30 minutes before”, or “no”.`, [
        { label: '30 min before', onClick: () => submitMessage('30 minutes before') },
        { label: '1 hour before', onClick: () => submitMessage('1 hour before') },
        { label: 'No', variant: 'muted', onClick: () => submitMessage('no') },
      ]);
    } catch (err) {
      appendBot('I could not reach the local HomeSyncHub server. Please check your XAMPP/Apache connection.');
    }
  };

  const showDraft = (draft) => {
    state.pendingDraft = draft;
    appendBot(`${draft.type.charAt(0).toUpperCase() + draft.type.slice(1)} draft ready: “${draft.title}” at ${displayDateTime(draft.start)}. Reminder: ${draft.reminder_minutes} minutes before.`, [
      { label: 'Save', onClick: () => saveDraft(draft) },
      { label: 'Edit in Add New', variant: 'muted', onClick: () => fillAddModal(draft) },
      { label: 'Cancel', variant: 'danger', onClick: () => { state.pendingDraft = null; appendBot('Draft cancelled.'); } },
    ]);
  };

  const clearAttachments = () => {
    if (fileInput) fileInput.value = '';
    if (attachmentPreview) { attachmentPreview.hidden = true; attachmentPreview.textContent = ''; }
  };

  const refreshAttachmentPreview = () => {
    if (!fileInput || !attachmentPreview) return;
    const files = Array.from(fileInput.files || []);
    attachmentPreview.hidden = files.length === 0;
    attachmentPreview.textContent = files.length ? files.map(file => file.name).join(', ') : '';
  };

  const submitChatUpload = async (text) => {
    const files = fileInput ? Array.from(fileInput.files || []) : [];
    if (!files.length) return submitMessage(text);
    const clean = String(text || '').trim() || 'File upload';
    setOpen(true);
    if (aiPanel) aiPanel.classList.remove('is-minimized');
    appendMessage('user', `${clean} (${files.length} attachment${files.length > 1 ? 's' : ''})`, '👤');
    if (input) input.value = '';
    const typing = appendMessage('bot', 'Joshleen AI is uploading the attachment…', '🤖');
    if (typing) typing.classList.add('typing');
    const data = new FormData();
    data.append('action', 'ai_chat_upload');
    data.append('message', clean);
    files.forEach(file => data.append('chat_files[]', file));
    try {
      const result = await postAssistantFormData(data);
      removeTyping(typing);
      appendBot(result.message || 'Upload processed.');
      clearAttachments();
    } catch (err) {
      removeTyping(typing);
      appendBot('I could not upload that file because the local server did not respond.');
    }
  };

  const submitMessage = (text) => {
    const clean = String(text || '').trim();
    if (!clean) return;
    setOpen(true);
    if (aiPanel) aiPanel.classList.remove('is-minimized');
    appendMessage('user', clean, '👤');
    if (input) input.value = '';
    const typing = appendMessage('bot', 'Joshleen AI is typing…', '🤖');
    if (typing) typing.classList.add('typing');
    window.setTimeout(async () => {
      removeTyping(typing);
      const result = interpretRequest(clean);
      if (result.kind === 'draft') return showDraft(result.draft);
      if (result.kind === 'draftUpdate') { appendBot(result.message, [
        { label: 'Save', onClick: () => saveDraft(result.draft) },
        { label: 'Edit in Add New', variant: 'muted', onClick: () => fillAddModal(result.draft) },
        { label: 'Cancel', variant: 'danger', onClick: () => { state.pendingDraft = null; appendBot('Draft cancelled.'); } },
      ]); return; }
      if (result.kind === 'savePending') return saveDraft(result.draft);
      if (result.kind === 'editPending') { fillAddModal(result.draft); appendBot('Opened the draft in Add New so you can edit it before saving.'); return; }
      if (result.kind === 'completeItem') {
        try {
          const completed = await postAssistantAction({ action: 'ai_complete_item', message: result.message });
          appendBot(completed.message || 'Completion request processed.');
        } catch (err) {
          appendBot('I could not complete that item because the local server did not respond.');
        }
        return;
      }
      if (result.kind === 'updateReminder') {
        try {
          const update = await postAssistantAction({ action: 'ai_update_reminder', item_id: result.itemId, reminder_minutes: result.minutes });
          state.awaitingReminder = false;
          appendBot(update.message || `Reminder updated to ${result.minutes} minutes before.`);
        } catch (err) {
          appendBot('I could not update the reminder because the local server did not respond.');
        }
        return;
      }
      appendBot(result.message || 'I can help with Calendar, Todo List, Meals, Rewards, reminders, and Family Hub actions.');
    }, 420);
  };

  if (aiPanel && aiToggles.length) {
    aiToggles.forEach(btn => btn.addEventListener('click', () => setOpen(!document.body.classList.contains('ai-panel-open'))));
    closeButtons.forEach(btn => btn.addEventListener('click', () => setOpen(false)));
    minimizeButtons.forEach(btn => btn.addEventListener('click', () => aiPanel.classList.toggle('is-minimized')));
    if (attachBtn && fileInput) attachBtn.addEventListener('click', () => fileInput.click());
    if (fileInput) fileInput.addEventListener('change', refreshAttachmentPreview);
    if (voiceBtn) voiceBtn.addEventListener('click', () => {
      const Recognition = window.SpeechRecognition || window.webkitSpeechRecognition;
      if (!Recognition) { appendBot('Voice command is not supported in this browser. You can still type the command.'); return; }
      const recognition = new Recognition();
      recognition.lang = navigator.language || 'en-SG';
      recognition.interimResults = false;
      recognition.maxAlternatives = 1;
      voiceBtn.classList.add('is-listening');
      recognition.onresult = event => {
        const transcript = event.results && event.results[0] && event.results[0][0] ? event.results[0][0].transcript : '';
        if (input) input.value = transcript;
        appendBot('Voice captured. Press send to confirm, or edit the text first.');
      };
      recognition.onerror = () => appendBot('Microphone permission was not granted or voice capture failed.');
      recognition.onend = () => voiceBtn.classList.remove('is-listening');
      recognition.start();
    });
    if (form) form.addEventListener('submit', (event) => { event.preventDefault(); const hasFiles = fileInput && fileInput.files && fileInput.files.length > 0; hasFiles ? submitChatUpload(input ? input.value : '') : submitMessage(input ? input.value : ''); });
    prompts.forEach(btn => btn.addEventListener('click', () => submitMessage(btn.getAttribute('data-ai-prompt') || btn.textContent || '')));
    document.addEventListener('keydown', event => { if (event.key === 'Escape') setOpen(false); });
  }

  const search = document.querySelector('.global-search input[name="q"]');
  if (search) {
    document.addEventListener('keydown', event => {
      if ((event.ctrlKey || event.metaKey) && String(event.key).toLowerCase() === 'k') {
        event.preventDefault();
        search.focus();
        search.select();
      }
    });
  }
})();

// v8.0 device shell: iOS/PWA install guidance, viewport units, and standalone classes.
(() => {
  const root = document.documentElement;
  const ua = navigator.userAgent || '';
  const isIOS = /iPad|iPhone|iPod/.test(ua) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
  const isStandalone = window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true;
  const isCoarsePointer = window.matchMedia('(hover: none) and (pointer: coarse)').matches;
  root.classList.toggle('ios-device', isIOS);
  root.classList.toggle('pwa-standalone', isStandalone);
  root.classList.toggle('touch-device', isCoarsePointer);
  if (document.body) {
    document.body.classList.toggle('ios-device', isIOS);
    document.body.classList.toggle('pwa-standalone', isStandalone);
    document.body.classList.toggle('touch-device', isCoarsePointer);
  }

  const setViewportHeight = () => {
    const height = window.visualViewport ? window.visualViewport.height : window.innerHeight;
    root.style.setProperty('--app-height', `${Math.round(height)}px`);
  };
  setViewportHeight();
  window.addEventListener('resize', setViewportHeight, { passive: true });
  window.addEventListener('orientationchange', () => setTimeout(setViewportHeight, 250), { passive: true });
  if (window.visualViewport) window.visualViewport.addEventListener('resize', setViewportHeight, { passive: true });

  const installBtn = document.getElementById('installBtn');
  if (installBtn && isIOS && !isStandalone) {
    installBtn.hidden = false;
    installBtn.textContent = 'Add to iPhone';
    installBtn.addEventListener('click', () => {
      window.alert('Install HomeSyncHub on iPhone/iPad: open this page in Safari, tap Share, then choose “Add to Home Screen”.');
    });
  }

  document.addEventListener('focusin', event => {
    const field = event.target && event.target.closest ? event.target.closest('input, textarea, select') : null;
    if (!field || !isIOS) return;
    setTimeout(() => field.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'smooth' }), 250);
  });
})();

// v9.4 auth visibility icon buttons, remember login helper, and parent/child registration prompt.
(() => {
  const $$ = (sel, root = document) => Array.from(root.querySelectorAll(sel));
  const $ = (sel, root = document) => root.querySelector(sel);

  function activateAuthTab(target) {
    $$('[data-tab]').forEach(t => t.classList.toggle('active', t.dataset.tab === target));
    $$('.tab-panel').forEach(panel => panel.classList.toggle('active', panel.dataset.panel === target));
  }

  $$('[data-visibility-toggle]').forEach(button => {
    const shell = button.closest('.auth-input-shell');
    const input = shell ? shell.querySelector('[data-visibility-input]') : null;
    if (!input) return;
    const kind = input.dataset.visibilityKind || (input.type === 'password' ? 'password' : 'mask');
    const syncButton = () => {
      const visible = kind === 'password' ? input.type !== 'password' : !input.classList.contains('is-masked');
      button.classList.toggle('is-visible', visible);
      button.setAttribute('aria-label', visible ? 'Hide this field' : 'Show this field');
      button.setAttribute('title', visible ? 'Hide' : 'Show');
      button.setAttribute('aria-pressed', String(visible));
      if (!button.querySelector('.visibility-icon')) {
        button.innerHTML = '<span class="visibility-icon" aria-hidden="true"></span><span class="sr-only">Show or hide field</span>';
      }
    };
    if (kind === 'mask') input.classList.remove('is-masked');
    syncButton();
    button.addEventListener('click', () => {
      if (kind === 'password') {
        input.type = input.type === 'password' ? 'text' : 'password';
      } else {
        input.classList.toggle('is-masked');
      }
      input.focus({ preventScroll: true });
      syncButton();
    });
  });

  const loginForm = document.querySelector('[data-login-form]');
  const loginEmail = document.querySelector('[data-login-username]');
  const rememberBox = document.querySelector('[data-remember-credentials]');
  if (loginForm && loginEmail && rememberBox) {
    const key = 'homesynchub-remembered-login-email';
    try {
      const savedEmail = localStorage.getItem(key) || '';
      if (savedEmail && !loginEmail.value) {
        loginEmail.value = savedEmail;
        rememberBox.checked = true;
      }
    } catch (_) {}
    loginForm.addEventListener('submit', () => {
      try {
        if (rememberBox.checked) localStorage.setItem(key, loginEmail.value.trim());
        else localStorage.removeItem(key);
      } catch (_) {}
    });
  }

  const registerForm = document.querySelector('[data-register-form]');
  if (!registerForm) return;
  const chooser = registerForm.querySelector('[data-register-role-choice]');
  const fields = registerForm.querySelector('[data-register-fields]');
  const accountInput = registerForm.querySelector('[data-account-type-input]');
  const title = registerForm.querySelector('[data-account-type-title]');
  const eyebrow = registerForm.querySelector('[data-account-type-eyebrow]');
  const help = registerForm.querySelector('[data-register-help]');
  const submit = registerForm.querySelector('[data-register-submit]');
  const back = registerForm.querySelector('[data-account-type-back]');
  const parentOnly = registerForm.querySelector('[data-parent-account-option]');
  const createMode = registerForm.querySelector('input[name="family_hub_mode"][value="create"]');
  const joinMode = registerForm.querySelector('input[name="family_hub_mode"][value="join"]');
  let lastFocusBeforePrompt = null;

  function fireFamilyHubModeSync() {
    const active = registerForm.querySelector('input[name="family_hub_mode"]:checked');
    if (active) active.dispatchEvent(new Event('change', { bubbles: true }));
  }

  function promptIsOpen() {
    return !!(chooser && !chooser.hidden);
  }

  function showChooser(openPrompt = true) {
    if (fields) fields.hidden = true;
    if (accountInput) accountInput.value = '';
    if (!chooser) return;
    chooser.hidden = !openPrompt;
    document.body.classList.toggle('auth-choice-open', openPrompt);
    if (openPrompt) {
      lastFocusBeforePrompt = document.activeElement instanceof HTMLElement ? document.activeElement : null;
      const firstChoice = chooser.querySelector('[data-select-account-type]');
      setTimeout(() => firstChoice?.focus({ preventScroll: true }), 40);
    }
  }

  function closeChooser() {
    if (chooser) chooser.hidden = true;
    document.body.classList.remove('auth-choice-open');
    if (lastFocusBeforePrompt && document.contains(lastFocusBeforePrompt)) {
      lastFocusBeforePrompt.focus({ preventScroll: true });
    }
  }

  function setAccountType(type) {
    const isChild = type === 'child';
    if (accountInput) accountInput.value = isChild ? 'child' : 'parent';
    closeChooser();
    if (fields) fields.hidden = false;
    if (title) title.textContent = isChild ? 'Create Child Account' : 'Create Parent Account';
    if (eyebrow) eyebrow.textContent = isChild ? 'Child registration' : 'Parent registration';
    if (submit) submit.textContent = isChild ? 'Create Child Account' : 'Create Parent Account';
    if (help) help.textContent = isChild
      ? 'Child accounts must join an existing Family Hub using the code shared by a parent or admin.'
      : 'Parent accounts can create a new Family Hub or join an existing one. Parent accounts receive approval permissions.';
    if (parentOnly) {
      parentOnly.hidden = isChild;
      parentOnly.setAttribute('aria-hidden', String(isChild));
    }
    if (createMode) createMode.disabled = isChild;
    if (isChild && joinMode) joinMode.checked = true;
    if (!isChild && createMode && !(joinMode && joinMode.checked)) createMode.checked = true;
    fireFamilyHubModeSync();
    setTimeout(() => fields?.querySelector('input[name="name"]')?.focus({ preventScroll: true }), 50);
  }

  registerForm.querySelectorAll('[data-select-account-type]').forEach(button => {
    button.addEventListener('click', () => setAccountType(button.dataset.selectAccountType === 'child' ? 'child' : 'parent'));
  });

  $$('[data-account-type-cancel]', registerForm).forEach(button => {
    button.addEventListener('click', () => {
      closeChooser();
      activateAuthTab('login');
    });
  });

  if (back) back.addEventListener('click', () => showChooser(true));

  document.querySelectorAll('[data-tab="register"]').forEach(tab => {
    tab.addEventListener('click', () => {
      if (accountInput && accountInput.value) return;
      showChooser(true);
    });
  });

  document.addEventListener('keydown', event => {
    if (!promptIsOpen()) return;
    if (event.key === 'Escape') {
      event.preventDefault();
      closeChooser();
      activateAuthTab('login');
      return;
    }
    if (event.key !== 'Tab' || !chooser) return;
    const focusable = $$('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])', chooser).filter(el => !el.disabled && el.offsetParent !== null);
    if (!focusable.length) return;
    const first = focusable[0];
    const last = focusable[focusable.length - 1];
    if (event.shiftKey && document.activeElement === first) {
      event.preventDefault();
      last.focus();
    } else if (!event.shiftKey && document.activeElement === last) {
      event.preventDefault();
      first.focus();
    }
  });

  registerForm.addEventListener('submit', event => {
    if (!accountInput || accountInput.value) return;
    event.preventDefault();
    activateAuthTab('register');
    showChooser(true);
  });

  closeChooser();
  if (fields) fields.hidden = true;
  if (accountInput) accountInput.value = '';
})();

// v9.5 responsive runtime guard: prevent phones/tablets from keeping the desktop layout.
(() => {
  const root = document.documentElement;
  const isTouchMobileCandidate = () => {
    const width = window.visualViewport ? window.visualViewport.width : window.innerWidth;
    const coarse = window.matchMedia('(hover: none) and (pointer: coarse)').matches;
    return width <= 1180 || (coarse && width <= 1366);
  };
  const applyRuntimeView = () => {
    root.setAttribute('data-runtime-view', isTouchMobileCandidate() ? 'mobile' : 'desktop');
  };
  applyRuntimeView();
  window.addEventListener('resize', applyRuntimeView, { passive: true });
  window.addEventListener('orientationchange', () => setTimeout(applyRuntimeView, 180), { passive: true });
  if (window.visualViewport) window.visualViewport.addEventListener('resize', applyRuntimeView, { passive: true });
})();
