/* global React, Icon, IconBtn, db, useLiveQuery, useKvRecord, usePinned, togglePin, relTime, useSendTo */
const { useState, useEffect, useMemo, useCallback, useRef } = React;

// ============================================================
// Widget catalog — { id, label, icon, render(props) }
// ============================================================
const WIDGET_CATALOG = [
  { id: 'stats',         label: 'Overview',       icon: 'insights',        size: 'wide',   desc: 'Counts across every tool' },
  { id: 'quick-todo',    label: 'Quick capture',  icon: 'flash_on',        size: 'wide',   desc: 'Add a todo or note from anywhere' },
  { id: 'recent-todos',  label: 'Open tasks',     icon: 'check_box',       size: 'normal', desc: 'Your open todos by priority', requires: 'todo' },
  { id: 'recent-notes',  label: 'Recent notes',   icon: 'description',     size: 'normal', desc: 'The 5 notes you touched last', requires: 'note' },
  { id: 'bookmarks',     label: 'Bookmarks',      icon: 'bookmark',        size: 'normal', desc: 'Your most-used links', requires: 'bookmark' },
  { id: 'recent-json',   label: 'JSON docs',      icon: 'data_object',     size: 'normal', desc: 'Recent JSON documents', requires: 'json' },
  { id: 'password-gen',  label: 'Password',       icon: 'key',             size: 'normal', desc: 'Generate a strong password', requires: 'password' },
  { id: 'totp-codes',    label: '2FA codes',      icon: 'lock_clock',      size: 'normal', desc: 'Live one-time codes from your saved keys', requires: 'totp' },
  { id: 'flash-review',  label: 'Flash review',   icon: 'style',           size: 'wide',   desc: 'Mini flashcard reviewer for due cards', requires: 'flashcard' },
  { id: 'pomodoro',      label: 'Focus timer',    icon: 'timer',           size: 'normal', desc: 'Current Pomodoro state · resume or start', requires: 'pomodoro' },
  { id: 'time-tracker',  label: 'Time tracker',   icon: 'timelapse',       size: 'normal', desc: 'Running timer + today’s tracked total', requires: 'timetracker' },
  { id: 'habits-today',  label: 'Today\'s habits',icon: 'check_circle',    size: 'wide',   desc: 'Tap to check off today\u2019s habits', requires: 'habits' },
  { id: 'mood-today',    label: 'Mood check-in',  icon: 'mood',            size: 'normal', desc: 'One-tap mood log for today', requires: 'mood' },
  { id: 'reading-now',   label: 'Reading now',    icon: 'auto_stories',    size: 'normal', desc: 'Books in progress with progress bars', requires: 'reading' },
  { id: 'world-clock',   label: 'World clock',    icon: 'public',          size: 'wide',   desc: 'A quick glance at your saved zones', requires: 'worldclock' },
  { id: 'ambient',       label: 'Ambient',        icon: 'graphic_eq',      size: 'normal', desc: 'Open a soundscape with one tap', requires: 'ambient' },
];
const WIDGET_BY_ID = Object.fromEntries(WIDGET_CATALOG.map(w => [w.id, w]));
const DEFAULT_WIDGETS = ['stats', 'quick-todo', 'recent-todos', 'recent-notes', 'bookmarks', 'time-tracker'];

// ============================================================
// Home page
// ============================================================
function HomeTool() {
  const { send, setTool, isToolEnabled } = useToolNav();
  const pinned = usePinned();
  const all = useLiveQuery(['todos', 'notes', 'jsonDocs', 'bookmarks', 'passwords', 'totpKeys', 'flashcardCollections', 'flashcardCards']) || {};
  const [widgets, setWidgets] = useState(DEFAULT_WIDGETS);
  const [editing, setEditing] = useState(false);
  const [loaded, setLoaded] = useState(false);
  const [appTitle, setAppTitle] = useState('Toolio');

  useEffect(() => {
    Promise.all([db.kv.get('homeWidgets'), db.kv.get('settings')]).then(([w, s]) => {
      if (Array.isArray(w?.v)) setWidgets(w.v);
      if (s?.v?.appTitle) setAppTitle(s.v.appTitle);
      setLoaded(true);
    });
    const unsub = db.subscribe(({ store }) => {
      if (store === 'kv') {
        db.kv.get('homeWidgets').then(w => { if (Array.isArray(w?.v)) setWidgets(w.v); });
        db.kv.get('settings').then(s => { if (s?.v?.appTitle) setAppTitle(s.v.appTitle); });
      }
    });
    return unsub;
  }, []);

  const saveWidgets = (next) => {
    setWidgets(next);
    db.kv.put({ k: 'homeWidgets', v: next });
  };
  const addWidget = (id) => { if (!widgets.includes(id)) saveWidgets([...widgets, id]); };
  const removeWidget = (id) => saveWidgets(widgets.filter(w => w !== id));
  const moveWidget = (id, dir) => {
    const i = widgets.indexOf(id);
    if (i < 0) return;
    const j = i + dir;
    if (j < 0 || j >= widgets.length) return;
    const next = [...widgets];
    [next[i], next[j]] = [next[j], next[i]];
    saveWidgets(next);
  };

  const greeting = useMemo(() => {
    const h = new Date().getHours();
    if (h < 5)  return 'Working late';
    if (h < 12) return 'Good morning';
    if (h < 17) return 'Good afternoon';
    if (h < 21) return 'Good evening';
    return 'Good night';
  }, []);

  // Resolve pinned objects (and filter by enabled tools)
  const resolvePinned = useCallback((p) => {
    if (!isToolEnabled(p.type)) return null;
    const stores = { todo: all.todos, note: all.notes, json: all.jsonDocs, bookmark: all.bookmarks, password: all.passwords, flashcard: all.flashcardCollections };
    const list = stores[p.type] || [];
    const item = list.find(x => x.id === p.id);
    if (!item) return null;
    // Flashcard pins also get a due count so the home card can show "X due"
    if (p.type === 'flashcard' && window.flashcards) {
      const cards = (all.flashcardCards || []).filter(c => c.collectionId === item.id);
      const dueCount = cards.filter(c => window.flashcards.isDue(c)).length;
      return { ...p, item, extra: { total: cards.length, due: dueCount } };
    }
    return { ...p, item };
  }, [all, isToolEnabled]);
  const pinnedRows = pinned.map(resolvePinned).filter(Boolean);

  if (!loaded) return <div className="home-tool home-loading" />;

  const widgetCtx = { all, send, setTool, isToolEnabled };

  return (
    <div className="home-tool">
      <header className="home-hero">
        <div className="home-hero-head">
          <div>
            <div className="home-greeting">{greeting} 👋</div>
            <h1 className="home-title">Welcome to <span>{appTitle}</span></h1>
            <p className="home-subtitle">Your local-first command center. Everything stays in this browser.</p>
          </div>
          <button
            className={`lh-btn ${editing ? 'primary' : 'ghost'}`}
            onClick={() => setEditing(e => !e)}
          >
            <Icon name={editing ? 'check' : 'tune'} />
            {editing ? 'Done editing' : 'Edit widgets'}
          </button>
        </div>
      </header>

      {/* PINNED */}
      <section className="home-section">
        <div className="home-section-head">
          <Icon name="push_pin" />
          <h2>Pinned</h2>
          <span className="home-section-count">{pinnedRows.length}</span>
          <div style={{ flex: 1 }} />
          <span className="home-section-hint">Pin items from any tool with the pin button</span>
        </div>
        {pinnedRows.length === 0 ? (
          <div className="home-empty-pin">
            <Icon name="push_pin" />
            <div>
              <div className="title">Nothing pinned yet</div>
              <div className="sub">Hover any todo, note, JSON doc, bookmark or password and click the pin to bring it here for one-click access.</div>
            </div>
          </div>
        ) : (
          <div className="home-pinned-grid">
            {pinnedRows.map(p => (
              <PinnedCard key={p.type + '_' + p.id} pin={p} onOpen={() => setTool(p.type)} />
            ))}
          </div>
        )}
      </section>

      {/* WIDGETS */}
      <section className="home-section">
        <div className="home-section-head">
          <Icon name="widgets" />
          <h2>Widgets</h2>
          <span className="home-section-count">{widgets.length}</span>
        </div>

        {editing && (
          <div className="home-widget-picker">
            <div className="home-widget-picker-label">Add widget</div>
            <div className="home-widget-picker-grid">
              {WIDGET_CATALOG.filter(w => !w.requires || isToolEnabled(w.requires)).map(w => {
                const on = widgets.includes(w.id);
                return (
                  <button
                    key={w.id}
                    className={`home-widget-chip ${on ? 'is-on' : ''}`}
                    onClick={() => on ? removeWidget(w.id) : addWidget(w.id)}
                  >
                    <Icon name={w.icon} />
                    <div className="home-widget-chip-body">
                      <div className="home-widget-chip-label">{w.label}</div>
                      <div className="home-widget-chip-desc">{w.desc}</div>
                    </div>
                    <Icon name={on ? 'check_circle' : 'add_circle'} className="home-widget-chip-flag" />
                  </button>
                );
              })}
            </div>
          </div>
        )}

        <div className="home-widget-grid">
          {widgets.map((id, i) => {
            const w = WIDGET_BY_ID[id];
            if (!w) return null;
            if (w.requires && !isToolEnabled(w.requires)) return null;
            return (
              <div key={id} className={`home-widget size-${w.size}`}>
                <div className="home-widget-head">
                  <Icon name={w.icon} />
                  <h3>{w.label}</h3>
                  <div style={{ flex: 1 }} />
                  {!editing && <WidgetHead id={id} />}
                  {editing && (
                    <>
                      <IconBtn name="arrow_upward" onClick={() => moveWidget(id, -1)} title="Move up" />
                      <IconBtn name="arrow_downward" onClick={() => moveWidget(id, 1)} title="Move down" />
                      <IconBtn name="close" onClick={() => removeWidget(id)} title="Remove widget" />
                    </>
                  )}
                </div>
                <div className="home-widget-body">
                  <WidgetBody id={id} ctx={widgetCtx} />
                </div>
              </div>
            );
          })}
          {widgets.length === 0 && (
            <div className="home-empty-widgets">
              <Icon name="widgets" />
              <div className="title">No widgets yet</div>
              <div className="sub">Click <strong>Edit widgets</strong> to add some.</div>
            </div>
          )}
        </div>
      </section>

      <SiteFooter />
    </div>
  );
}

// ============================================================
// Tool nav helper — uses parent App's setTool via SendTo
// ============================================================
function useToolNav() {
  const { send, isToolEnabled } = useSendTo();
  // We piggyback on send, which navigates to a tool, but ignore payload.
  const setTool = (t) => send(t, null);
  return { send, setTool, isToolEnabled: isToolEnabled || (() => true) };
}

// ============================================================
// Pinned card — renders any pinned item compactly
// ============================================================
function PinnedCard({ pin, onOpen }) {
  const { item, type } = pin;
  const meta = TYPE_META[type] || { icon: 'push_pin', label: type };
  let title = '', sub = '';
  if (type === 'todo')     { title = item.title; sub = item.done ? '✓ done' : (item.due ? relTime(item.due) : (item.project || 'Open')); }
  else if (type === 'note'){ title = item.title || 'Untitled'; sub = (item.body || '').replace(/[#*_`>]/g,'').slice(0, 80); }
  else if (type === 'json'){ title = item.title || 'Untitled'; sub = `${(item.body || '').length} chars · ${relTime(item.updatedAt)}`; }
  else if (type === 'bookmark') { title = item.title; sub = item.url.replace(/^https?:\/\//, ''); }
  else if (type === 'password') { title = item.label; sub = '•'.repeat(12); }
  else if (type === 'flashcard') {
    title = item.name || 'Untitled collection';
    const due = pin.extra?.due ?? 0;
    const total = pin.extra?.total ?? 0;
    sub = due > 0 ? `${due} due · ${total} card${total === 1 ? '' : 's'}` : `${total} card${total === 1 ? '' : 's'}`;
  }

  const handleClick = (e) => {
    if (type === 'bookmark') { window.open(item.url, '_blank'); return; }
    onOpen();
  };

  return (
    <div className="pinned-card" onClick={handleClick}>
      <div className="pinned-card-icon" data-type={type}>
        <Icon name={meta.icon} />
      </div>
      <div className="pinned-card-body">
        <div className="pinned-card-type">{meta.label}</div>
        <div className="pinned-card-title">{title || 'Untitled'}</div>
        {sub && <div className="pinned-card-sub">{sub}</div>}
      </div>
      <button
        className="pinned-card-unpin"
        title="Unpin"
        onClick={(e) => { e.stopPropagation(); togglePin(type, item.id); }}
      >
        <Icon name="close" />
      </button>
    </div>
  );
}

const TYPE_META = {
  todo:     { icon: 'check_box',    label: 'Todo' },
  note:     { icon: 'description',  label: 'Note' },
  json:     { icon: 'data_object',  label: 'JSON' },
  bookmark: { icon: 'bookmark',     label: 'Bookmark' },
  password: { icon: 'key',          label: 'Password' },
  flashcard:{ icon: 'style',         label: 'Flashcards' },
};

// ============================================================
// Widget renderers
// ============================================================
function WidgetBody({ id, ctx }) {
  switch (id) {
    case 'stats':         return <WStats ctx={ctx} />;
    case 'quick-todo':    return <WQuickCapture ctx={ctx} />;
    case 'recent-todos':  return <WRecentTodos ctx={ctx} />;
    case 'recent-notes':  return <WRecentNotes ctx={ctx} />;
    case 'bookmarks':     return <WBookmarks ctx={ctx} />;
    case 'recent-json':   return <WRecentJson ctx={ctx} />;
    case 'password-gen':  return <WPasswordGen />;
    case 'totp-codes':    return <WTotpCodes ctx={ctx} />;
    case 'flash-review':  return <WFlashReview ctx={ctx} />;
    case 'pomodoro':      return <WPomodoro ctx={ctx} />;
    case 'time-tracker':  {
      const TtWidget = window.TimeTrackerWidget;
      return TtWidget ? <TtWidget ctx={ctx} /> : <div className="home-widget-empty">Widget unavailable</div>;
    }
    case 'habits-today':  return <WHabitsToday ctx={ctx} />;
    case 'mood-today':    return <WMoodToday ctx={ctx} />;
    case 'reading-now':   return <WReadingNow ctx={ctx} />;
    case 'world-clock':   return <WWorldClock ctx={ctx} />;
    case 'ambient':       return <WAmbient ctx={ctx} />;
    default:              return <div className="home-widget-empty">Widget unavailable</div>;
  }
}

function WStats({ ctx }) {
  const { all, setTool, isToolEnabled } = ctx;
  const openTodos = (all.todos || []).filter(t => !t.done).length;
  const overdue = (all.todos || []).filter(t => !t.done && t.due && t.due < Date.now()).length;
  const allStats = [
    { label: 'Open todos', value: openTodos, sub: overdue ? `${overdue} overdue` : 'on track', tool: 'todo' },
    { label: 'Notes',     value: (all.notes || []).length, sub: 'in vault', tool: 'note' },
    { label: 'JSON',      value: (all.jsonDocs || []).length, sub: 'documents', tool: 'json' },
    { label: 'Bookmarks', value: (all.bookmarks || []).length, sub: 'links', tool: 'bookmark' },
    { label: 'Passwords', value: (all.passwords || []).length, sub: 'in vault', tool: 'password' },
  ];
  const stats = allStats.filter(s => isToolEnabled(s.tool));
  return (
    <div className="w-stats">
      {stats.map(s => (
        <button key={s.label} className="w-stat" onClick={() => setTool(s.tool)}>
          <div className="w-stat-value">{s.value}</div>
          <div className="w-stat-label">{s.label}</div>
          <div className="w-stat-sub">{s.sub}</div>
        </button>
      ))}
    </div>
  );
}

function WQuickCapture({ ctx }) {
  const { send, setTool, isToolEnabled } = ctx;
  const allModes = [
    ['todo','Todo','check_box'],
    ['note','Note','description'],
    ['bookmark','Bookmark','bookmark'],
  ].filter(([k]) => isToolEnabled(k));
  const [mode, setMode] = useState(allModes[0]?.[0] || 'todo');
  useEffect(() => {
    if (!allModes.some(m => m[0] === mode)) setMode(allModes[0]?.[0] || 'todo');
  }, [allModes, mode]);
  const [text, setText] = useState('');
  const submit = async () => {
    if (!text.trim()) return;
    if (mode === 'todo') {
      await db.todos.put({ title: text.trim(), done: false });
    } else if (mode === 'note') {
      await db.notes.put({ title: text.trim().split('\n')[0].slice(0, 60) || 'Untitled', body: text.trim() });
    } else if (mode === 'bookmark') {
      let url = text.trim();
      if (!/^https?:\/\//i.test(url)) url = 'https://' + url;
      await db.bookmarks.put({ url, title: url.replace(/^https?:\/\//, '').replace(/\/$/, ''), tags: [] });
    }
    setText('');
  };
  return (
    <div className="w-qc">
      <div className="w-qc-tabs">
        {allModes.map(([k,l,ic]) => (
          <button key={k} className={`w-qc-tab ${mode === k ? 'is-active' : ''}`} onClick={() => setMode(k)}>
            <Icon name={ic} />{l}
          </button>
        ))}
      </div>
      <div className="w-qc-input">
        {mode === 'note' ? (
          <textarea
            className="lh-input"
            rows={3}
            placeholder="Jot a note…"
            value={text}
            onChange={e => setText(e.target.value)}
            onKeyDown={e => { if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) submit(); }}
          />
        ) : (
          <input
            className="lh-input"
            placeholder={mode === 'todo' ? 'Add a task… (Enter to save)' : 'Paste a URL…'}
            value={text}
            onChange={e => setText(e.target.value)}
            onKeyDown={e => { if (e.key === 'Enter') submit(); }}
          />
        )}
        <button className="lh-btn primary" onClick={submit} disabled={!text.trim()}>
          <Icon name="add" />Save
        </button>
      </div>
    </div>
  );
}

function WRecentTodos({ ctx }) {
  const { all, send } = ctx;
  const items = (all.todos || []).filter(t => !t.done).slice(0, 6);
  if (items.length === 0) return <EmptyMini icon="check_box" label="All clear!" sub="No open todos." />;
  return (
    <div className="w-list">
      {items.map(t => (
        <button key={t.id} className="w-list-row" onClick={() => send('todo', { selectId: t.id })}>
          <span className={`todo-prio prio-${t.priority || 'low'}`} />
          <div className="w-list-row-body">
            <div className="w-list-row-title">{t.title}</div>
            <div className="w-list-row-meta">
              {t.due && <span>{relTime(t.due)}</span>}
              {t.project && <span>· {t.project}</span>}
            </div>
          </div>
        </button>
      ))}
    </div>
  );
}

function WRecentNotes({ ctx }) {
  const { all, send } = ctx;
  const items = [...(all.notes || [])].sort((a,b) => (b.updatedAt||0) - (a.updatedAt||0)).slice(0, 5);
  if (items.length === 0) return <EmptyMini icon="description" label="No notes yet" />;
  return (
    <div className="w-list">
      {items.map(n => (
        <button key={n.id} className="w-list-row" onClick={() => send('note', { selectId: n.id })}>
          <Icon name="description" />
          <div className="w-list-row-body">
            <div className="w-list-row-title">{n.title || 'Untitled'}</div>
            <div className="w-list-row-meta">
              <span>{relTime(n.updatedAt)}</span>
              {n.folder && <span>· {n.folder}</span>}
            </div>
          </div>
        </button>
      ))}
    </div>
  );
}

// ============================================================
// Shared hook — bookmark widget layout (used by both head + body)
// ============================================================
function useBookmarkWidgetLayout() {
  const [layout, setLayout] = useState('grid');
  useEffect(() => {
    let mounted = true;
    const load = () => db.kv.get('homeBookmarkLayout').then(rec => {
      if (mounted && rec?.v) setLayout(rec.v);
    });
    load();
    const unsub = db.subscribe(({ store, value }) => {
      if (store === 'kv' && (!value || value.k === 'homeBookmarkLayout')) load();
    });
    return () => { mounted = false; unsub(); };
  }, []);
  const set = (v) => { setLayout(v); db.kv.put({ k: 'homeBookmarkLayout', v }); };
  return [layout, set];
}

function WBookmarksHead() {
  const [layout, setLayout] = useBookmarkWidgetLayout();
  return (
    <div className="w-bm-toolbar">
      <button className={`w-bm-layout-btn ${layout === 'grid' ? 'is-active' : ''}`} onClick={() => setLayout('grid')} title="Grid"><Icon name="grid_view" /></button>
      <button className={`w-bm-layout-btn ${layout === 'list' ? 'is-active' : ''}`} onClick={() => setLayout('list')} title="List"><Icon name="view_list" /></button>
      <button className={`w-bm-layout-btn ${layout === 'compact' ? 'is-active' : ''}`} onClick={() => setLayout('compact')} title="Compact"><Icon name="view_headline" /></button>
    </div>
  );
}

function WidgetHead({ id }) {
  switch (id) {
    case 'bookmarks': return <WBookmarksHead />;
    default: return null;
  }
}

function WBookmarks({ ctx }) {
  const { all } = ctx;
  const [layout] = useBookmarkWidgetLayout();

  const items = [...(all.bookmarks || [])].slice(0, layout === 'compact' ? 12 : 8);
  if (items.length === 0) return <EmptyMini icon="bookmark" label="No bookmarks" />;

  const fav = (url) => {
    try { return `https://www.google.com/s2/favicons?domain=${new URL(url).hostname}&sz=64`; }
    catch { return ''; }
  };

  return (
    <>
      {layout === 'grid' && (
        <div className="w-bm-grid">
          {items.map(b => (
            <a key={b.id} className="w-bm" href={b.url} target="_blank" rel="noopener">
              <img src={fav(b.url)} alt="" onError={e => { e.target.style.display = 'none'; }} />
              <div className="w-bm-title">{b.title}</div>
            </a>
          ))}
        </div>
      )}
      {layout === 'list' && (
        <div className="w-bm-list">
          {items.map(b => (
            <a key={b.id} className="w-bm-row" href={b.url} target="_blank" rel="noopener">
              <img src={fav(b.url)} alt="" onError={e => { e.target.style.display = 'none'; }} />
              <div className="w-bm-row-body">
                <div className="w-bm-row-title">{b.title}</div>
                <div className="w-bm-row-url">{b.url.replace(/^https?:\/\//, '')}</div>
              </div>
            </a>
          ))}
        </div>
      )}
      {layout === 'compact' && (
        <div className="w-bm-compact">
          {items.map(b => (
            <a key={b.id} className="w-bm-compact-row" href={b.url} target="_blank" rel="noopener">
              <img src={fav(b.url)} alt="" onError={e => { e.target.style.visibility = 'hidden'; }} />
              <span className="w-bm-compact-title">{b.title}</span>
              <span className="w-bm-compact-url">{b.url.replace(/^https?:\/\//, '')}</span>
            </a>
          ))}
        </div>
      )}
    </>
  );
}

function WRecentJson({ ctx }) {
  const { all, send } = ctx;
  const items = [...(all.jsonDocs || [])].sort((a,b) => (b.updatedAt||0) - (a.updatedAt||0)).slice(0, 5);
  if (items.length === 0) return <EmptyMini icon="data_object" label="No JSON docs" />;
  return (
    <div className="w-list">
      {items.map(d => (
        <button key={d.id} className="w-list-row" onClick={() => send('json', { selectId: d.id })}>
          <Icon name="data_object" />
          <div className="w-list-row-body">
            <div className="w-list-row-title">{d.title || 'Untitled'}</div>
            <div className="w-list-row-meta">
              <span>{relTime(d.updatedAt)}</span><span>· {(d.body || '').length} chars</span>
            </div>
          </div>
        </button>
      ))}
    </div>
  );
}

function WPasswordGen() {
  const [pw, setPw] = useState('');
  const [copied, setCopied] = useState(false);
  const gen = () => {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+';
    let out = '';
    const arr = new Uint32Array(20);
    crypto.getRandomValues(arr);
    for (let i = 0; i < 20; i++) out += chars[arr[i] % chars.length];
    setPw(out); setCopied(false);
  };
  useEffect(() => { gen(); }, []);
  const copy = () => { navigator.clipboard.writeText(pw); setCopied(true); setTimeout(() => setCopied(false), 1500); };
  return (
    <div className="w-pw">
      <div className="w-pw-out">{pw}</div>
      <div className="w-pw-actions">
        <button className="lh-btn ghost" onClick={gen}><Icon name="refresh" />New</button>
        <button className="lh-btn primary" onClick={copy}>
          <Icon name={copied ? 'check' : 'content_copy'} />{copied ? 'Copied' : 'Copy'}
        </button>
      </div>
    </div>
  );
}

// 2FA codes — live one-time codes from the saved-key vault.
// Reuses the global base32Decode/totp/maskSecret helpers from totp.jsx.
function WTotpCodes({ ctx }) {
  const { all, setTool } = ctx;
  const keys = (all.totpKeys || []);
  const [nowSec, setNowSec] = useState(Math.floor(Date.now() / 1000));
  const [codes, setCodes] = useState({});
  const [copiedId, setCopiedId] = useState(null);

  useEffect(() => {
    const id = setInterval(() => setNowSec(Math.floor(Date.now() / 1000)), 1000);
    return () => clearInterval(id);
  }, []);

  const windowKey = keys
    .map(k => k.id + ':' + Math.floor(nowSec / (k.period || 30)))
    .join('|');
  useEffect(() => {
    let cancelled = false;
    (async () => {
      const next = {};
      for (const k of keys) {
        const bytes = typeof base32Decode === 'function' ? base32Decode(k.secret) : null;
        if (!bytes) { next[k.id] = { error: true }; continue; }
        try {
          next[k.id] = { code: await totp(bytes, { period: k.period || 30, digits: k.digits || 6 }) };
        } catch (e) { next[k.id] = { error: true }; }
      }
      if (!cancelled) setCodes(next);
    })();
    return () => { cancelled = true; };
    // eslint-disable-next-line
  }, [windowKey]);

  if (keys.length === 0) {
    return (
      <div className="w-totp-empty">
        <EmptyMini icon="lock_clock" label="No saved keys" sub="Save a 2FA key to see live codes here." />
        <button className="lh-btn ghost w-totp-open" onClick={() => setTool('totp')}>
          <Icon name="add" />Add a key
        </button>
      </div>
    );
  }

  const copy = async (k, code) => {
    const ok = typeof copyText === 'function' ? await copyText(code) : (navigator.clipboard.writeText(code), true);
    if (ok) { setCopiedId(k.id); setTimeout(() => setCopiedId(null), 1400); }
  };

  return (
    <div className="w-totp">
      {keys.slice(0, 5).map(k => {
        const period = k.period || 30;
        const remaining = period - (nowSec % period);
        const res = codes[k.id] || {};
        const urgent = remaining <= 5;
        const label = k.label || (typeof maskSecret === 'function' ? maskSecret(k.secret) : '••••') || 'Key';
        const display = res.code && res.code.length === 6
          ? res.code.slice(0, 3) + ' ' + res.code.slice(3)
          : (res.code || '— — —');
        const R = 9, CIRC = 2 * Math.PI * R;
        return (
          <button
            key={k.id}
            className={`w-totp-row ${res.error ? 'is-error' : ''} ${urgent && !res.error ? 'is-urgent' : ''}`}
            onClick={() => res.code && copy(k, res.code)}
            title={res.error ? 'Invalid key' : 'Click to copy'}
          >
            <div className="w-totp-label">{label}</div>
            {res.error ? (
              <span className="w-totp-err"><Icon name="error" />Invalid</span>
            ) : (
              <>
                <span className="w-totp-code">{display}</span>
                <span className="w-totp-ring">
                  <svg viewBox="0 0 24 24" width="24" height="24">
                    <circle className="w-totp-ring-track" cx="12" cy="12" r={R} />
                    <circle
                      className="w-totp-ring-fill" cx="12" cy="12" r={R}
                      strokeDasharray={CIRC}
                      strokeDashoffset={CIRC * (1 - remaining / period)}
                      transform="rotate(-90 12 12)"
                    />
                  </svg>
                  <span className="w-totp-ring-num">{remaining}</span>
                </span>
                <Icon name={copiedId === k.id ? 'check' : 'content_copy'} className="w-totp-copy" />
              </>
            )}
          </button>
        );
      })}
      {keys.length > 5 && (
        <button className="w-totp-more" onClick={() => setTool('totp')}>
          +{keys.length - 5} more · open 2FA codes
        </button>
      )}
    </div>
  );
}

function EmptyMini({ icon, label, sub }) {
  return (
    <div className="w-empty">
      <Icon name={icon} />
      <div className="title">{label}</div>
      {sub && <div className="sub">{sub}</div>}
    </div>
  );
}

// ============================================================
// WFlashReview — mini inline flashcard reviewer
// Pulls due cards across all collections, lets the user review
// one at a time without leaving Home.
// ============================================================
function WFlashReview({ ctx }) {
  const { all, setTool } = ctx;
  const cards = all.flashcardCards || [];
  const collections = all.flashcardCollections || [];
  const fc = window.flashcards;

  // Queue of due cards, captured at mount so a successful review doesn't
  // pull new cards in mid-session. Re-runs whenever the underlying lists
  // change in *length* (new collection/card created), not on every FSRS write.
  const collectionById = useMemo(
    () => Object.fromEntries(collections.map(c => [c.id, c])),
    [collections]
  );
  const [queue, setQueue] = useState([]);
  const [idx, setIdx] = useState(0);
  const [revealed, setRevealed] = useState(false);
  const lengthsRef = useRef('');

  useEffect(() => {
    if (!fc) return;
    const key = `${cards.length}|${collections.length}`;
    if (key === lengthsRef.current) return;
    lengthsRef.current = key;
    const due = cards.filter(c => fc.isDue(c) && collectionById[c.collectionId]);
    // Shuffle so repeats don't feel mechanical
    for (let i = due.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [due[i], due[j]] = [due[j], due[i]];
    }
    setQueue(due.slice(0, 30));
    setIdx(0);
    setRevealed(false);
  }, [cards.length, collections.length, cards, collectionById, fc]);

  if (!fc) return <EmptyMini icon="style" label="Flashcards loading…" />;

  const card = queue[idx];
  const done = !card;

  if (done) {
    if (queue.length === 0) {
      return (
        <div className="w-fc-empty">
          <Icon name="celebration" />
          <div className="title">Nothing due right now</div>
          <div className="sub">Cards reappear here once they're scheduled for review.</div>
          <button className="lh-btn ghost" onClick={() => setTool('flashcard')}>
            <Icon name="style" />Open Flashcards
          </button>
        </div>
      );
    }
    return (
      <div className="w-fc-empty">
        <Icon name="check_circle" />
        <div className="title">Session complete</div>
        <div className="sub">Reviewed {queue.length} card{queue.length === 1 ? '' : 's'}.</div>
        <button className="lh-btn ghost" onClick={() => setTool('flashcard')}>
          <Icon name="style" />Open Flashcards
        </button>
      </div>
    );
  }

  const collection = collectionById[card.collectionId];
  const promptF = collection.schema.find(f => f.id === collection.promptField);
  const answerFs = collection.schema.filter(f => collection.answerFields.includes(f.id));
  const promptVal = card.fields?.[collection.promptField];

  const rate = async (rating) => {
    const next = fc.fsrsReview(card.fsrs, rating, Date.now());
    await db.flashcardCards.put({ ...card, fsrs: next });
    if (rating === 1) {
      // Recycle to back of queue
      setQueue(prev => {
        const out = [...prev];
        const moved = out.splice(idx, 1)[0];
        out.push(moved);
        return out;
      });
      setRevealed(false);
    } else {
      setIdx(i => i + 1);
      setRevealed(false);
    }
  };

  const ratings = [
    { id: 1, label: 'Again', cls: 'r-again' },
    { id: 2, label: 'Hard',  cls: 'r-hard' },
    { id: 3, label: 'Good',  cls: 'r-good' },
    { id: 4, label: 'Easy',  cls: 'r-easy' },
  ];

  return (
    <div className="w-fc">
      <div className="w-fc-meta">
        <button className="w-fc-coll" onClick={() => setTool('flashcard')} title="Open collection">
          <Icon name="style" />{collection.name}
        </button>
        <span className="w-fc-progress"><b>{idx + 1}</b> / {queue.length}</span>
      </div>

      <div className={`w-fc-card ${revealed ? 'is-revealed' : ''}`}>
        <div className="w-fc-side w-fc-prompt-side">
          <div className="w-fc-label">Prompt · {promptF?.name || 'Front'}</div>
          <WFcValue value={promptVal} type={promptF?.type} big />
        </div>
        {revealed && (
          <div className="w-fc-side w-fc-answer-side">
            <div className="w-fc-divider"></div>
            <div className="w-fc-label">Answer</div>
            <div className="w-fc-answer-fields">
              {answerFs.map(f => {
                const v = card.fields?.[f.id];
                if (v == null || v === '' || (Array.isArray(v) && v.length === 0)) return null;
                return (
                  <div key={f.id} className="w-fc-answer-field">
                    <span className="w-fc-answer-label">{f.name}</span>
                    <WFcValue value={v} type={f.type} />
                  </div>
                );
              })}
            </div>
          </div>
        )}
      </div>

      {!revealed ? (
        <button className="w-fc-reveal" onClick={() => setRevealed(true)}>
          <Icon name="visibility" />Show answer
        </button>
      ) : (
        <div className="w-fc-rating">
          {ratings.map(r => (
            <button
              key={r.id}
              className={`w-fc-rate ${r.cls}`}
              onClick={() => rate(r.id)}
              title={`Rate as ${r.label}`}
            >
              <span className="label">{r.label}</span>
              <span className="interval">{fc.fsrsPreviewInterval(card.fsrs, r.id)}</span>
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

function WFcValue({ value, type, big = false }) {
  if (value == null || value === '') return <div className="w-fc-val" style={{ color: 'var(--text-faint)' }}>—</div>;
  if (type === 'image') return <img className="w-fc-val-img" src={value} alt="" />;
  if (type === 'audio') return <audio controls src={value} className="w-fc-val-audio" />;
  if (type === 'tags') {
    const arr = Array.isArray(value) ? value : [value];
    return (
      <div className="w-fc-val w-fc-val-tags">
        {arr.map((t, i) => <span key={i} className="fc-tag-chip">{t}</span>)}
      </div>
    );
  }
  if (type === 'date') {
    const n = Number(value);
    return <div className="w-fc-val">{Number.isFinite(n) ? new Date(n).toLocaleDateString() : String(value)}</div>;
  }
  return <div className={`w-fc-val ${big ? 'is-big' : ''} ${type === 'long' ? 'is-long' : ''}`}>{value}</div>;
}

// ============================================================
// WPomodoro — full focus-session control surface on Home.
// Reads `pomodoroState` + `pomodoroSettings` from kv; writes
// state changes back to kv so the tool resumes seamlessly when
// the user opens it.
//
// We keep one source of truth (kv) — the Pomodoro tool uses the
// same subscription, so widgets and the tool stay in lockstep.
// ============================================================
function WPomodoro({ ctx }) {
  const { setTool } = ctx;
  const state = useKvRecord('pomodoroState');
  const settings = useKvRecord('pomodoroSettings') || {};
  const [now, setNow] = useState(Date.now());
  const [draftTask, setDraftTask] = useState('');

  useEffect(() => {
    if (state?.status !== 'running') return;
    const id = setInterval(() => setNow(Date.now()), 1000);
    return () => clearInterval(id);
  }, [state?.status]);

  // Sync the local draft task when the underlying state changes
  useEffect(() => {
    if (state?.task != null) setDraftTask(state.task);
  }, [state?.task]);

  const phaseLabels = { focus: 'Focus', short: 'Short break', long: 'Long break' };
  const phaseColors = { focus: 'hsl(8, 78%, 58%)', short: 'hsl(178, 60%, 45%)', long: 'hsl(258, 60%, 62%)' };

  const focusMin = settings.focusMin ?? 25;
  const shortMin = settings.shortMin ?? 5;
  const longMin  = settings.longMin  ?? 15;
  const durationFor = (phase) => (
    phase === 'focus' ? focusMin :
    phase === 'short' ? shortMin :
    longMin
  ) * 60 * 1000;

  const phase = state?.phase || 'focus';
  const status = state?.status || 'idle';

  // ---- Helpers to write state back ----
  const writeState = (patch) => {
    const base = state || { phase: 'focus', status: 'idle', phaseEndsAt: null, remainingMs: null, cycleCount: 0, task: '' };
    db.kv.put({ k: 'pomodoroState', v: { ...base, ...patch } });
  };

  const start = () => {
    writeState({
      status: 'running',
      task: draftTask,
      phaseEndsAt: Date.now() + durationFor(phase),
      remainingMs: null,
    });
  };
  const resume = () => {
    const ms = state?.remainingMs ?? durationFor(phase);
    writeState({
      status: 'running',
      phaseEndsAt: Date.now() + ms,
      remainingMs: null,
    });
  };
  const pause = () => {
    const remaining = Math.max(0, (state?.phaseEndsAt || 0) - Date.now());
    writeState({
      status: 'paused',
      phaseEndsAt: null,
      remainingMs: remaining,
    });
  };
  const reset = () => {
    writeState({ status: 'idle', phaseEndsAt: null, remainingMs: null });
  };

  // ---- Computed display ----
  const remaining = status === 'running'
    ? Math.max(0, (state.phaseEndsAt || 0) - now)
    : status === 'paused'
      ? (state?.remainingMs ?? durationFor(phase))
      : durationFor(phase);
  const mm = Math.floor(remaining / 60000);
  const ss = Math.floor((remaining % 60000) / 1000);
  const color = phaseColors[phase];

  // Progress fraction for the ring
  const totalMs = durationFor(phase);
  const elapsedFrac = totalMs > 0 ? Math.min(1, 1 - remaining / totalMs) : 0;

  return (
    <div className={`w-pomo-v2 status-${status}`}>
      <div className="w-pomo-header">
        <div className="w-pomo-phase-pill" style={{ color, borderColor: `color-mix(in oklab, ${color} 35%, transparent)`, background: `color-mix(in oklab, ${color} 8%, transparent)` }}>
          <span className="w-pomo-phase-dot" style={{ background: color, boxShadow: status === 'running' ? `0 0 10px ${color}` : 'none' }} />
          <span>{phaseLabels[phase]}</span>
        </div>
        {status !== 'idle' && state?.task && (
          <div className="w-pomo-task-line" title={state.task}>{state.task}</div>
        )}
      </div>

      <div className="w-pomo-stage">
        <svg className="w-pomo-ring" viewBox="0 0 100 100" width="120" height="120">
          <circle cx="50" cy="50" r="44" className="w-pomo-ring-track" />
          <circle
            cx="50" cy="50" r="44"
            className="w-pomo-ring-fill"
            stroke={color}
            strokeDasharray={2 * Math.PI * 44}
            strokeDashoffset={(2 * Math.PI * 44) * (1 - elapsedFrac)}
            transform="rotate(-90 50 50)"
          />
        </svg>
        <div className="w-pomo-clock-v2" style={{ color }}>
          {String(mm).padStart(2, '0')}:{String(ss).padStart(2, '0')}
        </div>
        <div className="w-pomo-of">of {Math.round(totalMs / 60000)} min</div>
      </div>

      <div className="w-pomo-controls">
        {status === 'idle' && (
          <>
            <input
              className="w-pomo-task-input"
              type="text"
              placeholder={phase === 'focus' ? 'What are you focusing on?' : 'Step away from the screen.'}
              value={draftTask}
              onChange={e => setDraftTask(e.target.value)}
              onKeyDown={e => { if (e.key === 'Enter') start(); }}
              disabled={phase !== 'focus'}
              maxLength={120}
            />
            <button className="lh-btn primary" onClick={start}>
              <Icon name="play_arrow" />Start
            </button>
          </>
        )}
        {status === 'running' && (
          <>
            <button className="lh-btn primary" onClick={pause}>
              <Icon name="pause" />Pause
            </button>
            <button className="lh-btn ghost" onClick={() => setTool('pomodoro')} title="Open tool">
              <Icon name="open_in_new" />
            </button>
          </>
        )}
        {status === 'paused' && (
          <>
            <button className="lh-btn primary" onClick={resume}>
              <Icon name="play_arrow" />Resume
            </button>
            <button className="lh-btn ghost" onClick={reset}>
              <Icon name="refresh" />Reset
            </button>
            <button className="lh-btn ghost" onClick={() => setTool('pomodoro')} title="Open tool">
              <Icon name="open_in_new" />
            </button>
          </>
        )}
      </div>
    </div>
  );
}

// ============================================================
// WHabitsToday — today's habits with one-tap toggle
// ============================================================
function WHabitsToday({ ctx }) {
  const { setTool } = ctx;
  const state = useKvRecord('habitTracker');
  const habits = (state?.habits || []).filter(h => !h.archived);
  const completions = state?.completions || {};
  const today = new Date();
  const todayKey = `${today.getFullYear()}-${String(today.getMonth()+1).padStart(2,'0')}-${String(today.getDate()).padStart(2,'0')}`;
  const toggle = (habitId) => {
    const cur = completions[habitId] || {};
    const next = { ...cur };
    if (next[todayKey]) delete next[todayKey];
    else next[todayKey] = true;
    db.kv.put({ k: 'habitTracker', v: { ...(state || {}), habits: state?.habits || [], completions: { ...completions, [habitId]: next } } });
  };
  if (habits.length === 0) {
    return (
      <EmptyMini icon="check_circle" label="No habits yet"
        sub={<button className="lh-btn ghost" onClick={() => setTool('habits')}><Icon name="add" />Add a habit</button>} />
    );
  }
  return (
    <div className="w-habits">
      {habits.map(h => {
        const done = !!completions[h.id]?.[todayKey];
        return (
          <button
            key={h.id}
            className={`w-habit-chip ${done ? 'is-done' : ''}`}
            style={{ '--habit-color': h.color }}
            onClick={() => toggle(h.id)}
            title={done ? `Undo "${h.name}"` : `Mark "${h.name}" done`}
          >
            <Icon name={done ? 'check_circle' : (h.icon || 'circle')} />
            <span>{h.name}</span>
          </button>
        );
      })}
    </div>
  );
}

// ============================================================
// WMoodToday — 5-emoji mood check-in for today
// ============================================================
function WMoodToday({ ctx }) {
  const { setTool } = ctx;
  const state = useKvRecord('moodTracker');
  const today = new Date();
  const todayKey = `${today.getFullYear()}-${String(today.getMonth()+1).padStart(2,'0')}-${String(today.getDate()).padStart(2,'0')}`;
  const cur = state?.entries?.[todayKey];

  const SCALE = [
    { val: 1, emoji: '😞', label: 'Awful', color: 'hsl(8, 78%, 58%)' },
    { val: 2, emoji: '😕', label: 'Low',   color: 'hsl(28, 80%, 60%)' },
    { val: 3, emoji: '😐', label: 'Okay',  color: 'hsl(45, 70%, 55%)' },
    { val: 4, emoji: '🙂', label: 'Good',  color: 'hsl(155, 55%, 50%)' },
    { val: 5, emoji: '😄', label: 'Great', color: 'hsl(168, 65%, 48%)' },
  ];
  const setMood = (val) => {
    const next = { entries: { ...(state?.entries || {}), [todayKey]: { ...(cur || { note: '', tags: [] }), mood: val, ts: Date.now() } } };
    db.kv.put({ k: 'moodTracker', v: next });
  };

  return (
    <div className="w-mood">
      <div className="w-mood-prompt">
        {cur?.mood
          ? <>Today you felt <strong style={{ color: SCALE[cur.mood - 1].color }}>{SCALE[cur.mood - 1].label}</strong>.</>
          : <>How are you today?</>}
      </div>
      <div className="w-mood-row">
        {SCALE.map(m => (
          <button
            key={m.val}
            className={`w-mood-btn ${cur?.mood === m.val ? 'is-active' : ''}`}
            onClick={() => setMood(m.val)}
            title={m.label}
            style={cur?.mood === m.val ? { '--mood-c': m.color } : {}}
          >{m.emoji}</button>
        ))}
      </div>
      {cur?.mood && (
        <button className="w-mood-open" onClick={() => setTool('mood')}>
          <Icon name="open_in_new" />Add a note
        </button>
      )}
    </div>
  );
}

// ============================================================
// WReadingNow — books currently being read
// ============================================================
function WReadingNow({ ctx }) {
  const { setTool } = ctx;
  const state = useKvRecord('readingTracker');
  const reading = (state?.books || []).filter(b => b.status === 'reading').slice(0, 4);
  if (reading.length === 0) {
    return (
      <EmptyMini icon="auto_stories" label="No books in progress"
        sub={<button className="lh-btn ghost" onClick={() => setTool('reading')}><Icon name="add" />Add a book</button>} />
    );
  }
  return (
    <div className="w-list">
      {reading.map(b => (
        <button key={b.id} className="w-list-row" onClick={() => setTool('reading')}>
          <Icon name="menu_book" />
          <div className="w-list-row-body">
            <div className="w-list-row-title">{b.title}</div>
            <div className="w-list-row-meta">
              <span>{b.author || 'Unknown author'}</span>
              {b.progress > 0 && <span>· {b.progress}%</span>}
            </div>
          </div>
        </button>
      ))}
    </div>
  );
}

// ============================================================
// WWorldClock — compact clocks for the user's saved zones.
// Includes a per-widget picker so users can focus on a subset.
// Persisted to kv as `homeWorldClockSelection` (null/empty = show all).
// ============================================================
function WWorldClock({ ctx }) {
  const { setTool } = ctx;
  const zones = useKvRecord('worldClockZones') || ['America/Los_Angeles', 'America/New_York', 'Europe/London', 'Asia/Tokyo'];
  const selection = useKvRecord('homeWorldClockSelection');
  const prefs = useKvRecord('worldClockPrefs') || {};
  const hour24 = prefs.hour24 ?? true;
  const [picking, setPicking] = useState(false);
  const [now, setNow] = useState(Date.now());

  useEffect(() => {
    const id = setInterval(() => setNow(Date.now()), 30000);
    return () => clearInterval(id);
  }, []);

  const visible = useMemo(() => {
    if (!Array.isArray(zones)) return [];
    if (!selection || selection.length === 0) return zones;
    return zones.filter(z => selection.includes(z));
  }, [zones, selection]);

  const toggleZone = (zone) => {
    const cur = (Array.isArray(selection) && selection.length > 0) ? selection : zones.slice();
    const next = cur.includes(zone) ? cur.filter(z => z !== zone) : [...cur, zone];
    // If the user has selected ALL zones, store null so we keep tracking
    // future additions to the master list automatically.
    db.kv.put({ k: 'homeWorldClockSelection', v: next.length === zones.length ? null : next });
  };

  if (!Array.isArray(zones) || zones.length === 0) {
    return (
      <EmptyMini icon="public" label="No cities saved"
        sub={<button className="lh-btn ghost" onClick={() => setTool('worldclock')}><Icon name="add" />Add a city</button>} />
    );
  }

  return (
    <div className="w-clocks-v2">
      <div className="w-clocks-toolbar">
        <span className="w-clocks-caption">
          Showing <strong>{visible.length}</strong> of {zones.length} {visible.length === 1 ? 'city' : 'cities'}
        </span>
        <button
          className={`w-clocks-tool-btn ${picking ? 'is-active' : ''}`}
          onClick={() => setPicking(p => !p)}
          title="Pick which cities to show"
        >
          <Icon name="tune" />
        </button>
        <button
          className="w-clocks-tool-btn"
          onClick={() => setTool('worldclock')}
          title="Open World clock"
        >
          <Icon name="open_in_new" />
        </button>
      </div>

      {picking && (
        <div className="w-clocks-picker">
          {zones.map(z => {
            const parts = z.split('/');
            const cityName = parts[parts.length - 1].replace(/_/g, ' ');
            const on = !selection || selection.length === 0 || selection.includes(z);
            return (
              <button
                key={z}
                className={`w-clocks-picker-chip ${on ? 'is-on' : ''}`}
                onClick={() => toggleZone(z)}
              >
                <Icon name={on ? 'check_box' : 'check_box_outline_blank'} />
                <span>{cityName}</span>
              </button>
            );
          })}
        </div>
      )}

      {visible.length === 0 ? (
        <div className="w-clocks-empty">No cities selected. Add some above.</div>
      ) : (
        <div className={`w-clocks-grid count-${Math.min(visible.length, 4)}`}>
          {visible.map(z => (
            <ClockTile key={z} zone={z} hour24={hour24} now={now} />
          ))}
        </div>
      )}
    </div>
  );
}

// Minutes east of UTC for a zone at a given Date (constant per zone, used to
// shift the continuous hand angles). Local copy — worldclock.jsx has its own.
function zoneOffsetMin(zone, date) {
  const fmt = new Intl.DateTimeFormat('en-US', {
    timeZone: zone,
    year: 'numeric', month: '2-digit', day: '2-digit',
    hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false,
  });
  const parts = fmt.formatToParts(date);
  const get = (t) => parts.find(p => p.type === t)?.value || '00';
  const asUTC = Date.UTC(+get('year'), +get('month') - 1, +get('day'), +get('hour') % 24, +get('minute'), +get('second'));
  return Math.round((asUTC - date.getTime()) / 60000);
}

// One clock tile: mini analog + city name + digital time + day label
function ClockTile({ zone, hour24, now }) {
  const parts = zone.split('/');
  const cityName = parts[parts.length - 1].replace(/_/g, ' ');

  // Digital parts (respects hour24 pref)
  const digital = useMemo(() => {
    const fmt = new Intl.DateTimeFormat('en-US', {
      timeZone: zone,
      hour: 'numeric', minute: '2-digit',
      hour12: !hour24,
      weekday: 'short',
    });
    const tokens = fmt.formatToParts(new Date(now));
    const get = (t) => tokens.find(p => p.type === t)?.value || '';
    return { hour: get('hour'), minute: get('minute'), ampm: get('dayPeriod'), weekday: get('weekday') };
  }, [zone, hour24, now]);

  // Hand angles from CONTINUOUS time → always monotonic, always clockwise.
  const angles = useMemo(() => {
    const offMin = zoneOffsetMin(zone, new Date(now));
    const zonedMs = now + offMin * 60000;
    const totalMin = zonedMs / 60000;
    const totalHrs = totalMin / 60;
    const h24 = Math.floor(((totalHrs % 24) + 24) % 24);
    return {
      hour:   totalHrs * 30,
      minute: totalMin * 6,
      isDay:  h24 >= 6 && h24 < 19,
    };
  }, [zone, now]);

  return (
    <div className={`w-clock-tile ${angles.isDay ? 'is-day' : 'is-night'}`}>
      <svg className="w-clock-svg" viewBox="0 0 40 40" width="48" height="48">
        <circle cx="20" cy="20" r="18.5" className="w-clock-svg-ring" />
        {[0, 3, 6, 9].map(i => {
          const a = (i * 30) * Math.PI / 180;
          const x1 = 20 + Math.sin(a) * 15.5, y1 = 20 - Math.cos(a) * 15.5;
          const x2 = 20 + Math.sin(a) * 17.5, y2 = 20 - Math.cos(a) * 17.5;
          return <line key={i} x1={x1} y1={y1} x2={x2} y2={y2} className="w-clock-svg-tick" />;
        })}
        <line x1="20" y1="20" x2="20" y2="10" className="w-clock-svg-hand w-clock-svg-hour"
              transform={`rotate(${angles.hour} 20 20)`} />
        <line x1="20" y1="20" x2="20" y2="5" className="w-clock-svg-hand w-clock-svg-minute"
              transform={`rotate(${angles.minute} 20 20)`} />
        <circle cx="20" cy="20" r="1.6" className="w-clock-svg-cap" />
      </svg>
      <div className="w-clock-tile-meta">
        <div className="w-clock-tile-city">{cityName}</div>
        <div className="w-clock-tile-day">{digital.weekday}</div>
      </div>
      <div className="w-clock-tile-time">
        {digital.hour}:{digital.minute}
        {!hour24 && digital.ampm && <em>{digital.ampm}</em>}
      </div>
    </div>
  );
}

// ============================================================
// WAmbient — quick launch into a soundscape (no audio in widget)
// ============================================================
function WAmbient({ ctx }) {
  const { setTool } = ctx;
  const sounds = [
    { id: 'rain',   label: 'Rain',     icon: 'water_drop',           color: 'hsl(212, 70%, 60%)' },
    { id: 'ocean',  label: 'Ocean',    icon: 'waves',                color: 'hsl(192, 75%, 50%)' },
    { id: 'forest', label: 'Forest',   icon: 'park',                 color: 'hsl(108, 50%, 45%)' },
    { id: 'fire',   label: 'Fire',     icon: 'local_fire_department', color: 'hsl(20, 80%, 55%)' },
  ];
  return (
    <div className="w-ambient">
      <div className="w-ambient-prompt">Open ambient to layer sounds.</div>
      <div className="w-ambient-grid">
        {sounds.map(s => (
          <button key={s.id} className="w-ambient-btn" style={{ '--amb-c': s.color }} onClick={() => setTool('ambient')}>
            <Icon name={s.icon} />
            <span>{s.label}</span>
          </button>
        ))}
      </div>
    </div>
  );
}

Object.assign(window, { HomeTool });
