const { useState, useEffect, useRef, useLayoutEffect } = React;

    // Neutral GGTok chat pools (gifts reuse the default table). Passed to GGTok in neutral mode.
    const NEUTRAL_GGTOK_POOLS = {
      names: [
        "pixel_pat", "prod_pro", "focus_fox", "dreamy_dev", "task_titan", "bubbly_b",
        "calm_coder", "aurora_a", "skylar_blue", "study_buddy", "grind_mode", "starry_night",
        "honey_lulu", "golden_hour", "spark_plug", "marshmallow", "lulu_b", "night_owl_99",
        "velvet", "cotton_c", "peachy_keen", "starlit", "rosie_p", "daisy_d", "alex_xo"
      ],
      compliments: [
        "how are you so productive?", "literally goals ✨", "you're on fire 🔥", "so inspiring ✨",
        "working so hard! 💪", "wish I was this organized", "the focus is unreal ✨",
        "love the calm vibes! 👏", "this layout is so clean 💻", "you got this! 💪",
        "so satisfying to watch 👏", "pure motivation 🔥", "best stream ever ⭐",
        "wait how do you stay so focused?? 👀", "taking notes from this stream fr 📝",
        "this is my study-with-me now 💻", "what app is that?? need it",
        "the way you just power through 🔥", "no thoughts just finishing the list ✨",
        "you make it look easy honestly", "lowkey motivated to do my own tasks now",
        "the consistency >>> 💪", "locked in 🔒✨", "small wins count too 💪",
        "checking things off is so satisfying", "your future self is thanking you ✨"
      ],
      banter: [
        "first!! ⭐", "hi chat 👋", "anyone else here from the fyp?", "good morning everyone ✨",
        "drink some water too! 💧", "don't forget to stretch! 🧘", "what's next on the list?? 👀",
        "we're all rooting for you 💪", "this is so calming honestly", "vibes are immaculate ✨"
      ],
      goodGirl: [
        "you're doing great ⭐", "keep going! ✨", "let's gooo 🔥", "incredible focus today 💪",
        "so proud of you ✨", "getting it done 👏", "yes!! keep it up 💪", "absolute legend ⭐",
        "you're on a roll 🔥", "keep that momentum 👏", "unstoppable today ✨", "clean work ✨",
        "discipline on display 👏", "great pace today 💪", "calm and locked in ✨", "shining today ⭐"
      ],
      completeText: (label) => `✨ JUST FINISHED "${String(label || '').toUpperCase()}" — LET'S GO ✨`,
      // Cool, neutral chat-name colors (blue-grays) instead of the default pinks.
      chatColor: () => `hsl(${205 + Math.floor(Math.random() * 35)}, ${22 + Math.floor(Math.random() * 18)}%, 58%)`,
      gifts: (window.GGTok && window.GGTok.defaultPools && window.GGTok.defaultPools.gifts) || []
    };

    function App() {
      const [tasks, setTasks] = useState([]);
      const [inputValue, setInputValue] = useState('');
      const [searchQuery, setSearchQuery] = useState('');
      const [filterTag, setFilterTag] = useState('');
      const [filterContext, setFilterContext] = useState('');
      const [draggedTaskId, setDraggedTaskId] = useState(null);
      const [view, setView] = useState('inbox');
      const [selectedTask, setSelectedTask] = useState(null); 
      const [dailyHearts, setDailyHearts] = useState(0);
      const [dailyMiniHearts, setDailyMiniHearts] = useState(0);
      const [totalHearts, setTotalHearts] = useState(0); 
      const [kissElo, setKissElo] = useState(1200); 
      const [lastResetDate, setLastResetDate] = useState(new Date().toDateString());
      
      // Unlockable States
      const [unlockedFonts, setUnlockedFonts] = useState(['default']);
      const [activeFont, setActiveFont] = useState('default');
      const [unlockedAnims, setUnlockedAnims] = useState(['float']);
      const [activeAnim, setActiveAnim] = useState('float');
      const [unlockedPraises, setUnlockedPraises] = useState(['default']);
      const [activePraise, setActivePraise] = useState('default');
      const [unlockedThemes, setUnlockedThemes] = useState(['default']);
      const [activeTheme, setActiveTheme] = useState('default');
      const [unlockedIcons, setUnlockedIcons] = useState(['check']);
      const [activeIcon, setActiveIcon] = useState('check');
      const [showArchive, setShowArchive] = useState(false);
      const [confirmReset, setConfirmReset] = useState(false);

      // Streak / profile / gallery states (persisted with stats).
      const [streak, setStreak] = useState(0);
      const [bestStreak, setBestStreak] = useState(0);
      const [lastActiveDate, setLastActiveDate] = useState(null);
      const [daysActive, setDaysActive] = useState(0);
      const [tasksCompleted, setTasksCompleted] = useState(0);
      const [avatarId, setAvatarId] = useState('star');
      const [unlockedBackgrounds, setUnlockedBackgrounds] = useState(['default']);
      const [activeBackground, setActiveBackground] = useState('default');
      const [showGallery, setShowGallery] = useState(false);
      const [showStats, setShowStats] = useState(false);
      const [showGoals, setShowGoals] = useState(false);
      const streakCheckedRef = useRef(false);

      // Capture category + ephemeral celebration / popup states.
      const [captureCategory, setCaptureCategory] = useState(null);
      const [confetti, setConfetti] = useState(null);     // { id }
      const [loginBonus, setLoginBonus] = useState(null);  // { hearts, streak }
      const [affirmation, setAffirmation] = useState(null);// { id, text }
      const [reminder, setReminder] = useState(null);      // { id, text }
      const [particles, setParticles] = useState(null);    // { id, x, y }
      const [booting, setBooting] = useState(true);

      const [tournament, setTournament] = useState({ participants: [], pairs: [], currentPairIndex: 0, round: 1, maxRounds: 4, isTransitioning: false });
      const [rewardAnim, setRewardAnim] = useState(null);
      const [captureAnim, setCaptureAnim] = useState({ active: false, id: 0 });
      const [isGGTok, setIsGGTok] = useState(false);
      // Gender-neutral skin/copy toggle (default OFF = the original girly look).
      const [neutralMode, setNeutralMode] = useState(false);
      // Keep the module-scope category lookup (used by TaskRow) in sync, synchronously.
      window.NEUTRAL_MODE = neutralMode;
      // Active copy sets — swap to neutral wording when the toggle is on.
      const PRAISES = neutralMode ? NEUTRAL_PRAISES : SHOP_PRAISES;
      const COMPLETIONS = neutralMode ? NEUTRAL_COMPLETION : COMPLETION_MESSAGES;
      const AFFIRMS = neutralMode ? NEUTRAL_AFFIRMATIONS : AFFIRMATIONS;
      const REMINDERS = neutralMode ? NEUTRAL_REMINDERS : REMINDER_LINES;
      const CATEGORIES = neutralMode ? NEUTRAL_CATEGORIES : TASK_CATEGORIES;

      // Fake "praise Twitter" feed on the right — tweets nice things about the user, reacts to typing.
      const [tweets, setTweets] = useState([]);
      // Goals + subliminal flasher (task #12/#14): user phrases that flash subliminally on screen.
      const [goals, setGoals] = useState([]);
      const [unlockedGoalSlots, setUnlockedGoalSlots] = useState(2);
      const [subliminal, setSubliminal] = useState(null); // { id, text }

      // Notes (standalone documents) + help overlay
      const [notes, setNotes] = useState([]);
      const [selectedNote, setSelectedNote] = useState(null);
      const [confirmDeleteNote, setConfirmDeleteNote] = useState(null);
      const [showHelp, setShowHelp] = useState(false);

      // Productivity-economy refs (feature 6): track editor focus + typing milestones.
      const editingRef = useRef(false);
      const typedCharsRef = useRef(0);
      const isGGTokRef = useRef(false);

      // Praise words rotate through every UNLOCKED affirmation rather than a single equipped one,
      // so buying a new one simply adds it to the rotation. This ref tracks the rotation position.
      const praiseIndexRef = useRef(0);
      // Cosmetics rotate through every unlocked option (like praise), so buying more = more variety.
      const fontIndexRef = useRef(0);
      const animIndexRef = useRef(0);
      const iconIndexRef = useRef(0);
      // GGTok hearts now track real dollars earned; this carries the sub-$1 remainder between gifts.
      const giftDollarCarryRef = useRef(0);

      useEffect(() => { isGGTokRef.current = isGGTok; }, [isGGTok]);

      useEffect(() => {
        document.title = neutralMode ? "Workspace" : "HeartBox 💖";
      }, [neutralMode]);
      
      const [toastMessage, setToastMessage] = useState(null);

      // PIN State

      // Firebase Sync State
      const [user, setUser] = useState(null);
      const [db, setDb] = useState(null);
      const [fbAPI, setFbAPI] = useState(window.FirebaseAPI);
      const authRef = useRef(null);
      const appId = 'heartbox-private';

      // PIN privacy gate — sits between Google sign-in and the app. The hash is stored
      // in Firestore stats so the same PIN works across devices; pinUnlocked is per-session
      // so the PIN is re-requested every time the app is opened.
      const [pinHash, setPinHash] = useState(null);
      const [pinUnlocked, setPinUnlocked] = useState(false);
      const [statsReady, setStatsReady] = useState(false); // true once the stats doc has loaded (so PIN status is known)
      const [forcePinSetup, setForcePinSetup] = useState(false); // "Change PIN" forces the create flow even if one exists
      // Surfaces Firestore read/write failures loudly. The usual cause of "nothing
      // syncs" is expired/locked security rules → permission-denied on every op.
      const [syncError, setSyncError] = useState(null); // { code, message }
      const reportSyncError = (err, where) => {
        console.error('[ggtodo] Firestore error during ' + where + ':', err && err.code, err && err.message, err);
        setSyncError({ code: (err && err.code) || 'unknown', message: (err && err.message) || String(err), where });
      };
      const hashPin = async (pin, uid) => {
        const data = new TextEncoder().encode(pin + '::ggtodo::' + uid);
        const buf = await crypto.subtle.digest('SHA-256', data);
        return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');
      };

      // Live-stream overlay (reusable ggtok.js). The host owns the economy: a gift plays our chime
      // and grants damped hearts; GGTok runs the rest (viewers, chat, gifts, USD earnings, panel).
      const ggtok = GGTok.useGGTok({
        active: isGGTok,
        pools: neutralMode ? NEUTRAL_GGTOK_POOLS : undefined,
        onGift: (g) => {
          // GGTok gift chime is played softly (and skipped entirely when the overlay is muted).
          if (!g.muted) playGiftSound(Math.min(1, Math.log10((g.coins || 0) + 1) / 4.5), 0.25);
          // Hearts now track the dollars this gift earns ($1 ≈ 1 heart), with a sub-dollar carry so
          // even tiny gifts eventually pay out and the heart total stays in lockstep with earnings.
          giftDollarCarryRef.current += (g.coins || 0) * 0.005;
          const reward = Math.floor(giftDollarCarryRef.current);
          if (reward > 0) {
            giftDollarCarryRef.current -= reward;
            awardHearts(reward);
          }
        }
      });

      const showToast = (msg) => { setToastMessage(msg); setTimeout(() => setToastMessage(null), 4000); };

      // Central heart-grant: bumps daily + total hearts together and persists (same shape as onGift).
      const awardHearts = (n) => {
        if (!n || n <= 0) return;
        setDailyHearts(dh => {
          const nd = dh + n;
          setTotalHearts(th => {
            const nt = th + n;
            if (user && db && fbAPI) fbAPI.setDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'stats', 'data'), { dailyHearts: nd, totalHearts: nt }, { merge: true });
            return nt;
          });
          return nd;
        });
      };

      // Tiny natural-language due-date parser for inline capture (e.g. "call mom tomorrow").
      // Returns { dueDate: 'YYYY-MM-DD'|null, text } with the matched keyword stripped from text.
      const parseInlineDate = (raw) => {
        const days = ['sunday','monday','tuesday','wednesday','thursday','friday','saturday'];
        const fmt = (d) => `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
        const lower = raw.toLowerCase();
        const now = new Date();
        let due = null, matched = null;
        if (/\btoday\b/.test(lower)) { due = fmt(now); matched = /\btoday\b/i; }
        else if (/\btomorrow\b/.test(lower)) { const d = new Date(now); d.setDate(d.getDate()+1); due = fmt(d); matched = /\btomorrow\b/i; }
        else if (/\bnext week\b/.test(lower)) { const d = new Date(now); d.setDate(d.getDate()+7); due = fmt(d); matched = /\bnext week\b/i; }
        else {
          for (let i = 0; i < days.length; i++) {
            const re = new RegExp(`\\b${days[i]}\\b`, 'i');
            if (re.test(lower)) { const d = new Date(now); const delta = ((i - d.getDay()) + 7) % 7 || 7; d.setDate(d.getDate()+delta); due = fmt(d); matched = re; break; }
          }
        }
        const text = matched ? raw.replace(matched, '').replace(/\s{2,}/g,' ').trim() : raw;
        return { dueDate: due, text };
      };

      // Hype generators: build chat lines + tweets that REFER to what the user actually typed,
      // so the room and the timeline feel like they're reacting to this specific task (task #13).
      const FAN_HANDLES = [
        { name: 'Your Biggest Fan', handle: '@always_rooting4u' },
        { name: 'Productivity Stan', handle: neutralMode ? '@getitdone_daily' : '@getitdone_queen' },
        { name: 'Hype Committee', handle: '@officialhypeteam' },
        { name: 'Future You', handle: '@futureself_xo' },
        { name: 'The Universe', handle: '@universe_says' },
        { name: 'Daily Motivation', handle: neutralMode ? '@keepgoingfriend' : '@keepgoinghun' },
        { name: 'Bestie', handle: '@ride_or_die' }
      ];
      const snippetOf = (t) => { const s = String(t || '').trim(); const w = s.split(/\s+/).slice(0, 5).join(' '); return w + (s.split(/\s+/).length > 5 ? '…' : ''); };
      const chatLineAbout = (snip, kind) => {
        const done = [`YESSS "${snip}" DONE 🙌`, `proud of you for finishing "${snip}" ✨`, `"${snip}" ✅ icon behavior`, neutralMode ? `you really just did "${snip}" 🔥` : `she really just did "${snip}" 😭💖`];
        const add = [`ooh "${snip}" love that for you`, `"${snip}" — yes get it 🔥`, `adding "${snip}"?? so organized 📝`, `"${snip}" is gonna feel SO good to finish`, `manifesting "${snip}" for you rn ✨`];
        const pool = kind === 'complete' ? done : add;
        return pool[Math.floor(Math.random() * pool.length)];
      };
      const tweetAbout = (snip, kind) => {
        const done = [`just watched someone absolutely demolish "${snip}". inspiring tbh 🙌`, `"${snip}" ✅ — this is what discipline looks like.`, `reminder: you finished "${snip}" today. that counts. 💪`];
        const add = [`"${snip}" just got added to the list. the focus is unreal 📝✨`, `lowkey "${snip}" is going to change everything. believe it.`, neutralMode ? `planning "${snip}" — we love someone with a plan 💪` : `she's planning "${snip}" — we love a woman with a plan 💖`, `"${snip}" on the agenda? unstoppable energy today 🔥`];
        const pool = kind === 'complete' ? done : add;
        return pool[Math.floor(Math.random() * pool.length)];
      };
      const pushTweet = (text) => {
        const f = FAN_HANDLES[Math.floor(Math.random() * FAN_HANDLES.length)];
        setTweets(prev => [{ id: Date.now() + Math.random(), name: f.name, handle: f.handle, text, likes: Math.floor(Math.random()*900)+12, retweets: Math.floor(Math.random()*120)+1, ts: Date.now() }, ...prev].slice(0, 24));
      };
      // React to a piece of text the user wrote: nudge the GGTok chat + the praise timeline.
      const reactToText = (text, kind) => {
        const snip = snippetOf(text);
        if (!snip) return;
        if (isGGTokRef.current) ggtok.react('message', { text: chatLineAbout(snip, kind) });
        pushTweet(tweetAbout(snip, kind));
      };

      // User talks back to their own stream: echo their message, earn a heart, get a fan reply (#11).
      const userChatCountRef = useRef(0);
      const handleUserChat = (text) => {
        const t = String(text || '').trim();
        if (!t) return;
        ggtok.react('message', { user: neutralMode ? 'You 💬' : 'You 💖', text: t });
        // A heart for engaging, but lightly capped so it isn't an infinite faucet.
        userChatCountRef.current += 1;
        if (userChatCountRef.current % 2 === 1) { awardHearts(1); showToast('+1 💖 for hyping the chat'); }
        // A viewer or two reacts to what you said.
        const replies = neutralMode
          ? [`@You yes exactly 🙌`, `chat agrees with you 🙌`, `so real ✨`, `say it louder 📣`, `facts 🔥`]
          : [`@You omg yes 💕`, `chat agrees with you 🙌`, `she's RIGHT ✨`, `say it louder 💖`, `facts queen 🔥`];
        setTimeout(() => ggtok.react('message', { text: replies[Math.floor(Math.random()*replies.length)] }), 700 + Math.random()*900);
      };

      // Idle praise tweets so the timeline always has warm things scrolling by (task #8).
      const GENERIC_TWEETS = [
        "honestly? you're doing better than you think. keep going 💖",
        "reminder that showing up at all is the hard part. proud of you ✨",
        "the way you keep your list tidy is lowkey iconic 📝",
        "your consistency is going to pay off so big. mark my words 🔥",
        "small steps still count as steps. you're moving 💪",
        "imagine how good tonight's 'done' list is going to feel 💕",
        neutralMode ? "you make productivity look effortless and i'm here for it 💪" : "you make productivity look soft and pretty and i'm here for it 🩷",
        "future you is already grateful for what you're doing right now ✨",
        "checking in to say: you've got this, no notes 💖"
      ];
      // Keep the latest (mode-aware) line set in a ref so the idle interval re-randomizes
      // against the current wording even after the neutral toggle is flipped live.
      const genericTweetsRef = useRef(GENERIC_TWEETS);
      genericTweetsRef.current = GENERIC_TWEETS;
      useEffect(() => {
        if (tweets.length === 0) { genericTweetsRef.current.slice(0, 3).forEach(t => pushTweet(t)); }
        const iv = setInterval(() => { 
          if (document.hidden) return;
          const pool = genericTweetsRef.current; pushTweet(pool[Math.floor(Math.random() * pool.length)]); 
        }, 22000);
        return () => clearInterval(iv);
      }, []);

      // Subliminal flasher: every so often, flash one of the user's goal phrases on screen (task #12).
      useEffect(() => {
        const phrases = goals.map(g => (g.text || '').trim()).filter(Boolean);
        if (phrases.length === 0) return;
        const tick = () => {
          if (document.hidden) return;
          const text = phrases[Math.floor(Math.random() * phrases.length)];
          setSubliminal({ id: Date.now(), text });
          setTimeout(() => setSubliminal(null), 1100);
        };
        const iv = setInterval(tick, 14000);
        return () => clearInterval(iv);
      }, [goals]);

      // Goal slot mechanics: each extra subliminal slot costs hearts and unlocks one more phrase.
      const GOAL_SLOT_COST = 25;
      const buyGoalSlot = () => {
        if (totalHearts < GOAL_SLOT_COST) { showToast('Not enough 💖 to unlock a slot yet'); return; }
        const nt = totalHearts - GOAL_SLOT_COST;
        const slots = unlockedGoalSlots + 1;
        setTotalHearts(nt); setUnlockedGoalSlots(slots);
        if (user && db && fbAPI) saveStats({ totalHearts: nt, unlockedGoalSlots: slots });
        showToast('✨ New subliminal slot unlocked');
      };
      const setGoalText = (idx, text) => {
        setGoals(prev => {
          const next = prev.slice();
          next[idx] = { ...(next[idx] || {}), text };
          if (user && db && fbAPI) saveStats({ goals: next });
          return next;
        });
      };
      const clearGoal = (idx) => {
        setGoals(prev => {
          const next = prev.filter((_, i) => i !== idx);
          if (user && db && fbAPI) saveStats({ goals: next });
          return next;
        });
      };

      // Eisenhower matrix (task #9): tasks land in a quadrant by urgency × importance. If the user
      // hasn't placed a task by hand, we guess: a near/overdue date = urgent, score/category = important.
      const QUADRANTS = [
        { id: 'do',        title: 'Do First',  sub: 'Urgent + Important',      cls: 'bg-rose-50 border-rose-200',    tag: 'bg-rose-500' },
        { id: 'schedule', title: 'Schedule',   sub: 'Important, Not Urgent',  cls: 'bg-amber-50 border-amber-200',  tag: 'bg-amber-500' },
        { id: 'delegate', title: 'Delegate',   sub: 'Urgent, Not Important',  cls: 'bg-sky-50 border-sky-200',      tag: 'bg-sky-500' },
        { id: 'delete',   title: 'Later / Drop', sub: 'Not Urgent or Important', cls: 'bg-slate-50 border-slate-200', tag: 'bg-slate-400' }
      ];
      const classifyQuadrant = (task) => {
        if (task.quadrant) return task.quadrant;
        const today = new Date(); today.setHours(23,59,59,999);
        const soon = new Date(); soon.setDate(soon.getDate() + 3);
        const urgent = !!task.dueDate && new Date(task.dueDate) <= soon;
        const important = (task.score || 0) > 0 || ['poise','routine'].includes(task.category) || (task.hearts || 0) >= 3 || task.isProject;
        if (urgent && important) return 'do';
        if (!urgent && important) return 'schedule';
        if (urgent && !important) return 'delegate';
        return 'delete';
      };
      const setQuadrant = (taskId, q) => {
        setTasks(prev => prev.map(t => t.id === taskId ? { ...t, quadrant: q } : t));
        if (user && db && fbAPI) fbAPI.updateDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'tasks', taskId), { quadrant: q });
      };

      // Patch the currently-open task in state + Firestore (used by the detail-panel editors).
      const patchSelected = (patch) => {
        if (!selectedTask) return;
        const id = selectedTask.id;
        setSelectedTask(st => st ? { ...st, ...patch } : st);
        setTasks(prev => prev.map(t => t.id === id ? { ...t, ...patch } : t));
        if (user && db && fbAPI) fbAPI.updateDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'tasks', id), { ...patch });
      };
      // Add a tag/context to the open task; the first tag/context earns a small organizing bonus (#3/#16).
      const addToken = (kind) => (raw) => {
        const v = String(raw || '').trim();
        if (!v) return;
        const field = kind === 'tag' ? 'tags' : 'contexts';
        const token = kind === 'tag' ? '#' + v.replace(/^#/, '').toLowerCase().replace(/\s+/g, '') : '@' + v.replace(/^@/, '').replace(/\s+/g, '');
        const cur = selectedTask[field] || [];
        if (cur.includes(token)) return;
        if (cur.length === 0) awardHearts(1);
        patchSelected({ [field]: [...cur, token] });
      };
      const removeToken = (kind, token) => {
        const field = kind === 'tag' ? 'tags' : 'contexts';
        patchSelected({ [field]: (selectedTask[field] || []).filter(x => x !== token) });
      };
      // Convert a task to/from a project (a task that holds checklist sub-steps) — task #6.
      const toggleProject = () => patchSelected({ isProject: !selectedTask.isProject, subtasks: selectedTask.subtasks || [] });
      const addSubtask = (text) => {
        const v = String(text || '').trim();
        if (!v) return;
        patchSelected({ subtasks: [...(selectedTask.subtasks || []), { id: Math.random().toString(36).slice(2, 9), text: v, done: false }] });
      };
      const toggleSubtask = (sid) => {
        const subs = (selectedTask.subtasks || []).map(s => s.id === sid ? { ...s, done: !s.done } : s);
        const justDone = subs.find(s => s.id === sid)?.done && !(selectedTask.subtasks || []).find(s => s.id === sid)?.done;
        if (justDone) awardHearts(1);
        patchSelected({ subtasks: subs });
      };
      const removeSubtask = (sid) => patchSelected({ subtasks: (selectedTask.subtasks || []).filter(s => s.id !== sid) });

      const saveStats = async (updates) => {
        if (!user || !db || !fbAPI) return;
        await fbAPI.setDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'stats', 'data'), updates, { merge: true });
      };

      // Returns the next praise word in the rotation (cycling through all unlocked ones, in shop
      // order) and advances the pointer. Newly unlocked affirmations are picked up automatically.
      const nextPraise = () => {
        const pool = PRAISES.filter(p => unlockedPraises.includes(p.id));
        if (pool.length === 0) return PRAISES[0];
        const praise = pool[praiseIndexRef.current % pool.length];
        praiseIndexRef.current = (praiseIndexRef.current + 1) % pool.length;
        return praise;
      };

      // Fonts and animations also ROTATE through everything unlocked (task #15) — buying more just
      // widens the variety instead of replacing the one equipped item. Advances on each event.
      const nextFontClass = () => {
        const pool = SHOP_FONTS.filter(f => unlockedFonts.includes(f.id));
        if (pool.length === 0) return activeFontClass;
        const f = pool[fontIndexRef.current % pool.length];
        fontIndexRef.current = (fontIndexRef.current + 1) % pool.length;
        return f.class;
      };
      const nextAnim = () => {
        const pool = SHOP_ANIMS.filter(a => unlockedAnims.includes(a.id));
        if (pool.length === 0) return activeAnim;
        const a = pool[animIndexRef.current % pool.length];
        animIndexRef.current = (animIndexRef.current + 1) % pool.length;
        return a.id;
      };
      // Check-off icons vary per task (stable per task via its id) so the list feels lively.
      const iconForTask = (task) => {
        const pool = unlockedIcons && unlockedIcons.length ? unlockedIcons : ['check'];
        let h = 0; const s = String(task.id || ''); for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0;
        return pool[h % pool.length];
      };

      // Burst of sparkly confetti for streak milestones.
      const triggerConfetti = () => {
        const id = Date.now();
        setConfetti({ id });
        setTimeout(() => setConfetti(c => (c && c.id === id ? null : c)), 2800);
      };

      // Spray a few floating heart particles from a point — the subtle check-off flourish.
      const spawnParticles = (x, y) => {
        const id = Date.now();
        setParticles({ id, x, y });
        setTimeout(() => setParticles(p => (p && p.id === id ? null : p)), 1200);
      };

      // Runs once per session when stats first load: advances the daily streak, grants a glowing
      // login bonus (hearts scale with the streak), bumps the "Days Being a Good Girl" counter,
      // and celebrates milestones with confetti.
      const runDailyLogin = (data) => {
        if (streakCheckedRef.current) return;
        streakCheckedRef.current = true;
        const todayStr = new Date().toDateString();
        const yesterdayStr = new Date(Date.now() - 86400000).toDateString();
        const prevActive = data.lastActiveDate || null;
        if (prevActive === todayStr) return; // already greeted today
        const prevStreak = data.streak || 0;
        const newStreak = prevActive === yesterdayStr ? prevStreak + 1 : 1;
        const newDays = (data.daysActive || 0) + 1;
        const newBest = Math.max(data.bestStreak || 0, newStreak);
        const bonus = 3 + Math.min(newStreak, 12);
        setStreak(newStreak); setBestStreak(newBest); setDaysActive(newDays); setLastActiveDate(todayStr);
        setDailyHearts(dh => {
          const nd = dh + bonus;
          setTotalHearts(th => {
            const nt = th + bonus;
            saveStats({ streak: newStreak, bestStreak: newBest, daysActive: newDays, lastActiveDate: todayStr, dailyHearts: nd, totalHearts: nt });
            return nt;
          });
          return nd;
        });
        setLoginBonus({ hearts: bonus, streak: newStreak });
        setTimeout(() => setLoginBonus(null), 5000);
        if (STREAK_MILESTONES.includes(newStreak)) { triggerConfetti(); playRoundSound(); }
      };

      // Reward real productivity (focus / typing / copying). Always grants hearts; in GGTok mode it
      // also bumps session earnings and occasionally makes the chat react. Uses the same nested-setter
      // Firestore-write pattern as the gift handler so we always persist the freshest totals.
      const awardProductivity = (coins, reaction) => {
        if (!coins || coins <= 0) return;
        setDailyHearts(dh => {
          const newDaily = dh + coins;
          setTotalHearts(th => {
            const newTotal = th + coins;
            if (user && db && fbAPI) {
              fbAPI.setDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'stats', 'data'), {
                dailyHearts: newDaily, totalHearts: newTotal
              }, { merge: true });
            }
            return newTotal;
          });
          return newDaily;
        });

        // Make the live chat react to real productivity (no-op when the overlay is off).
        if (reaction) ggtok.react('productivity');
      };

      // Count characters typed into any editor; every 25 chars earns a soft token.
      const registerTyping = (delta) => {
        if (delta <= 0) return;
        typedCharsRef.current += delta;
        if (typedCharsRef.current >= 25) {
          typedCharsRef.current = 0;
          playSoftChime();
          awardProductivity(1, true);
        }
      };

      // Copy text to clipboard with a reward + toast.
      const copyToClipboard = async (text, label = 'Copied!') => {
        try {
          await navigator.clipboard.writeText(text || '');
          playCopySound();
          awardProductivity(2, true);
          showToast(label + ' 🩷');
        } catch (e) {
          showToast("Couldn't copy 😢");
        }
      };

      // --- Standalone notes ---
      const createNote = async () => {
        const id = Math.random().toString(36).substr(2, 9);
        const now = Date.now();
        const note = { id, title: 'Untitled', content: '', createdAt: now, modifiedAt: now };
        setNotes(prev => [note, ...prev]);
        setSelectedNote(note);
        playAddSound();
        if (user && db && fbAPI) {
          await fbAPI.setDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'notes', id), note);
        }
      };

      const saveNote = (id, updates) => {
        const merged = { ...updates, modifiedAt: Date.now() };
        setNotes(prev => prev.map(n => n.id === id ? { ...n, ...merged } : n));
        setSelectedNote(prev => prev && prev.id === id ? { ...prev, ...merged } : prev);
        if (user && db && fbAPI) {
          fbAPI.setDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'notes', id), merged, { merge: true });
        }
      };

      const deleteNote = async (id) => {
        setNotes(prev => prev.filter(n => n.id !== id));
        setSelectedNote(prev => prev && prev.id === id ? null : prev);
        if (user && db && fbAPI) {
          await fbAPI.deleteDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'notes', id));
        }
      };

      // Persist a task's editor body.
      const saveTaskBody = (id, body) => {
        setTasks(prev => prev.map(t => t.id === id ? { ...t, body } : t));
        setSelectedTask(prev => prev && prev.id === id ? { ...prev, body } : prev);
        if (user && db && fbAPI) {
          fbAPI.updateDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'tasks', id), { body });
        }
      };

      // Authenticate & Setup DB
      useEffect(() => {
        if (!fbAPI) return;
        
        const config = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : {
          apiKey: "AIzaSyD_2ptaqjk1URKhwoEhRZwirVMEEcha39Y",
          authDomain: "gg-to-do.firebaseapp.com",
          projectId: "gg-to-do",
          storageBucket: "gg-to-do.firebasestorage.app",
          messagingSenderId: "332290400317",
          appId: "1:332290400317:web:1c5bbb0d091ba53bf98a54"
        };
        
        let appInstance = null;
        try { appInstance = fbAPI.initializeApp(config); } catch(e) { appInstance = fbAPI.initializeApp(config, "app-"+Math.random()); }
        
        const auth = fbAPI.getAuth(appInstance);
        authRef.current = auth;
        // iOS Safari (and some networks/proxies) silently break Firestore's default
        // real-time transport (WebChannel), so the device reads/writes its own data
        // but never receives live updates from other devices. Force long-polling,
        // which is reliable everywhere. Falls back to getFirestore if already inited.
        let firestore;
        try {
          firestore = fbAPI.initializeFirestore(appInstance, { experimentalForceLongPolling: true });
        } catch (e) {
          firestore = fbAPI.getFirestore(appInstance);
        }
        setDb(firestore);

        const unsub = fbAPI.onAuthStateChanged(auth, (u) => {
           setUser(u);
           if (u) {
             // Diagnostic: log identity so two devices can be compared. If the uid
             // differs between phone and web, they're different accounts/builds —
             // that's why data wouldn't "sync".
             console.log('[ggtodo] build', window.APP_VERSION, '| signed in as', u.email, '| uid', u.uid, '| project', config.projectId);
             showToast("Cloud Sync Active! ☁️");
           } else {
             setBooting(false);
           }
        });
        return () => unsub();
      }, [fbAPI]);

      // Real-time Sync Listeners
      useEffect(() => {
        if (!user || !db || !fbAPI) return;
        setStatsReady(false); // re-evaluate PIN status for this user once stats arrive

        // Process offline inbox queue — staggered reveal after app loads
        const queue = JSON.parse(localStorage.getItem('offlineInboxQueue') || '[]');
        if (queue.length > 0) {
          localStorage.removeItem('offlineInboxQueue');
          // Wait for UI to be fully visible, then reveal tasks one by one
          setTimeout(() => {
            showToast(`📥 Importing ${queue.length} inbox task${queue.length > 1 ? 's' : ''}…`);
            queue.forEach((item, i) => {
              setTimeout(() => {
                const { dueDate, text } = parseInlineDate(item.text);
                const cid = neutralMode ? NEUTRAL_CATEGORIES[0].id : TASK_CATEGORIES[0].id;
                const newTask = {
                  id: Math.random().toString(36).substr(2,9),
                  text, category: cid, status: 'pending', hearts: 0,
                  createdAt: item.timestamp || Date.now(),
                  dueDate, quadrant: 2, order: 0, subtasks: [], score: 1000, matchesPlayed: 0,
                  _newlyImported: true  // flag for animation
                };
                fbAPI.setDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'tasks', newTask.id), newTask)
                  .catch(e => console.error(e));
                // Final task: show success banner
                if (i === queue.length - 1) {
                  setTimeout(() => {
                    showToast(`✅ ${queue.length} task${queue.length > 1 ? 's' : ''} added to your inbox!`);
                  }, 400);
                }
              }, 800 + i * 320);  // stagger each task 320ms apart
            });
          }, 900);  // wait 900ms for app to fully render before starting
        }

        const tasksRef = fbAPI.collection(db, 'artifacts', appId, 'users', user.uid, 'tasks');
        const unsubTasks = fbAPI.onSnapshot(tasksRef, (snapshot) => {
          const loadedTasks = [];
          snapshot.forEach(doc => loadedTasks.push(doc.data()));
          loadedTasks.sort((a, b) => b.createdAt - a.createdAt);
          setTasks(loadedTasks);
          setSyncError(null); // a successful read clears any prior error
        }, (err) => reportSyncError(err, 'reading tasks'));

        const notesRef = fbAPI.collection(db, 'artifacts', appId, 'users', user.uid, 'notes');
        const unsubNotes = fbAPI.onSnapshot(notesRef, (snapshot) => {
          const loadedNotes = [];
          snapshot.forEach(doc => loadedNotes.push(doc.data()));
          loadedNotes.sort((a, b) => (b.modifiedAt || 0) - (a.modifiedAt || 0));
          setNotes(loadedNotes);
        }, (err) => reportSyncError(err, 'reading notes'));

        const statsRef = fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'stats', 'data');
        const unsubStats = fbAPI.onSnapshot(statsRef, (docSnap) => {
          if (docSnap.exists()) {
             const data = docSnap.data();
             setPinHash(data.pinHash || null);
             const today = new Date().toDateString();
             
             if (data.lastResetDate && data.lastResetDate !== today) {
               setDailyHearts(0); setDailyMiniHearts(0); setLastResetDate(today);
               fbAPI.setDoc(statsRef, { dailyHearts: 0, dailyMiniHearts: 0, lastResetDate: today }, { merge: true });
             } else {
               setDailyHearts(data.dailyHearts || 0); setDailyMiniHearts(data.dailyMiniHearts || 0);
               setLastResetDate(data.lastResetDate || today);
             }
             
             setTotalHearts(data.totalHearts || 0); setKissElo(data.kissElo || 1200);
             if(data.unlockedFonts) setUnlockedFonts(data.unlockedFonts);
             if(data.activeFont) setActiveFont(data.activeFont);
             if(data.unlockedAnims) setUnlockedAnims(data.unlockedAnims);
             if(data.activeAnim) setActiveAnim(data.activeAnim);
             if(data.unlockedPraises) setUnlockedPraises(data.unlockedPraises);
             if(data.activePraise) setActivePraise(data.activePraise);
             if(data.unlockedThemes) setUnlockedThemes(data.unlockedThemes);
             if(data.activeTheme) setActiveTheme(data.activeTheme);
             if(data.unlockedIcons) setUnlockedIcons(data.unlockedIcons);
             if(data.activeIcon) setActiveIcon(data.activeIcon);
             // Streak / profile fields
             setStreak(data.streak || 0);
             setBestStreak(data.bestStreak || 0);
             setDaysActive(data.daysActive || 0);
             setTasksCompleted(data.tasksCompleted || 0);
             setLastActiveDate(data.lastActiveDate || null);
             if(data.avatarId) setAvatarId(data.avatarId);
             if(data.unlockedBackgrounds) setUnlockedBackgrounds(data.unlockedBackgrounds);
             if(data.activeBackground) setActiveBackground(data.activeBackground);
             if(Array.isArray(data.goals)) setGoals(data.goals);
             if(typeof data.unlockedGoalSlots === 'number') setUnlockedGoalSlots(data.unlockedGoalSlots);
             if(typeof data.neutralMode === 'boolean') setNeutralMode(data.neutralMode);
             runDailyLogin(data);
             setStatsReady(true);
             setBooting(false);
          } else {
             // Brand-new account — start the streak at day one, no PIN yet.
             setPinHash(null);
             runDailyLogin({});
             setStatsReady(true);
             setBooting(false);
          }
        }, (err) => { reportSyncError(err, 'reading stats'); setStatsReady(true); setBooting(false); });

        return () => { unsubTasks(); unsubNotes(); unsubStats(); };
      }, [user, db, fbAPI, appId]);

      // Handle Theme Updates
      useEffect(() => {
        const theme = SHOP_THEMES?.find(t => t.id === activeTheme) || SHOP_THEMES?.[0];
        if (theme) document.body.className = `${theme.class} transition-colors duration-500`;
      }, [activeTheme]);

      // Focus trickle: simply keeping an editor focused slowly earns hearts/tokens.
      useEffect(() => {
        const id = setInterval(() => { 
          if (document.hidden) return;
          if (editingRef.current) awardProductivity(1, true); 
        }, 8000);
        return () => clearInterval(id);
      }, [user, db, fbAPI]);

      // Keep a live ref of tasks for interval-driven reminders (avoids stale closures).
      const tasksSnapshotRef = useRef([]);
      useEffect(() => { tasksSnapshotRef.current = tasks; }, [tasks]);

      // Loader fallback: never hang the sparkly splash if the cloud is slow or offline.
      useEffect(() => { const t = setTimeout(() => setBooting(false), 2000); return () => clearTimeout(t); }, []);

      // Random positive affirmation pop-ups (paused during GGTok mode).
      useEffect(() => {
        if (!user) return;
        const id = setInterval(() => {
          if (document.hidden || isGGTokRef.current) return;
          setAffirmation({ id: Date.now(), text: AFFIRMS[Math.floor(Math.random() * AFFIRMS.length)] });
          setTimeout(() => setAffirmation(null), 5000);
        }, 95000);
        return () => clearInterval(id);
      }, [user]);

      // Gentle in-app reminders for tasks due today / overdue (sparkly hearts, never an OS popup).
      useEffect(() => {
        if (!user) return;
        const id = setInterval(() => {
          if (document.hidden || isGGTokRef.current) return;
          const today = new Date(); today.setHours(0,0,0,0);
          const due = tasksSnapshotRef.current.filter(t => t.status === 'open' && t.dueDate && new Date(t.dueDate) <= today);
          if (due.length === 0) return;
          const pick = due[Math.floor(Math.random() * due.length)];
          const line = REMINDERS[Math.floor(Math.random() * REMINDERS.length)];
          setReminder({ id: Date.now(), text: `${line}: “${pick.text}” 💖✨` });
          setTimeout(() => setReminder(null), 6000);
        }, 150000);
        return () => clearInterval(id);
      }, [user]);

      // Global help: Ctrl+/ (or ?) toggles the help overlay; Esc closes it. Ctrl+H stays as
      // editor-backspace and is intentionally left alone.
      useEffect(() => {
        const onKey = (e) => {
          if ((e.ctrlKey || e.metaKey) && e.key === '/') { e.preventDefault(); setShowHelp(h => !h); }
          else if (e.key === '?' && !e.target.matches('input, textarea')) { e.preventDefault(); setShowHelp(h => !h); }
          else if (e.key === 'Escape') setShowHelp(false);
        };
        window.addEventListener('keydown', onKey);
        return () => window.removeEventListener('keydown', onKey);
      }, []);


      const handleCapture = async (e) => {
        if (e.key === 'Enter' && inputValue.trim()) {
          const id = Math.random().toString(36).substr(2, 9);
          const rawText = inputValue.trim();
          const extractedTags = (rawText.match(/#[\w]+/g) || []).map(t => t.toLowerCase());
          // Contexts are GTD-style @markers (e.g. @Home, @Office) — display-cased, deduped.
          const extractedContexts = [...new Set((rawText.match(/@[\w]+/g) || []).map(c => '@' + c.slice(1)))];
          // Pull a natural due date out of the text ("call mom tomorrow") before stripping markers.
          const { dueDate, text: datedText } = parseInlineDate(rawText);
          const cleanText = datedText.replace(/[#@][\w]+/g, '').replace(/\s{2,}/g,' ').trim() || rawText;

          const newTask = {
            id,
            text: cleanText,
            status: 'open',
            createdAt: Date.now(),
            order: Date.now(),
            score: 0,
            matchesPlayed: 0,
            hearts: 1,
            notes: '',
            body: '',
            tags: extractedTags,
            contexts: extractedContexts,
            category: captureCategory,
            dueDate: dueDate,
            isProject: false,
            subtasks: []
          };

          playAddSound();
          setTasks([newTask, ...tasks]);
          setInputValue('');
          setCaptureAnim({ active: true, id: Date.now(), praiseText: nextPraise().text, fontClass: nextFontClass() });
          setTimeout(() => setCaptureAnim({ active: false }), 600);

          // Every capture earns hearts (task #5); tagging/contexts and a due date each add a bonus (#3).
          let reward = 2;
          const tagged = extractedTags.length > 0 || extractedContexts.length > 0 || !!captureCategory;
          if (tagged) reward += 1;
          if (dueDate) reward += 1;
          awardHearts(reward);
          showToast(`+${reward} 💖  ${tagged || dueDate ? 'nice organizing!' : 'captured!'}`);

          // Smarter chat + Twitter: react to the actual thing the user just wrote (task #13).
          reactToText(cleanText, 'add');

          const nElo = kissElo + 2 + extractedTags.length + extractedContexts.length + (dueDate ? 2 : 0);
          setKissElo(nElo);

          if (user && db && fbAPI) {
            try {
              await fbAPI.setDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'tasks', newTask.id), newTask);
              await saveStats({ kissElo: nElo, lastResetDate });
            } catch (err) {
              reportSyncError(err, 'saving a task');
            }
          }
        }
      };

      const handleDragStart = (e, task) => {
        setDraggedTaskId(task.id);
        e.dataTransfer.effectAllowed = 'move';
        setTimeout(() => { if (e.target) e.target.style.opacity = '0.5'; }, 0);
      };
      const handleDragEnd = (e) => {
        if (e.target) e.target.style.opacity = '1';
        setDraggedTaskId(null);
      };
      const handleDragOver = (e) => {
        e.preventDefault();
        e.dataTransfer.dropEffect = 'move';
      };
      const handleDrop = async (e, targetTask) => {
        e.preventDefault();
        if (!draggedTaskId || draggedTaskId === targetTask.id) return;
        
        let inboxTasks = tasks.filter(t => t.status === 'open' && t.matchesPlayed === 0).sort((a,b) => (b.order || b.createdAt) - (a.order || a.createdAt));
        inboxTasks = inboxTasks.filter(t => t.id !== draggedTaskId);
        
        const targetIndex = inboxTasks.findIndex(t => t.id === targetTask.id);
        if (targetIndex === -1) return;
        
        let newOrder = 0;
        const rect = e.currentTarget.getBoundingClientRect();
        const dropRatio = (e.clientY - rect.top) / rect.height;
        const insertAfter = dropRatio > 0.5;
        const insertIndex = insertAfter ? targetIndex + 1 : targetIndex;
        
        if (insertIndex === 0) {
          newOrder = (inboxTasks[0].order || inboxTasks[0].createdAt) + 1000;
        } else if (insertIndex >= inboxTasks.length) {
          newOrder = (inboxTasks[inboxTasks.length-1].order || inboxTasks[inboxTasks.length-1].createdAt) - 1000;
        } else {
          const prevOrder = inboxTasks[insertIndex - 1].order || inboxTasks[insertIndex - 1].createdAt;
          const nextOrder = inboxTasks[insertIndex].order || inboxTasks[insertIndex].createdAt;
          newOrder = (prevOrder + nextOrder) / 2;
        }
        
        setTasks(prev => prev.map(t => t.id === draggedTaskId ? {...t, order: newOrder} : t));
        if (user && db && fbAPI) fbAPI.updateDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'tasks', draggedTaskId), { order: newOrder });
      };

      const completeTask = async (task, e) => {
        playRewardSound();
        // Streak heart multiplier: every milestone reached adds +0.5× to earned hearts.
        const mult = 1 + streakLevel(streak) * 0.5;
        const hearts = Math.max(1, Math.round(getDisplayHearts(task) * mult));
        const rect = e.currentTarget.getBoundingClientRect();
        spawnParticles(rect.left + rect.width / 2, rect.top + rect.height / 2);
        setRewardAnim({ id: Date.now(), x: rect.left + rect.width / 2, y: rect.top, heartsAmount: hearts, type: nextAnim(), praiseText: nextPraise().text, fontClass: nextFontClass() });
        showToast(COMPLETIONS[Math.floor(Math.random() * COMPLETIONS.length)]);

        const newCompleted = tasksCompleted + 1;
        setTasksCompleted(newCompleted);
        const nDaily = dailyHearts + hearts; const nTotal = totalHearts + hearts; const nElo = kissElo + 10 + (hearts * 2);

        const completedAt = Date.now();
        const earnedHearts = hearts;
        setDailyHearts(nDaily); setTotalHearts(nTotal); setKissElo(nElo);
        setTasks(prev => prev.map(t => t.id === task.id ? { ...t, status: 'completed', completedAt, earnedHearts } : t));

        ggtok.react('complete', { label: task.text });
        pushTweet(tweetAbout(snippetOf(task.text), 'complete'));

        if (user && db && fbAPI) {
          await fbAPI.updateDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'tasks', task.id), { status: 'completed', completedAt, earnedHearts });
          await saveStats({ dailyHearts: nDaily, totalHearts: nTotal, kissElo: nElo, tasksCompleted: newCompleted });
        }

        setTimeout(() => setRewardAnim(null), 2500);
      };

      // Send a completed task back to the active inbox (history → open).
      const restoreTask = async (task) => {
        playAddSound();
        setTasks(prev => prev.map(t => t.id === task.id ? { ...t, status: 'open', completedAt: null } : t));
        showToast('↩️ Task restored to inbox');
        if (user && db && fbAPI) {
          await fbAPI.updateDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'tasks', task.id), { status: 'open', completedAt: null });
        }
      };

      const buyItem = async (item, type) => {
        const configs = {
          font: { list: unlockedFonts, active: activeFont, setList: setUnlockedFonts, setActive: setActiveFont, keyL: 'unlockedFonts', keyA: 'activeFont' },
          anim: { list: unlockedAnims, active: activeAnim, setList: setUnlockedAnims, setActive: setActiveAnim, keyL: 'unlockedAnims', keyA: 'activeAnim' },
          praise: { list: unlockedPraises, active: activePraise, setList: setUnlockedPraises, setActive: setActivePraise, keyL: 'unlockedPraises', keyA: 'activePraise' },
          theme: { list: unlockedThemes, active: activeTheme, setList: setUnlockedThemes, setActive: setActiveTheme, keyL: 'unlockedThemes', keyA: 'activeTheme' },
          icon: { list: unlockedIcons, active: activeIcon, setList: setUnlockedIcons, setActive: setActiveIcon, keyL: 'unlockedIcons', keyA: 'activeIcon' }
        };
        
        const conf = configs[type];
        if (conf.list.includes(item.id)) {
          conf.setActive(item.id);
          await saveStats({ [conf.keyA]: item.id });
        } else if (totalHearts >= item.price) {
          const nTotal = totalHearts - item.price;
          const newList = [...conf.list, item.id];
          setTotalHearts(nTotal);
          conf.setList(newList);
          conf.setActive(item.id);
          await saveStats({ totalHearts: nTotal, [conf.keyL]: newList, [conf.keyA]: item.id });
          showToast(`Unlocked ${item.name}! ✨`);
        } else {
          showToast(neutralMode ? "Not enough hearts yet!" : "Not enough hearts, darling!");
        }
      };

      // Reward gallery: backgrounds unlock by streak milestone, or can be bought with hearts.
      const bgUnlocked = (bg) => unlockedBackgrounds.includes(bg.id) || (bg.price === 0 && streak >= bg.streak);
      const selectBackground = async (bg) => {
        if (bgUnlocked(bg)) {
          setActiveBackground(bg.id);
          await saveStats({ activeBackground: bg.id });
        } else if (bg.price > 0 && totalHearts >= bg.price) {
          const nTotal = totalHearts - bg.price;
          const newList = [...unlockedBackgrounds, bg.id];
          setTotalHearts(nTotal); setUnlockedBackgrounds(newList); setActiveBackground(bg.id);
          await saveStats({ totalHearts: nTotal, unlockedBackgrounds: newList, activeBackground: bg.id });
          showToast(`Unlocked ${bg.name}! ✨`);
        } else if (bg.price > 0) {
          showToast(neutralMode ? "Not enough hearts yet!" : "Not enough hearts, darling!");
        } else {
          showToast(`Reach a ${bg.streak}-day streak to unlock 💖`);
        }
      };
      const selectAvatar = async (id) => { setAvatarId(id); await saveStats({ avatarId: id }); };

      const handleMatchupResult = async (res) => {
        const pair = tournament.pairs[tournament.currentPairIndex]; if (!pair) return;
        const [l, r] = pair;
        const lp = res === 'left' ? 1 : res === 'draw' ? 0.5 : 0;
        const rp = res === 'right' ? 1 : res === 'draw' ? 0.5 : 0;
        const nElo = kissElo + 1; setKissElo(nElo);

        if (user && db && fbAPI) {
          await fbAPI.updateDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'tasks', l.id), { score: l.score + lp, matchesPlayed: l.matchesPlayed + 1 });
          if (r) {
            await fbAPI.updateDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'tasks', r.id), { score: r.score + rp, matchesPlayed: r.matchesPlayed + 1 });
          }
          await saveStats({ kissElo: nElo });
        }
        
        const updatedTasks = tasks.map(t => {
          if (t.id === l.id) return { ...t, score: t.score + lp, matchesPlayed: t.matchesPlayed + 1 };
          if (r && t.id === r.id) return { ...t, score: t.score + rp, matchesPlayed: t.matchesPlayed + 1 };
          return t;
        });
        setTasks(updatedTasks);

        if (tournament.currentPairIndex + 1 < tournament.pairs.length) {
          setTournament({...tournament, currentPairIndex: tournament.currentPairIndex + 1});
        } else if (tournament.round < tournament.maxRounds) {
          playRoundSound();
          let act = updatedTasks.filter(t => tournament.participants.includes(t.id));
          act.sort(() => Math.random() - 0.5); // Randomize to break score ties
          act.sort((a,b) => b.score - a.score);
          
          let p = []; 
          for(let i=0; i<act.length; i+=2) {
            if(i+1 < act.length) p.push([act[i], act[i+1]]);
            else p.push([act[i], null]); // Give the odd one out a bye
          }
          setTournament({...tournament, pairs: p, currentPairIndex: 0, round: tournament.round + 1, isTransitioning: true});
        } else {
          const ranked = updatedTasks.filter(t => t.matchesPlayed > 0).sort((a,b) => b.score - a.score);
          const finalTasks = [...updatedTasks];
          
          finalTasks.forEach(task => {
            const rIdx = ranked.findIndex(t => t.id === task.id);
            if (rIdx !== -1) {
              const perc = ((ranked.length - (rIdx+1)) / ranked.length) * 100;
              let h = 1; if(perc>=90) h=5; else if(perc>=70) h=4; else if(perc>=40) h=3; else if(perc>=15) h=2;
              task.hearts = h;
              if (user && db && fbAPI) fbAPI.updateDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'tasks', task.id), { hearts: h });
            }
          });
          setTasks(finalTasks);
          showToast("Tournament Complete! Priorities Ranked. 🏆");
          setView('inbox');
        }
      };

      const handleResetStats = async () => {
        if (!confirmReset) {
          setConfirmReset(true);
          setTimeout(() => setConfirmReset(false), 3000);
          return;
        }
        setDailyHearts(0); setDailyMiniHearts(0); setTotalHearts(0); setKissElo(1200);
        setStreak(0); setBestStreak(0); setDaysActive(0); setTasksCompleted(0);
        setConfirmReset(false);
        setShowArchive(false);
        if (user && db && fbAPI) {
          await saveStats({ dailyHearts: 0, dailyMiniHearts: 0, totalHearts: 0, kissElo: 1200, streak: 0, bestStreak: 0, daysActive: 0, tasksCompleted: 0 });
        }
        showToast("Stats cleared! Fresh start. ✨");
      };

      const getIconCmp = (id) => {
        if (id === 'star') return Star;
        if (id === 'heart') return Heart;
        if (id === 'sparkle') return Sparkles;
        if (id.startsWith('emoji_')) {
          const map = { emoji_peach: '🍑', emoji_strawberry: '🍓', emoji_pinkheart: '🩷', emoji_bow: '🎀', emoji_sparkles: '✨', emoji_cherryblossom: '🌸', emoji_tulip: '🌷', emoji_sparkleheart: '💖' };
          return (props) => <div style={{fontSize: props.size*0.8, lineHeight: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', width: props.size, height: props.size}} className={props.className}>{map[id]}</div>;
        }
        return CheckCircle;
      };

      const activeFontClass = SHOP_FONTS.find(f => f.id === activeFont)?.class || '';
      const activePraiseData = PRAISES.find(p => p.id === activePraise) || PRAISES[0];
      const activeThemeClass = SHOP_THEMES.find(t => t.id === activeTheme)?.class || 'bg-[#FFF0F5]';

      // Profile / gallery / progress derived values.
      const activeBgData = BACKGROUNDS.find(b => b.id === activeBackground) || BACKGROUNDS[0];
      const mainBgStyle = { backgroundImage: `radial-gradient(circle at 18% 22%, rgba(244,114,182,0.18) 0%, transparent 42%), radial-gradient(circle at 82% 78%, rgba(196,131,252,0.16) 0%, transparent 45%), ${activeBgData.grad}` };
      const avatarData = AVATARS.find(a => a.id === avatarId) || AVATARS[0];
      const cutenessLvl = streakLevel(streak);
      const dailyProgress = Math.min(1, DAILY_GOAL > 0 ? dailyHearts / DAILY_GOAL : 0);
      const heartMultiplier = 1 + cutenessLvl * 0.5;


      // Soft pink, sparkly loading splash while the cloud connects.
      if (booting) return (
        <div className={`min-h-screen ${neutralMode ? 'neutral-mode bg-[#f5f5f7]' : 'bg-main'} flex flex-col items-center justify-center gap-6`}>
          {toastMessage && <div className="fixed top-8 left-1/2 -translate-x-1/2 bg-white/95 backdrop-blur text-pink-500 px-6 py-3 rounded-full shadow-glow-lg border border-pink-50 z-50 font-bold text-center animate-slide-up">{toastMessage}</div>}
          <div className="relative w-16 h-16 flex items-center justify-center">
            <div className="loader-ring" />
            {neutralMode ? <Circle size={26} className="absolute text-[#007AFF] loader-pulse" /> : <Heart size={26} className="absolute fill-pink-400 text-pink-400 loader-pulse" />}
          </div>
          <div className="flex items-center gap-2 text-pink-400 font-bold tracking-widest text-xs uppercase">
            {!neutralMode && <Sparkles size={16} className="text-pink-300 animate-pulse" />}
            {neutralMode ? 'Getting things ready…' : 'Getting pretty…'}
            {!neutralMode && <Sparkles size={16} className="text-pink-300 animate-pulse" />}
          </div>
        </div>
      );

      if (!user) return (
        <div className={`min-h-screen ${neutralMode ? 'neutral-mode bg-[#f5f5f7]' : 'bg-main'} flex flex-col items-center justify-center p-4 font-sans`}>
          {toastMessage && <div className="fixed top-8 left-1/2 -translate-x-1/2 bg-white/95 backdrop-blur text-pink-500 px-6 py-3 rounded-full shadow-glow-lg border border-pink-50 z-50 font-bold text-center animate-slide-up">{toastMessage}</div>}
          <div className="signin-card p-9 rounded-[2rem] text-center max-w-sm w-full relative overflow-hidden">
            {!neutralMode && <div className="absolute inset-0 pointer-events-none" style={{background:'radial-gradient(circle at 60% 0%, rgba(244,114,182,0.08) 0%, transparent 70%)'}}/>}
            <div className="icon-badge w-16 h-16 mx-auto mb-6 rounded-[22px] flex items-center justify-center">
              {neutralMode ? <Circle className="text-white" size={30} /> : <Heart className="text-white fill-white drop-shadow-sm" size={30} />}
            </div>
            <h2 className={`text-2xl font-bold mb-3 ${neutralMode ? 'text-slate-800' : 'text-pink-500'}`}>
              {neutralMode ? 'Sign In' : 'Welcome Back ✨'}
            </h2>
            <p className={`mb-8 text-sm leading-relaxed ${neutralMode ? 'text-slate-500' : 'text-slate-500'}`}>
              {neutralMode ? 'Sign in to access and sync your data.' : 'Sign in with Google to sync your tasks and progress across all your devices.'}
            </p>
            <button
              onClick={() => {
                const provider = new fbAPI.GoogleAuthProvider();
                fbAPI.signInWithPopup(authRef.current, provider).catch(e => {
                  if (e.code !== 'auth/popup-closed-by-user') showToast(e.message);
                });
              }}
              className={`w-full py-4 rounded-2xl font-bold text-base tracking-wide transition-all active:scale-95 flex items-center justify-center gap-3 ${neutralMode ? 'bg-[#007AFF] text-white shadow-md' : 'btn-gradient text-white'}`}
            >
              <span className="bg-white rounded-lg p-1 flex items-center justify-center shadow-sm"><GoogleLogo size={18} /></span>
              Continue with Google
            </button>
            <div className="mt-5 text-[10px] font-mono text-slate-400 relative">build {window.APP_VERSION}</div>
          </div>
        </div>
      );

      // Signed in, but waiting for the stats doc so we know whether a PIN exists.
      if (user && !statsReady) return (
        <div className={`min-h-screen ${neutralMode ? 'neutral-mode bg-[#f5f5f7]' : 'bg-main'} flex flex-col items-center justify-center gap-6`}>
          <div className="relative w-16 h-16 flex items-center justify-center">
            <div className="loader-ring" />
            {neutralMode ? <Lock size={24} className="absolute text-[#007AFF] loader-pulse" /> : <Lock size={24} className="absolute text-pink-400 loader-pulse" />}
          </div>
        </div>
      );

      // PIN privacy gate — create one on first sign-in, then require it each session.
      if (user && !pinUnlocked) return (
        <PinScreen
          mode={(pinHash && !forcePinSetup) ? 'enter' : 'create'}
          neutralMode={neutralMode}
          userEmail={user.email}
          onSignOut={() => {
            fbAPI.signOut(authRef.current).then(() => {
              setUser(null); setPinUnlocked(false); setStatsReady(false); setForcePinSetup(false);
              setTasks([]); setNotes([]);
            });
          }}
          onComplete={async (pin) => {
            const h = await hashPin(pin, user.uid);
            if (pinHash && !forcePinSetup) {
              if (h === pinHash) { setPinUnlocked(true); return true; }
              return false;
            }
            await saveStats({ pinHash: h });
            setPinHash(h);
            setForcePinSetup(false);
            setPinUnlocked(true);
            return true;
          }}
        />
      );


      return (
        <div style={isGGTok ? undefined : mainBgStyle} className={`min-h-screen transition-colors duration-1000 overflow-x-hidden antialiased ${neutralMode ? 'neutral-mode' : ''} ${isGGTok ? 'bg-[#2a1a2e] flex flex-col md:flex-row justify-start md:justify-center items-center md:items-start gap-4 md:gap-8 p-2 md:p-6' : 'bg-main relative p-2 md:p-4'}`}>
          {/* GGTok Live Panel — reusable module (ggtok.js) */}
          {isGGTok && <GGTok.GGTokPanel state={ggtok} onClose={() => setIsGGTok(false)} onSendMessage={handleUserChat} />}

          {/* Loud sync-failure banner. Almost always permission-denied from expired or
              locked Firestore security rules → nothing reads or writes. */}
          {syncError && (
            <div className="fixed top-0 left-0 right-0 z-[100] bg-red-500 text-white px-4 py-3 shadow-lg flex items-start gap-3 text-sm font-sans">
              <span className="text-lg leading-none">⚠️</span>
              <div className="flex-1 min-w-0">
                <div className="font-bold">Not syncing — your changes aren't saving to the cloud.</div>
                <div className="text-red-100 text-xs mt-0.5 break-words">
                  {syncError.code === 'permission-denied'
                    ? 'Firestore denied access (permission-denied). The gg-to-do database security rules are blocking reads/writes — they likely expired. Fix: Firebase Console → Firestore → Rules.'
                    : `${syncError.code}: ${syncError.message}`}
                </div>
                <button onClick={() => copyToClipboard(`code: ${syncError.code}\nwhere: ${syncError.where}\nmsg: ${syncError.message}\nuid: ${user?.uid}\nbuild: ${window.APP_VERSION}`, 'Error details copied!')} className="text-xs underline mt-1 text-white/90">copy error details</button>
              </div>
              <button onClick={() => setSyncError(null)} className="shrink-0 text-white/80 hover:text-white"><XIcon size={18}/></button>
            </div>
          )}

          {/* Always-visible diagnostic strip. Compare across devices: if the BUILD
              differs, the device is on a stale cached build (the usual iOS Safari
              cause); if the UID differs, it's a different Google account. */}
          <div onClick={() => copyToClipboard(`build: ${window.APP_VERSION}\nproject: gg-to-do\nemail: ${user?.email}\nuid: ${user?.uid}`, 'Sync info copied!')}
               className="fixed bottom-1 left-1/2 -translate-x-1/2 z-[90] bg-black/60 text-white/90 text-[10px] font-mono px-2.5 py-1 rounded-full cursor-pointer max-w-[94vw] truncate text-center"
               title="Tap to copy. Compare the email + build across your phone and desktop.">
            b{window.APP_VERSION} · {user ? user.email : 'signed out'} · {user ? user.uid.slice(0, 4) : '—'}
          </div>

          {/* Praise timeline — a fake "Twitter/X" feed that says nice things about you (task #8) */}
          <div className="hidden xl:flex fixed right-4 top-4 bottom-4 w-80 z-30 flex-col card-glass rounded-3xl shadow-glow-lg border border-white/60 overflow-hidden">
            <div className="flex items-center gap-2 px-4 py-3 border-b border-pink-100 shrink-0">
              <div className="w-8 h-8 rounded-full bg-gradient-to-br from-pink-400 to-rose-400 flex items-center justify-center text-white font-black">✦</div>
              <div className="leading-tight">
                <div className="font-cute-display font-extrabold text-slate-700 text-sm">Your Hype Feed</div>
                <div className="text-[10px] text-pink-300 font-bold uppercase tracking-widest">#1 fan account</div>
              </div>
            </div>
            <div className="flex-1 overflow-y-auto px-3 py-3 space-y-3">
              {tweets.map(tw => (
                <div key={tw.id} className="bg-pink-50/50 rounded-2xl p-3 border border-pink-100/70">
                  <div className="flex items-center gap-2 mb-1">
                    <div className="w-7 h-7 rounded-full bg-gradient-to-br from-pink-300 to-purple-300 shrink-0" />
                    <div className="min-w-0">
                      <div className="text-xs font-bold text-slate-700 truncate leading-tight">{tw.name} <span className="text-pink-300 font-medium">{tw.handle}</span></div>
                    </div>
                  </div>
                  <div className="text-sm text-slate-600 leading-snug">{tw.text}</div>
                  <div className="flex items-center gap-4 mt-2 text-[11px] text-pink-300 font-bold">
                    <span>♻ {tw.retweets}</span>
                    <span className="flex items-center gap-1"><Heart size={11} className="fill-pink-300 text-pink-300"/> {tw.likes}</span>
                  </div>
                </div>
              ))}
            </div>
          </div>

          {/* Main App Container */}
          <div className={`w-full relative flex flex-col mx-auto ${isGGTok ? 'max-w-2xl h-[50vh] md:h-[calc(100vh-3rem)] overflow-y-auto rounded-3xl bg-[#FFF0F5]/95 shadow-2xl p-4 md:p-6 ring-1 ring-pink-200/60' : 'max-w-2xl h-full'}`}>
          <style dangerouslySetInnerHTML={{__html: `
            @keyframes floatUp { 0% { transform: translateY(0); opacity: 0; } 10% { opacity: 1; } 100% { transform: translateY(-100px); opacity: 0; } }
            @keyframes burst { 0% { transform: translate(0,0) scale(0.5); opacity: 1; } 100% { transform: translate(var(--tx), var(--ty)) scale(1.5); opacity: 0; } }
            @keyframes shower { 0% { transform: translateY(-50px); opacity: 1; } 100% { transform: translateY(100vh); opacity: 0; } }
            @keyframes pop { 0% { transform: scale(0.5); opacity: 0; } 50% { transform: scale(1.2); opacity: 1; } 100% { transform: scale(1); opacity: 1; } }
            @keyframes flash { 0% { opacity: 0; transform: translateY(5px); } 50% { opacity: 0.5; } 100% { opacity: 0; transform: translateY(-15px); } }
            .animate-float { animation: floatUp 2s forwards; }
            .animate-burst { animation: burst 1.2s forwards; }
            .animate-shower { animation: shower 2.5s linear forwards; }
            .animate-good-girl { animation: pop 0.4s forwards; }
            .animate-subliminal { animation: flash 0.6s forwards; }
            @keyframes subFlash { 0% { opacity: 0; transform: translate(-50%,-50%) scale(0.92); } 18% { opacity: 0.92; } 70% { opacity: 0.85; } 100% { opacity: 0; transform: translate(-50%,-50%) scale(1.08); } }
            .subliminal-flash { animation: subFlash 1.1s ease-in-out forwards; }
          `}} />
          
          {toastMessage && <div className="fixed top-8 left-1/2 -translate-x-1/2 bg-white/95 backdrop-blur text-pink-500 px-6 py-3 rounded-full shadow-glow-lg border border-pink-50 z-50 font-bold text-center animate-slide-up">{toastMessage}</div>}

          {/* Subliminal goal flash — a quick whisper of a goal phrase across the screen (task #12) */}
          {subliminal && (
            <div key={subliminal.id} className="fixed left-1/2 top-1/2 z-[80] pointer-events-none subliminal-flash select-none">
              <div className="font-cute-display text-5xl md:text-7xl font-extrabold tracking-tight text-pink-400/70 drop-shadow-[0_2px_24px_rgba(244,114,182,0.45)] whitespace-nowrap">{subliminal.text}</div>
            </div>
          )}

          {/* Sparkly confetti for streak milestones */}
          {confetti && (
            <div key={confetti.id} className="fixed inset-0 pointer-events-none z-[70]">
              {[...Array(70)].map((_, i) => {
                const colors = ['#f472b6','#fb7185','#f9a8d4','#c084fc','#fbcfe8','#fda4af','#fcd34d'];
                return <div key={i} className="confetti-piece" style={{ left: `${Math.random()*100}vw`, background: colors[i % colors.length], animationDuration: `${2 + Math.random()*1.3}s`, animationDelay: `${Math.random()*0.6}s`, transform: `scale(${0.7 + Math.random()*0.8})` }} />;
              })}
            </div>
          )}

          {/* Soft-glowing daily login bonus */}
          {loginBonus && (
            <div className="fixed top-20 left-1/2 -translate-x-1/2 z-[65] signin-card rounded-3xl px-7 py-5 bonus-glow text-center animate-slide-up">
              <div className="text-3xl mb-1">🎀✨</div>
              <div className="font-cute-display text-xl font-extrabold text-pink-500">Daily Login Bonus!</div>
              <div className="text-pink-400 font-bold mt-1 flex items-center justify-center gap-1">+{loginBonus.hearts} <Heart size={16} className="fill-pink-400 text-pink-400" /></div>
              <div className="text-[11px] uppercase tracking-widest font-bold text-pink-300 mt-1">🔥 {loginBonus.streak}-day streak — good girl!</div>
            </div>
          )}

          {/* Random affirmation pop-up */}
          {affirmation && (
            <div key={affirmation.id} className="fixed bottom-24 right-6 z-[65] affirmation-pop card-glass border border-white/60 shadow-glow-lg rounded-2xl px-5 py-3 flex items-center gap-2 max-w-[80vw]">
              <Sparkles size={18} className="text-pink-300 shrink-0" />
              <span className="font-bold text-pink-500">{affirmation.text}</span>
            </div>
          )}

          {/* Gentle in-app reminder */}
          {reminder && (
            <div key={reminder.id} className="fixed bottom-24 left-1/2 -translate-x-1/2 z-[65] animate-slide-up bg-gradient-to-r from-pink-400 to-rose-400 text-white shadow-glow-lg rounded-full px-5 py-3 flex items-center gap-2 max-w-[88vw]">
              <Heart size={16} className="fill-white shrink-0" />
              <span className="font-bold text-sm truncate">{reminder.text}</span>
            </div>
          )}

          {/* Floating heart particles on check-off */}
          {particles && (
            <div key={particles.id} className="pointer-events-none">
              {[...Array(8)].map((_, i) => {
                const a = (Math.PI * 2 * i) / 8 + Math.random() * 0.5; const d = 36 + Math.random() * 38;
                return <Heart key={i} size={14 + Math.random()*10} className="heart-particle fill-pink-400 text-pink-400" style={{ left: particles.x, top: particles.y, '--px': `${Math.cos(a)*d}px`, '--py': `${Math.sin(a)*d - 30}px`, animationDelay: `${i*0.02}s` }} />;
              })}
            </div>
          )}

          {rewardAnim && (
            <div className="fixed z-50 pointer-events-none text-center" style={{ left: rewardAnim.x - 100, top: rewardAnim.y - 80, width: 200 }}>
              <div className={`text-pink-500 text-3xl mb-2 animate-good-girl ${rewardAnim.fontClass || activeFontClass}`}>{(rewardAnim.praiseText || activePraiseData.text).toUpperCase()}!</div>
              <div className="relative h-20">
                {[...Array(rewardAnim.heartsAmount * (rewardAnim.type === 'shower' ? 3 : 1))].map((_, i) => {
                  if (rewardAnim.type === 'burst') {
                    const a = Math.random()*Math.PI*2; const d = 70+Math.random()*70;
                    return <Heart key={i} className="absolute fill-pink-400 text-pink-400 animate-burst left-1/2 top-1/2" size={24} style={{ '--tx': `${Math.cos(a)*d}px`, '--ty': `${Math.sin(a)*d}px`, animationDelay: `${i*0.05}s` }} />
                  }
                  if (rewardAnim.type === 'shower') return <Heart key={i} className="fixed fill-pink-400 text-pink-400 animate-shower" size={20} style={{ left: `${Math.random()*100}vw`, top: '-20px', animationDelay: `${Math.random()*1}s` }} />
                  return <Heart key={i} className="absolute fill-pink-400 text-pink-400 animate-float" size={24} style={{ left: `${40+Math.random()*20}%`, animationDelay: `${i*0.1}s` }} />
                })}
              </div>
            </div>
          )}

          {view === 'inbox' && (
            <div className="pt-8 pb-20">
              {/* Profile bar: avatar (cuter with streak), streak counter, heart-shaped daily progress, gallery/stats */}
              <div className="flex flex-wrap items-center gap-x-3 gap-y-2 mb-6 card-glass rounded-3xl p-3 md:p-4 shadow-glow border border-white/60">
                <Avatar emoji={avatarData.emoji} level={cutenessLvl} onClick={() => setShowStats(true)} />
                <div className="flex flex-col min-w-0 flex-1">
                  <div className="font-cute-display font-extrabold text-pink-500 leading-tight truncate">{CUTENESS_LABELS[cutenessLvl]}{neutralMode ? '' : ' girl'}</div>
                  <div className="flex flex-wrap items-center gap-1 text-[11px] font-bold text-pink-300">
                    <span>🔥 {streak}d streak</span>
                    {heartMultiplier > 1 && <span className="bg-pink-100 text-pink-500 px-1.5 py-0.5 rounded-full">×{heartMultiplier.toFixed(1)} hearts</span>}
                  </div>
                </div>
                <div className="ml-auto shrink-0 flex items-center gap-2">
                  <HeartProgress progress={dailyProgress} />
                  <button onClick={() => setShowGallery(true)} title="Reward Gallery" className="card-glass text-pink-400 p-2.5 rounded-full shadow-glow border border-white/60 hover:bg-pink-50/60 transition-all duration-200"><Sparkles size={20} /></button>
                  <button onClick={() => setShowGoals(true)} title="Goals" className="card-glass text-pink-400 p-2.5 rounded-full shadow-glow border border-white/60 hover:bg-pink-50/60 transition-all duration-200"><Target size={20} /></button>
                  <button onClick={() => setView('matrix')} title="Priority Matrix" className="card-glass text-pink-400 p-2.5 rounded-full shadow-glow border border-white/60 hover:bg-pink-50/60 transition-all duration-200"><Grid size={20} /></button>
                  <button onClick={() => { const v = !neutralMode; setNeutralMode(v); if (user && db && fbAPI) saveStats({ neutralMode: v }); showToast(v ? 'Gender-neutral mode on' : 'Girly mode on 💖'); }} title={neutralMode ? 'Switch to girly theme' : 'Switch to gender-neutral theme'} className={`p-2.5 rounded-full shadow-glow border transition-colors ${neutralMode ? 'bg-[#007AFF] text-white border-transparent' : 'card-glass text-pink-400 border-white/60 hover:bg-pink-50/60 transition-all duration-200'}`}><Contrast size={20} /></button>
                  <button onClick={() => setShowStats(true)} title="Stats" className="card-glass text-pink-400 p-2.5 rounded-full shadow-glow border border-white/60 hover:bg-pink-50/60 transition-all duration-200"><Star size={20} /></button>
                </div>
              </div>
              <div className="flex flex-wrap justify-center md:justify-between items-center mb-8 gap-2">
                <button onClick={() => setView('shop')} className="relative card-glass text-pink-400 p-3 rounded-full shadow-glow border border-white/60 hover:bg-pink-50/60 transition-all duration-200">
                  <ShoppingBag size={24} />
                  <span className="absolute -top-1 -right-1 bg-pink-400 text-white text-[10px] font-bold px-1.5 py-0.5 rounded-full">{kissElo}</span>
                </button>
                <div className="flex items-center gap-2 card-glass px-5 py-2 rounded-full shadow-glow border border-white/60">
                  <div className="text-xl font-bold text-pink-400">{dailyHearts}</div>
                  <Heart size={20} className="fill-pink-400 text-pink-400" />
                </div>
                
                <button onClick={() => setView('notes')} className="card-glass p-3 rounded-full shadow-glow border border-white/60 hover:bg-pink-50/60 transition-all duration-200 text-pink-400 flex items-center justify-center relative group">
                  <FileText size={22} />
                  {notes.length > 0 && <span className="absolute -top-1 -right-1 bg-pink-400 text-white text-[10px] font-bold px-1.5 py-0.5 rounded-full">{notes.length}</span>}
                  <span className="absolute -bottom-8 bg-slate-800 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-50">Notes</span>
                </button>

                <button onClick={() => setView('history')} className="card-glass p-3 rounded-full shadow-glow border border-white/60 hover:bg-pink-50/60 transition-all duration-200 text-pink-400 flex items-center justify-center relative group">
                  <History size={22} />
                  {tasks.filter(t => t.status === 'completed').length > 0 && <span className="absolute -top-1 -right-1 bg-pink-400 text-white text-[10px] font-bold px-1.5 py-0.5 rounded-full">{tasks.filter(t => t.status === 'completed').length}</span>}
                  <span className="absolute -bottom-8 bg-slate-800 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-50">History</span>
                </button>

                <button onClick={() => setShowHelp(true)} className="card-glass p-3 rounded-full shadow-glow border border-white/60 hover:bg-pink-50/60 transition-all duration-200 text-pink-400 flex items-center justify-center relative group">
                  <span className="text-lg font-black leading-none">?</span>
                  <span className="absolute -bottom-8 bg-slate-800 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-50">Help (Ctrl+/)</span>
                </button>

                <button onClick={() => setIsGGTok(!isGGTok)} className="card-glass p-3 rounded-full shadow-glow border border-white/60 hover:bg-pink-50/60 transition-all duration-200 text-pink-400 flex items-center justify-center relative group">
                  <div className={`absolute top-0 right-0 w-3 h-3 rounded-full border-2 border-white ${isGGTok ? 'bg-green-400' : 'bg-red-400 animate-pulse'}`}></div>
                  <span className="text-xl leading-none">🎬</span>
                  <span className="absolute -bottom-8 bg-slate-800 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-50">GGTok Mode</span>
                </button>

                <button onClick={() => {
                  let act = tasks.filter(t => t.status === 'open');
                  if (act.length < 2) return;
                  act.sort(() => Math.random() - 0.5); act.sort((a,b) => b.score - a.score);
                  let p = []; for(let i=0; i<act.length; i+=2) if(i+1<act.length) p.push([act[i], act[i+1]]); else p.push([act[i], null]);
                  setTournament({...tournament, participants: act.map(t=>t.id), pairs: p, currentPairIndex: 0, round: 1, isTransitioning: false});
                  setView('tournament_play');
                }} className="btn-gradient px-4 py-2 rounded-full font-bold flex items-center gap-2"><Trophy size={18}/> Play <Heart size={14} className="fill-white" /></button>
              </div>

              <div className="relative card-glass rounded-[1.5rem] capture-glow border border-white/60 mb-4" style={{padding:'2px'}}>
                <input type="text" value={inputValue} onChange={e => setInputValue(e.target.value)} onKeyDown={handleCapture} placeholder="What's on your mind? #tags" className="w-full bg-transparent px-6 py-4 text-lg outline-none placeholder:text-pink-200 rounded-[1.5rem]" />
                {captureAnim.active && <div className="absolute right-6 top-1/2 -translate-y-1/2 animate-subliminal text-pink-300 flex items-center gap-2"><span className={`text-[10px] uppercase font-bold ${captureAnim.fontClass || activeFontClass}`}>{(captureAnim.praiseText || activePraiseData.text).toLowerCase()}</span><Heart size={12} className="fill-pink-300" /></div>}
              </div>

              {/* Category picker — applied to the next captured task */}
              <div className="flex flex-wrap gap-2 mb-6 px-1">
                {CATEGORIES.map(c => (
                  <button key={c.id} onClick={() => setCaptureCategory(captureCategory === c.id ? null : c.id)}
                    className={`px-3 py-1.5 rounded-full text-xs font-bold transition-all border ${captureCategory === c.id ? `${c.cls} border-transparent shadow-md scale-105` : 'bg-white/70 text-pink-300 border-pink-100 hover:bg-pink-50'}`}>
                    {c.emoji} {c.name}
                  </button>
                ))}
              </div>

              {/* Search & Filter Bar */}
              {(() => {
                const uniqueTags = [...new Set(tasks.filter(t => t.status === 'open').flatMap(t => t.tags || []))];
                const uniqueContexts = [...new Set(tasks.filter(t => t.status === 'open').flatMap(t => t.contexts || []))];
                const filterTasks = (taskList) => taskList.filter(t => {
                  if (searchQuery && !t.text.toLowerCase().includes(searchQuery.toLowerCase())) return false;
                  if (filterTag && !(t.tags || []).includes(filterTag)) return false;
                  if (filterContext && !(t.contexts || []).includes(filterContext)) return false;
                  return true;
                });
                const rankedTasks = filterTasks(tasks.filter(t => t.status === 'open' && t.matchesPlayed > 0)).sort((a,b)=>b.score-a.score);
                const inboxTasks = filterTasks(tasks.filter(t => t.status === 'open' && t.matchesPlayed === 0)).sort((a,b)=>(b.order||b.createdAt)-(a.order||a.createdAt));
                const allOpenCount = filterTasks(tasks.filter(t => t.status === 'open')).length;
                
                return (
                  <>
                    <div className="mb-8 space-y-3">
                      <div className="flex gap-2">
                        <input type="text" value={searchQuery} onChange={e => setSearchQuery(e.target.value)} placeholder="Search tasks..." className="flex-1 card-glass border border-white/60 rounded-full px-5 py-3 text-sm outline-none focus:border-pink-200 shadow-glow" />
                      </div>
                      {(uniqueTags.length > 0 || uniqueContexts.length > 0) && (
                        <div className="flex flex-wrap gap-2 px-1">
                          {uniqueContexts.map(ctx => (
                            <button key={ctx} onClick={() => setFilterContext(filterContext === ctx ? '' : ctx)} className={`px-3 py-1 rounded-full text-xs font-bold transition-colors ${filterContext === ctx ? 'bg-sky-500 text-white shadow-md' : 'bg-sky-50 text-sky-500 hover:bg-sky-100'}`}>
                              {ctx}
                            </button>
                          ))}
                          {uniqueTags.map(tag => (
                            <button key={tag} onClick={() => setFilterTag(filterTag === tag ? '' : tag)} className={`px-3 py-1 rounded-full text-xs font-bold transition-colors ${filterTag === tag ? 'bg-pink-400 text-white shadow-md' : 'bg-pink-50 text-pink-400 hover:bg-pink-100'}`}>
                              {tag}
                            </button>
                          ))}
                        </div>
                      )}
                    </div>

                    <div className="space-y-10">
                      {rankedTasks.length > 0 && (
                        <section>
                          <h2 className="ribbon-header text-xs font-bold text-white uppercase mb-4 tracking-widest">Ranked</h2>
                          <div className="space-y-3">{rankedTasks.map(t => <TaskRow key={t.id} task={t} IconCmp={getIconCmp(iconForTask(t))} onComplete={e => completeTask(t, e)} onClick={() => setSelectedTask(t)} onCopy={() => copyToClipboard(`${t.text}${t.body ? '\n\n' + t.body : ''}`, 'Task copied!')} />)}</div>
                        </section>
                      )}
                      <section>
                        <h2 className="ribbon-header text-xs font-bold text-white uppercase mb-4 tracking-widest">Inbox</h2>
                        <div className="space-y-3">
                          {inboxTasks.map(t => <TaskRow key={t.id} task={t} IconCmp={getIconCmp(iconForTask(t))} onComplete={e => completeTask(t, e)} onClick={() => setSelectedTask(t)} onCopy={() => copyToClipboard(`${t.text}${t.body ? '\n\n' + t.body : ''}`, 'Task copied!')} draggable={true} onDragStart={e => handleDragStart(e, t)} onDragEnd={handleDragEnd} onDragOver={handleDragOver} onDrop={e => handleDrop(e, t)} />)}
                          {allOpenCount === 0 && <div className="text-center py-10 text-pink-200 font-medium border-2 border-dashed border-pink-100 rounded-3xl">Beautifully empty.</div>}
                        </div>
                      </section>
                    </div>
                  </>
                );
              })()}
            </div>
          )}

          {view === 'notes' && (
            <div className="pt-8 pb-20">
              <div className="flex justify-between items-center mb-8 gap-2">
                <button onClick={() => setView('inbox')} className="card-glass text-pink-400 p-3 rounded-full shadow-glow border border-white/60 hover:bg-pink-50/60 transition-all duration-200 flex items-center gap-1 pr-4"><ChevronLeft size={22}/> <span className="font-bold text-sm">Tasks</span></button>
                <h1 className="ribbon-header text-sm font-bold text-white uppercase tracking-widest">My Notes</h1>
                <button onClick={createNote} className="btn-gradient p-3 rounded-full flex items-center gap-1 pr-4"><Plus size={22}/> <span className="font-bold text-sm">New</span></button>
              </div>

              <div className="space-y-3">
                {notes.length === 0 && (
                  <div className="text-center py-16 text-pink-200 font-medium border-2 border-dashed border-pink-100 rounded-3xl">
                    {neutralMode ? 'No notes yet.' : 'No notes yet, darling.'}<br/>Tap <span className="font-bold text-pink-300">New</span> to start writing. ✨
                  </div>
                )}
                {notes.map(n => (
                  <div key={n.id} onClick={() => setSelectedNote(n)} className="group flex items-center gap-4 card-glass p-4 rounded-2xl shadow-glow hover:shadow-glow-lg border border-white/60 hover:border-pink-200 cursor-pointer transition-all duration-200 hover:-translate-y-0.5"
                       title={`Created: ${new Date(n.createdAt || n.modifiedAt).toLocaleString()}\nModified: ${new Date(n.modifiedAt).toLocaleString()}`}>
                    <div className="bg-pink-50 p-3 rounded-2xl shrink-0"><FileText size={20} className="text-pink-400"/></div>
                    <div className="flex-1 min-w-0">
                      <div className="truncate text-lg font-bold text-slate-700">{n.title || 'Untitled'}</div>
                      <div className="truncate text-sm text-slate-400">{(n.content || '').split('\n')[0] || 'Empty note'}</div>
                      <div className="text-[10px] uppercase tracking-widest font-bold text-pink-300 mt-1">Modified {new Date(n.modifiedAt).toLocaleString()}</div>
                    </div>
                    <button onClick={e => { e.stopPropagation(); copyToClipboard(`${n.title}\n\n${n.content || ''}`, 'Note copied!'); }} className="text-pink-200 hover:text-pink-500 transition-colors opacity-0 group-hover:opacity-100 shrink-0"><Copy size={20}/></button>
                  </div>
                ))}
              </div>
            </div>
          )}

          {view === 'history' && (
            <div className="pt-8 pb-20">
              <div className="flex justify-between items-center mb-6 gap-2">
                <button onClick={() => setView('inbox')} className="card-glass text-pink-400 p-3 rounded-full shadow-glow border border-white/60 hover:bg-pink-50/60 transition-all duration-200 flex items-center gap-1 pr-4"><ChevronLeft size={22}/> <span className="font-bold text-sm">Tasks</span></button>
                <h1 className="ribbon-header text-sm font-bold text-white uppercase tracking-widest flex items-center gap-2"><History size={16}/> History</h1>
                <div className="w-12" />
              </div>
              {(() => {
                const done = tasks.filter(t => t.status === 'completed');
                if (done.length === 0) return (
                  <div className="text-center py-16 text-pink-200 font-medium border-2 border-dashed border-pink-100 rounded-3xl">
                    {neutralMode ? 'Nothing completed yet.' : 'No finished tasks yet, darling.'}<br/>Complete a task and it'll show up here. ✨
                  </div>
                );
                // Order by completion time (fall back to createdAt for older tasks
                // saved before completedAt existed).
                const stamp = t => t.completedAt || t.createdAt || 0;
                done.sort((a, b) => stamp(b) - stamp(a));
                // Bucket into friendly date groups.
                const startOf = d => { const x = new Date(d); x.setHours(0,0,0,0); return x.getTime(); };
                const today = startOf(Date.now());
                const yesterday = today - 86400000;
                const weekAgo = today - 6 * 86400000;
                const groups = { Today: [], Yesterday: [], 'This Week': [], Earlier: [] };
                done.forEach(t => {
                  const d = startOf(stamp(t));
                  if (d >= today) groups.Today.push(t);
                  else if (d >= yesterday) groups.Yesterday.push(t);
                  else if (d >= weekAgo) groups['This Week'].push(t);
                  else groups.Earlier.push(t);
                });
                const totalHeartsEarned = done.reduce((s, t) => s + (t.earnedHearts || getDisplayHearts(t)), 0);
                return (
                  <div className="space-y-6">
                    <div className="flex gap-3">
                      <div className="flex-1 card-glass rounded-2xl p-4 shadow-glow border border-white/60 text-center">
                        <div className="text-2xl font-bold text-pink-500">{done.length}</div>
                        <div className="text-[10px] uppercase tracking-widest font-bold text-pink-300 mt-1">Completed</div>
                      </div>
                      <div className="flex-1 card-glass rounded-2xl p-4 shadow-glow border border-white/60 text-center">
                        <div className="text-2xl font-bold text-pink-500 flex items-center justify-center gap-1">{totalHeartsEarned} <Heart size={18} className="fill-pink-400 text-pink-400"/></div>
                        <div className="text-[10px] uppercase tracking-widest font-bold text-pink-300 mt-1">Hearts Earned</div>
                      </div>
                    </div>
                    {Object.entries(groups).map(([label, items]) => items.length === 0 ? null : (
                      <div key={label}>
                        <h3 className="text-xs font-bold text-pink-300 uppercase mb-3 tracking-widest px-1">{label} · {items.length}</h3>
                        <div className="space-y-2">
                          {items.map(t => {
                            const cat = getCategory(t.category);
                            return (
                              <div key={t.id} className="group flex items-center gap-3 card-glass p-3.5 rounded-2xl shadow-glow border border-white/60">
                                <div className="w-9 h-9 rounded-full bg-gradient-to-br from-pink-100 to-rose-100 flex items-center justify-center shrink-0"><CheckCircle size={18} className="text-pink-400"/></div>
                                <div className="flex-1 min-w-0">
                                  <div className="truncate text-slate-500 line-through">{t.text}</div>
                                  <div className="flex items-center gap-2 mt-0.5 flex-wrap">
                                    <span className="text-[10px] font-bold text-pink-300">{t.completedAt ? new Date(t.completedAt).toLocaleString([], { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : 'Completed'}</span>
                                    {cat && <span className={`text-[10px] uppercase font-bold px-2 py-0.5 rounded-full ${cat.cls}`}>{cat.emoji} {cat.name}</span>}
                                  </div>
                                </div>
                                <HeartDisplay count={t.earnedHearts ? Math.min(5, t.earnedHearts) : getDisplayHearts(t)} isStale={false} />
                                <button onClick={() => copyToClipboard(`${t.text}${t.body ? '\n\n' + t.body : ''}`, 'Task copied!')} className="text-pink-200 hover:text-pink-500 transition-colors opacity-0 group-hover:opacity-100 shrink-0" title="Copy"><Copy size={18}/></button>
                                <button onClick={() => restoreTask(t)} className="text-pink-200 hover:text-pink-500 transition-colors shrink-0" title="Restore to inbox"><History size={18}/></button>
                              </div>
                            );
                          })}
                        </div>
                      </div>
                    ))}
                  </div>
                );
              })()}
            </div>
          )}

          {view === 'matrix' && (
            <div className="pt-8 pb-20">
              <div className="flex justify-between items-center mb-6 gap-2">
                <button onClick={() => setView('inbox')} className="card-glass text-pink-400 p-3 rounded-full shadow-glow border border-white/60 hover:bg-pink-50/60 transition-all duration-200 flex items-center gap-1 pr-4"><ChevronLeft size={22}/> <span className="font-bold text-sm">Tasks</span></button>
                <h1 className="ribbon-header text-sm font-bold text-white uppercase tracking-widest flex items-center gap-2"><Grid size={16}/> Priority Matrix</h1>
                <div className="w-12" />
              </div>
              {(() => {
                const open = tasks.filter(t => t.status === 'open');
                return (
                  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                    {QUADRANTS.map(q => {
                      const items = open.filter(t => classifyQuadrant(t) === q.id);
                      return (
                        <div key={q.id} className={`rounded-3xl border ${q.cls} p-4 min-h-[180px]`}>
                          <div className="flex items-center gap-2 mb-3">
                            <span className={`w-2.5 h-2.5 rounded-full ${q.tag}`} />
                            <h3 className="font-bold text-slate-700">{q.title}</h3>
                            <span className="text-[10px] uppercase tracking-wide font-bold text-slate-400">{q.sub}</span>
                          </div>
                          <div className="space-y-2">
                            {items.length === 0 && <div className="text-xs text-slate-300 font-medium py-4 text-center">Nothing here.</div>}
                            {items.map(t => (
                              <div key={t.id} className="card-glass rounded-2xl px-3 py-2 flex items-center gap-2 shadow-glow"
                                   title={`Created: ${new Date(t.createdAt).toLocaleString()}${t.dueDate ? `\nDue: ${new Date(t.dueDate).toLocaleDateString()}` : ''}`}>
                                <button onClick={e => completeTask(t, e)} className="text-pink-300 hover:text-pink-500 shrink-0" title="Complete"><Heart size={16}/></button>
                                <span className="flex-1 min-w-0 truncate text-sm text-slate-700">{t.text}</span>
                                <select value={classifyQuadrant(t)} onChange={e => setQuadrant(t.id, e.target.value)} className="text-[11px] bg-transparent text-slate-400 font-bold outline-none cursor-pointer">
                                  {QUADRANTS.map(o => <option key={o.id} value={o.id}>{o.title}</option>)}
                                </select>
                              </div>
                            ))}
                          </div>
                        </div>
                      );
                    })}
                  </div>
                );
              })()}
            </div>
          )}

          {view === 'tournament_setup' && (
            <div className="fixed inset-0 bg-white/90 z-50 flex items-center justify-center p-4">
              <div className="modal-card rounded-[2rem] p-5 md:p-8 w-full max-w-md flex flex-col max-h-[80vh]">
                <div className="flex justify-between items-center mb-6"><h2 className="text-2xl font-bold">Contenders</h2><button onClick={() => setView('inbox')}><XIcon size={24}/></button></div>
                <div className="flex-1 overflow-y-auto space-y-2 mb-6">
                  {tasks.filter(t => t.status === 'open').map(t => (
                    <label key={t.id} className="flex items-center gap-4 p-3 rounded-xl hover:bg-pink-50 cursor-pointer">
                      <input type="checkbox" checked={tournament.participants.includes(t.id)} onChange={() => setTournament({...tournament, participants: tournament.participants.includes(t.id) ? tournament.participants.filter(p => p !== t.id) : [...tournament.participants, t.id]})} className="w-5 h-5 accent-pink-400" />
                      <span className="font-medium truncate">{t.text}</span>
                    </label>
                  ))}
                </div>
                <button onClick={() => {
                  let act = tasks.filter(t => tournament.participants.includes(t.id));
                  act.sort(() => Math.random() - 0.5);
                  act.sort((a,b) => b.score - a.score);
                  let p = []; 
                  for(let i=0; i<act.length; i+=2) {
                    if(i+1 < act.length) p.push([act[i], act[i+1]]);
                    else p.push([act[i], null]);
                  }
                  setTournament({...tournament, pairs: p, isTransitioning: false}); setView('tournament_play');
                }} className="btn-gradient w-full font-bold py-4 rounded-2xl flex items-center justify-center gap-2"><Heart size={18} className="fill-white"/> Start</button>
              </div>
            </div>
          )}

          {view === 'tournament_play' && tournament.isTransitioning && (
             <div className="fixed inset-0 z-50 flex flex-col items-center justify-center p-6 text-center bg-white/90 backdrop-blur">
                <h2 className="text-4xl font-black text-pink-400 mb-4 animate-bounce">Round {tournament.round - 1} Complete!</h2>
                <p className="text-pink-300 font-bold mb-8 text-lg">Scores updated.</p>
                <button onClick={() => setTournament({...tournament, isTransitioning: false})} className="bg-pink-400 text-white font-bold px-8 py-4 rounded-full text-xl shadow-xl hover:scale-105 transition-transform">Start Round {tournament.round}</button>
             </div>
          )}

          {view === 'tournament_play' && !tournament.isTransitioning && (
            <div className={`fixed inset-0 z-50 flex flex-col items-center justify-center p-6 text-center ${activeThemeClass}`}>
              <div className="absolute top-8 w-full flex justify-between px-8 text-pink-300 font-bold uppercase text-sm">
                <button onClick={() => setView('inbox')} className="text-pink-400"><XIcon size={20}/></button>
                <span>Round {tournament.round} / {tournament.maxRounds}</span>
                <div className="w-5" />
              </div>
              <h2 className="text-3xl font-bold mb-12">More Important?</h2>
              {tournament.pairs[tournament.currentPairIndex]?.[1] === null ? (
                <div className="flex flex-col items-center justify-center gap-6 w-full max-w-lg">
                  <h3 className="text-2xl font-bold text-pink-400">Odd Task Out!</h3>
                  <button onClick={() => handleMatchupResult('left')} className="w-full card-glass p-10 rounded-3xl shadow-glow-lg border-2 border-white/70 hover:border-pink-300 text-2xl font-bold transition-all hover:-translate-y-1">
                    {tournament.pairs[tournament.currentPairIndex][0].text}
                    <div className="text-sm font-bold text-pink-300 uppercase mt-4 animate-pulse">Give Free Point (Bye)</div>
                  </button>
                </div>
              ) : (
                <div className="flex flex-col md:flex-row gap-6 w-full max-w-4xl">
                  <button onClick={() => handleMatchupResult('left')} className="flex-1 card-glass p-10 rounded-3xl shadow-glow-lg border-2 border-white/70 hover:border-pink-300 text-2xl font-bold transition-all hover:-translate-y-1">{tournament.pairs[tournament.currentPairIndex]?.[0]?.text}</button>
                  <button onClick={() => handleMatchupResult('draw')} className="card-glass px-6 py-3 rounded-full font-bold shadow-glow border border-white/60 hover:bg-pink-50/60 transition-all">Draw</button>
                  <button onClick={() => handleMatchupResult('right')} className="flex-1 card-glass p-10 rounded-3xl shadow-glow-lg border-2 border-white/70 hover:border-pink-300 text-2xl font-bold transition-all hover:-translate-y-1">{tournament.pairs[tournament.currentPairIndex]?.[1]?.text}</button>
                </div>
              )}
            </div>
          )}

          {view === 'shop' && (
            <div className="fixed inset-0 bg-black/10 backdrop-blur-[3px] z-50 flex items-center justify-center p-4">
              <div className="modal-card rounded-[2rem] p-5 md:p-8 w-full max-w-lg flex flex-col max-h-[90vh]">
                <div className="flex justify-between items-center mb-6"><div><h2 className="text-2xl font-bold">Reward Shop</h2><div className="text-pink-500 font-bold">Balance: {totalHearts} ♡</div></div><button onClick={() => setView('inbox')} className="bg-pink-50 p-2 rounded-full"><XIcon size={20}/></button></div>
                <div className="flex-1 overflow-y-auto space-y-8 pr-2 pb-4">
                  
                  <section>
                    <h3 className="text-xs font-bold text-pink-300 uppercase mb-1">Praise Words</h3>
                    <p className="text-[11px] text-pink-300 mb-3">Unlocked affirmations all rotate in automatically. ✨</p>
                    <div className="space-y-3">{PRAISES.map(p => {
                      const owned = unlockedPraises.includes(p.id);
                      return (
                      <div key={p.id} className={`flex justify-between items-center p-4 rounded-2xl border-2 ${owned ? 'border-pink-400 bg-pink-50' : 'border-pink-50'}`}>
                        <div className="font-bold text-lg">{p.text.toUpperCase()}!</div>
                        {owned
                          ? <span className="px-4 py-2 rounded-xl text-sm font-bold bg-pink-400 text-white flex items-center gap-1"><Sparkles size={14}/> In Rotation</span>
                          : <button onClick={() => buyItem(p, 'praise')} className="px-4 py-2 rounded-xl text-sm font-bold bg-pink-50 text-pink-500">{`${p.price} ♡`}</button>}
                      </div>
                    );})}</div>
                  </section>

                  <section>
                    <h3 className="text-xs font-bold text-pink-300 uppercase mb-3">App Themes</h3>
                    <div className="space-y-3">{SHOP_THEMES.map(t => (
                      <div key={t.id} className={`flex justify-between items-center p-4 rounded-2xl border-2 ${activeTheme === t.id ? 'border-pink-400 bg-pink-50' : 'border-pink-50'}`}>
                        <div className="flex items-center gap-3">
                          <div className={`w-6 h-6 rounded-full shadow-inner border border-black/10 ${t.class}`}></div>
                          <div className="font-bold">{t.name}</div>
                        </div>
                        <button onClick={() => buyItem(t, 'theme')} className={`px-4 py-2 rounded-xl text-sm font-bold ${activeTheme === t.id ? 'bg-pink-400 text-white' : 'bg-pink-50 text-pink-500'}`}>{activeTheme === t.id ? 'Active' : unlockedThemes.includes(t.id) ? 'Equip' : `${t.price} ♡`}</button>
                      </div>
                    ))}</div>
                  </section>

                  <section>
                    <h3 className="text-xs font-bold text-pink-300 uppercase mb-3">Completion Icons</h3>
                    <div className="space-y-3">{SHOP_ICONS.map(i => {
                      const IconCmp = getIconCmp(i.id);
                      return (
                        <div key={i.id} className={`flex justify-between items-center p-4 rounded-2xl border-2 ${activeIcon === i.id ? 'border-pink-400 bg-pink-50' : 'border-pink-50'}`}>
                          <div className="flex items-center gap-3">
                            <div className="bg-white p-2 rounded-full shadow-sm"><IconCmp size={18} className="text-pink-400" /></div>
                            <div className="font-bold">{i.name}</div>
                          </div>
                          <button onClick={() => buyItem(i, 'icon')} className={`px-4 py-2 rounded-xl text-sm font-bold ${activeIcon === i.id ? 'bg-pink-400 text-white' : 'bg-pink-50 text-pink-500'}`}>{activeIcon === i.id ? 'Active' : unlockedIcons.includes(i.id) ? 'Equip' : `${i.price} ♡`}</button>
                        </div>
                      )
                    })}</div>
                  </section>

                  <section>
                    <h3 className="text-xs font-bold text-pink-300 uppercase mb-3">Praise Fonts</h3>
                    <div className="space-y-3">{SHOP_FONTS.map(f => (
                      <div key={f.id} className={`flex justify-between items-center p-4 rounded-2xl border-2 ${activeFont === f.id ? 'border-pink-400 bg-pink-50' : 'border-pink-50'}`}>
                        <div className={`text-xl ${f.class}`}>{activePraiseData.text.toUpperCase()}!</div>
                        <button onClick={() => buyItem(f, 'font')} className={`px-4 py-2 rounded-xl text-sm font-bold ${activeFont === f.id ? 'bg-pink-400 text-white' : 'bg-pink-50 text-pink-500'}`}>{activeFont === f.id ? 'Active' : unlockedFonts.includes(f.id) ? 'Equip' : `${f.price} ♡`}</button>
                      </div>
                    ))}</div>
                  </section>

                  <section>
                    <h3 className="text-xs font-bold text-pink-300 uppercase mb-3">Animations</h3>
                    <div className="space-y-3">{SHOP_ANIMS.map(a => (
                      <div key={a.id} className={`flex justify-between items-center p-4 rounded-2xl border-2 ${activeAnim === a.id ? 'border-pink-400 bg-pink-50' : 'border-pink-50'}`}>
                        <div className="font-bold">{a.name}</div>
                        <button onClick={() => buyItem(a, 'anim')} className={`px-4 py-2 rounded-xl text-sm font-bold ${activeAnim === a.id ? 'bg-pink-400 text-white' : 'bg-pink-50 text-pink-500'}`}>{activeAnim === a.id ? 'Active' : unlockedAnims.includes(a.id) ? 'Equip' : `${a.price} ♡`}</button>
                      </div>
                    ))}</div>
                  </section>

                </div>
              </div>
            </div>
          )}

          {/* Reward Gallery — unlock new heart backgrounds with streaks or hearts */}
          {showGallery && (
            <div className="fixed inset-0 bg-black/10 backdrop-blur-[3px] z-50 flex items-center justify-center p-4">
              <div className="modal-card rounded-[2rem] p-5 md:p-8 w-full max-w-lg flex flex-col max-h-[90vh]">
                <div className="flex justify-between items-center mb-2">
                  <div>
                    <h2 className="text-2xl font-bold flex items-center gap-2"><Sparkles size={22} className="text-pink-400"/> Reward Gallery</h2>
                    <div className="text-pink-500 font-bold">Balance: {totalHearts} ♡ · 🔥 {streak}-day streak</div>
                  </div>
                  <button onClick={() => setShowGallery(false)} className="bg-pink-50 p-2 rounded-full"><XIcon size={20}/></button>
                </div>
                <p className="text-[11px] text-pink-300 mb-4">Soft heart backgrounds bloom open as your streak grows. ✨</p>
                <div className="flex-1 overflow-y-auto grid grid-cols-2 gap-3 pr-1 pb-2">
                  {BACKGROUNDS.map(bg => {
                    const unlocked = bgUnlocked(bg);
                    const active = activeBackground === bg.id;
                    return (
                      <button key={bg.id} onClick={() => selectBackground(bg)} className={`relative rounded-3xl p-4 h-28 flex flex-col justify-end text-left border-2 overflow-hidden transition-all ${active ? 'border-pink-400 ring-2 ring-pink-200' : 'border-pink-50 hover:border-pink-200'}`} style={{ backgroundImage: bg.grad }}>
                        <span className="font-bold text-pink-700/80 text-sm drop-shadow-sm">{bg.name}</span>
                        <span className="text-[10px] font-bold uppercase tracking-wide text-pink-600/70">
                          {active ? 'Equipped ✨' : unlocked ? 'Tap to equip' : bg.price > 0 ? `${bg.price} ♡` : `🔒 ${bg.streak}-day streak`}
                        </span>
                        {!unlocked && <div className="absolute inset-0 bg-white/45 flex items-center justify-center text-2xl">🔒</div>}
                      </button>
                    );
                  })}
                </div>
              </div>
            </div>
          )}

          {/* Stats — "Days Being a Good Girl" + streaks + avatar customization */}
          {showGoals && (
            <div className="fixed inset-0 bg-black/10 backdrop-blur-[3px] z-50 flex items-center justify-center p-4" onClick={() => setShowGoals(false)}>
              <div className="modal-card rounded-[2rem] p-5 md:p-8 w-full max-w-md flex flex-col max-h-[90vh]" onClick={e => e.stopPropagation()}>
                <div className="flex justify-between items-center mb-2">
                  <h2 className="text-2xl font-bold flex items-center gap-2"><Target size={22} className="text-pink-400"/> Goals</h2>
                  <button onClick={() => setShowGoals(false)} className="bg-pink-50 p-2 rounded-full"><XIcon size={20}/></button>
                </div>
                <p className="text-sm text-pink-400/80 mb-5">Write the things you want to become true. They'll flash on screen now and then — a gentle subliminal nudge. ✨</p>
                <div className="space-y-3 overflow-y-auto">
                  {[...Array(unlockedGoalSlots)].map((_, i) => (
                    <div key={i} className="flex items-center gap-2">
                      <input type="text" value={(goals[i] && goals[i].text) || ''} onChange={e => setGoalText(i, e.target.value)} placeholder={`Goal ${i + 1} — e.g. "drink more water"`} className="flex-1 bg-pink-50/60 border border-pink-100 rounded-2xl px-4 py-3 text-sm outline-none focus:border-pink-300" />
                      {goals[i] && goals[i].text && <button onClick={() => clearGoal(i)} className="text-pink-200 hover:text-pink-500 shrink-0" title="Clear"><Trash2 size={18}/></button>}
                    </div>
                  ))}
                </div>
                <button onClick={buyGoalSlot} className="mt-5 w-full flex items-center justify-center gap-2 bg-pink-50 hover:bg-pink-100 text-pink-500 font-bold py-3 rounded-2xl border border-pink-100 transition-colors">
                  <Plus size={18}/> Unlock another slot · {GOAL_SLOT_COST} ♡
                </button>
                <div className="text-center text-xs text-pink-300 mt-2 font-bold">Balance: {totalHearts} ♡</div>
              </div>
            </div>
          )}

          {showStats && (
            <div className="fixed inset-0 bg-black/10 backdrop-blur-[3px] z-50 flex items-center justify-center p-4">
              <div className="modal-card rounded-[2rem] p-5 md:p-8 w-full max-w-md flex flex-col max-h-[90vh]">
                <div className="flex justify-between items-center mb-6">
                  <h2 className="text-2xl font-bold flex items-center gap-2"><Star size={22} className="text-pink-400"/> Your Stats</h2>
                  <button onClick={() => setShowStats(false)} className="bg-pink-50 p-2 rounded-full"><XIcon size={20}/></button>
                </div>
                <div className="flex-1 overflow-y-auto space-y-5 pr-1">
                  <div className="flex flex-col items-center text-center bg-gradient-to-b from-pink-50 to-white rounded-3xl p-5 border border-pink-100">
                    <Avatar emoji={avatarData.emoji} level={cutenessLvl} size="lg" />
                    <div className="font-cute-display text-xl font-extrabold text-pink-500 mt-2">{CUTENESS_LABELS[cutenessLvl]}{neutralMode ? '' : ' girl'}</div>
                    <div className="text-5xl font-black text-pink-400 mt-3 flex items-center gap-2">{daysActive} <Sparkles size={28} className="text-pink-300"/></div>
                    <div className="text-[11px] uppercase tracking-widest font-bold text-pink-300">{neutralMode ? 'Days Active' : 'Days Being a Good Girl'}</div>
                  </div>
                  <div className="grid grid-cols-2 gap-3 text-center">
                    <div className="bg-pink-50/60 rounded-2xl p-4"><div className="text-2xl font-black text-pink-500">🔥 {streak}</div><div className="text-[10px] uppercase tracking-widest font-bold text-pink-300">Current Streak</div></div>
                    <div className="bg-pink-50/60 rounded-2xl p-4"><div className="text-2xl font-black text-pink-500">🏆 {bestStreak}</div><div className="text-[10px] uppercase tracking-widest font-bold text-pink-300">Best Streak</div></div>
                    <div className="bg-pink-50/60 rounded-2xl p-4"><div className="text-2xl font-black text-pink-500 flex items-center justify-center gap-1">{tasksCompleted} <Heart size={16} className="fill-pink-400 text-pink-400"/></div><div className="text-[10px] uppercase tracking-widest font-bold text-pink-300">Tasks Completed</div></div>
                    <div className="bg-pink-50/60 rounded-2xl p-4"><div className="text-2xl font-black text-pink-500">{totalHearts} ♡</div><div className="text-[10px] uppercase tracking-widest font-bold text-pink-300">Heart Balance</div></div>
                  </div>
                  <div className="bg-pink-50/40 rounded-2xl p-4">
                    <div className="flex items-center justify-between mb-2"><span className="text-[11px] uppercase tracking-widest font-bold text-pink-300">Today's Goal</span><span className="text-xs font-bold text-pink-400">{dailyHearts}/{DAILY_GOAL} ♡</span></div>
                    <div className="flex items-center gap-3"><HeartProgress progress={dailyProgress} /><span className="text-xs font-bold text-pink-400">{Math.round(dailyProgress*100)}%</span></div>
                  </div>
                  <div>
                    <div className="text-[11px] uppercase tracking-widest font-bold text-pink-300 mb-2">Choose Your Avatar</div>
                    <div className="flex flex-wrap gap-2">
                      {AVATARS.map(a => (
                        <button key={a.id} onClick={() => selectAvatar(a.id)} className={`w-12 h-12 rounded-2xl text-2xl flex items-center justify-center border-2 transition-all ${avatarId === a.id ? 'border-pink-400 bg-pink-50 scale-105' : 'border-pink-100 hover:bg-pink-50'}`}>{a.emoji}</button>
                      ))}
                    </div>
                    <p className="text-[11px] text-pink-300 mt-2">Your avatar gets cuter at {STREAK_MILESTONES.join(', ')}-day streaks. 💖</p>
                  </div>

                  <div className="pt-4 mt-4 border-t border-pink-100 flex justify-center flex-wrap gap-2">
                    <button
                      onClick={() => { setPinUnlocked(false); setShowStats(false); }}
                      className="px-5 py-2 rounded-full text-sm font-bold text-pink-500 bg-pink-50 hover:bg-pink-100 transition-colors flex items-center gap-1.5"
                    >
                      <Lock size={15} /> Lock
                    </button>
                    <button
                      onClick={() => { setForcePinSetup(true); setPinUnlocked(false); setShowStats(false); }}
                      className="px-5 py-2 rounded-full text-sm font-bold text-slate-500 bg-slate-100 hover:bg-slate-200 transition-colors"
                    >
                      Change PIN
                    </button>
                    <button
                      onClick={() => {
                        fbAPI.signOut(authRef.current).then(() => {
                          setUser(null);
                          setPinUnlocked(false);
                          setStatsReady(false);
                          setForcePinSetup(false);
                          setTasks([]);
                          setNotes([]);
                          setShowStats(false);
                        });
                      }}
                      className="px-5 py-2 rounded-full text-sm font-bold text-slate-500 bg-slate-100 hover:bg-slate-200 transition-colors"
                    >
                      Sign Out
                    </button>
                  </div>

                  {/* Sync diagnostics — compare these across devices. Same account + same
                      build on both = data WILL sync; if the uid differs, you're on
                      different Google accounts (or a stale cached build). */}
                  <div className="mt-4 text-center text-[10px] leading-relaxed text-slate-400 font-mono break-all">
                    <div>{user?.email || 'no account'}</div>
                    <div>uid {user ? user.uid.slice(0, 10) + '…' : '—'} · build {window.APP_VERSION}</div>
                    <button onClick={() => { copyToClipboard(`account: ${user?.email}\nuid: ${user?.uid}\nbuild: ${window.APP_VERSION}\nproject: gg-to-do`, 'Sync info copied!'); }} className="mt-1 text-pink-300 hover:text-pink-500 underline">copy sync info</button>
                  </div>

                </div>
              </div>
            </div>
          )}

          {selectedTask && (
            <div className="fixed inset-0 bg-black/10 backdrop-blur-[2px] z-50 flex justify-end">
              <div className="w-full md:w-[min(900px,72vw)] card-glass h-full shadow-glow-lg flex flex-col p-6 border-l border-white/50">
                <div className="flex justify-between items-center mb-6">
                  <HeartDisplay count={getDisplayHearts(selectedTask)} isStale={(Date.now()-selectedTask.createdAt) > 86400000} />
                  <div className="flex items-center gap-2">
                    <button onClick={() => copyToClipboard(`${selectedTask.text}${selectedTask.body ? '\n\n' + selectedTask.body : ''}`, 'Task copied!')} className="text-pink-300 hover:text-pink-500 transition-colors flex items-center gap-1 text-sm font-bold"><Copy size={18}/> Copy</button>
                    <button onClick={() => setSelectedTask(null)}><XIcon size={24} className="text-slate-400"/></button>
                  </div>
                </div>
                <textarea className="text-2xl font-bold w-full outline-none resize-none mb-4" rows={2} value={selectedTask.text} onChange={e => {
                  const nt = e.target.value; setSelectedTask({...selectedTask, text: nt});
                  setTasks(tasks.map(t => t.id === selectedTask.id ? {...t, text: nt} : t));
                  if (user && db && fbAPI) fbAPI.updateDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'tasks', selectedTask.id), { text: nt });
                }} />

                <div className="flex items-center gap-4 mb-5">
                  <div className="font-bold text-slate-700 flex items-center gap-2"><Calendar size={18} className="text-pink-400"/> Due Date</div>
                  <input type="date" value={selectedTask.dueDate || ''}
                    onClick={e => e.target.showPicker && e.target.showPicker()}
                    onChange={e => {
                    const nd = e.target.value || null; setSelectedTask({...selectedTask, dueDate: nd});
                    setTasks(tasks.map(t => t.id === selectedTask.id ? {...t, dueDate: nd} : t));
                    if (user && db && fbAPI) fbAPI.updateDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'tasks', selectedTask.id), { dueDate: nd });
                  }} className="bg-pink-50 border border-pink-100 text-pink-500 rounded-xl px-3 py-2 outline-none text-sm font-bold cursor-pointer hover:bg-pink-100" />
                </div>

                <div className="flex items-center gap-2 mb-5 flex-wrap">
                  <div className="font-bold text-slate-700 flex items-center gap-2 mr-1"><Tag size={18} className="text-pink-400"/> Category</div>
                  {CATEGORIES.map(c => (
                    <button key={c.id} onClick={() => {
                      const nc = selectedTask.category === c.id ? null : c.id;
                      setSelectedTask({...selectedTask, category: nc});
                      setTasks(tasks.map(t => t.id === selectedTask.id ? {...t, category: nc} : t));
                      if (user && db && fbAPI) fbAPI.updateDoc(fbAPI.doc(db, 'artifacts', appId, 'users', user.uid, 'tasks', selectedTask.id), { category: nc });
                    }} className={`px-3 py-1.5 rounded-full text-xs font-bold transition-all border ${selectedTask.category === c.id ? `${c.cls} border-transparent shadow-md` : 'bg-white text-pink-300 border-pink-100 hover:bg-pink-50'}`}>{c.emoji} {c.name}</button>
                  ))}
                </div>

                {/* Tags + Contexts editors — add to tasks you already created (#16) */}
                <div className="flex flex-col gap-3 mb-5">
                  <div className="flex items-center gap-2 flex-wrap">
                    <div className="font-bold text-slate-700 flex items-center gap-2 mr-1"><Tag size={18} className="text-pink-400"/> Tags</div>
                    {(selectedTask.tags || []).map(tag => (
                      <span key={tag} className="bg-pink-100 text-pink-500 text-[11px] font-bold pl-2.5 pr-1.5 py-0.5 rounded-full uppercase flex items-center gap-1">{tag}<button onClick={() => removeToken('tag', tag)} className="hover:text-pink-700"><XIcon size={12}/></button></span>
                    ))}
                    <input type="text" placeholder="+ tag" onKeyDown={e => { if (e.key === 'Enter') { addToken('tag')(e.target.value); e.target.value=''; } }} className="bg-pink-50/60 border border-pink-100 rounded-full px-3 py-1 text-xs outline-none w-24 focus:border-pink-300" />
                  </div>
                  <div className="flex items-center gap-2 flex-wrap">
                    <div className="font-bold text-slate-700 flex items-center gap-2 mr-1">@ Context</div>
                    {(selectedTask.contexts || []).map(ctx => (
                      <span key={ctx} className="bg-sky-100 text-sky-600 text-[11px] font-bold pl-2.5 pr-1.5 py-0.5 rounded-full flex items-center gap-1">{ctx}<button onClick={() => removeToken('context', ctx)} className="hover:text-sky-800"><XIcon size={12}/></button></span>
                    ))}
                    <input type="text" placeholder="+ @context" onKeyDown={e => { if (e.key === 'Enter') { addToken('context')(e.target.value); e.target.value=''; } }} className="bg-sky-50 border border-sky-100 rounded-full px-3 py-1 text-xs outline-none w-28 focus:border-sky-300" />
                  </div>
                </div>

                {/* Project mode — promote a task into a checklist of sub-steps (#6) */}
                <div className="mb-5">
                  <div className="flex items-center justify-between mb-2">
                    <div className="font-bold text-slate-700 flex items-center gap-2"><Folder size={18} className="text-indigo-400"/> Project</div>
                    <button onClick={toggleProject} className={`px-3 py-1.5 rounded-full text-xs font-bold border transition-colors ${selectedTask.isProject ? 'bg-indigo-500 text-white border-transparent' : 'bg-white text-indigo-400 border-indigo-100 hover:bg-indigo-50'}`}>{selectedTask.isProject ? 'It\'s a project' : 'Convert to project'}</button>
                  </div>
                  {selectedTask.isProject && (
                    <div className="space-y-2 bg-indigo-50/40 rounded-2xl p-3 border border-indigo-100">
                      {(selectedTask.subtasks || []).map(s => (
                        <div key={s.id} className="flex items-center gap-2">
                          <button onClick={() => toggleSubtask(s.id)} className={`shrink-0 ${s.done ? 'text-indigo-500' : 'text-indigo-200'}`}>{s.done ? <CheckCircle size={18}/> : <Circle size={18}/>}</button>
                          <span className={`flex-1 min-w-0 truncate text-sm ${s.done ? 'line-through text-slate-400' : 'text-slate-700'}`}>{s.text}</span>
                          <button onClick={() => removeSubtask(s.id)} className="text-indigo-200 hover:text-indigo-500 shrink-0"><Trash2 size={15}/></button>
                        </div>
                      ))}
                      <input type="text" placeholder="+ add a step…" onKeyDown={e => { if (e.key === 'Enter') { addSubtask(e.target.value); e.target.value=''; } }} className="w-full bg-white border border-indigo-100 rounded-xl px-3 py-2 text-sm outline-none focus:border-indigo-300" />
                    </div>
                  )}
                </div>

                <div className="flex-1 flex flex-col min-h-0">
                  <div className="font-bold text-slate-700 flex items-center gap-2 mb-3"><FileText size={18} className="text-pink-400" /> Editor</div>
                  <TextEditor
                    key={selectedTask.id}
                    value={selectedTask.body || selectedTask.notes || ''}
                    onSave={(v) => saveTaskBody(selectedTask.id, v)}
                    onTyping={registerTyping}
                    onCopy={() => awardProductivity(2, true)}
                    onFocusChange={(f) => { editingRef.current = f; }}
                    placeholder="Write freely — terminal shortcuts work here…"
                    autoFocus={false}
                    minHeight="100%"
                  />
                </div>
                <button onClick={e => { completeTask(selectedTask, e); setSelectedTask(null); }} className="btn-gradient w-full font-bold py-4 rounded-2xl mt-4 shrink-0 flex items-center justify-center gap-2"><Heart size={18} className="fill-white" /> Complete Task</button>
              </div>
            </div>
          )}

          {selectedNote && (
            <div className="fixed inset-0 bg-black/10 backdrop-blur-[2px] z-50 flex justify-end">
              <div className="w-full md:w-[min(900px,72vw)] card-glass h-full shadow-glow-lg flex flex-col p-6 border-l border-white/50">
                <div className="flex justify-between items-center mb-4">
                  <div className="text-[10px] uppercase tracking-widest font-bold text-pink-300">
                    Created {new Date(selectedNote.createdAt).toLocaleDateString()} · Modified {new Date(selectedNote.modifiedAt).toLocaleString()}
                  </div>
                  <div className="flex items-center gap-3">
                    <button onClick={() => copyToClipboard(`${selectedNote.title}\n\n${selectedNote.content || ''}`, 'Note copied!')} className="text-pink-300 hover:text-pink-500 transition-colors flex items-center gap-1 text-sm font-bold"><Copy size={18}/> Copy</button>
                    <button onClick={() => { if (confirmDeleteNote === selectedNote.id) { deleteNote(selectedNote.id); setConfirmDeleteNote(null); } else { setConfirmDeleteNote(selectedNote.id); setTimeout(() => setConfirmDeleteNote(null), 3000); } }} className={`text-sm font-bold transition-colors ${confirmDeleteNote === selectedNote.id ? 'text-red-500' : 'text-pink-300 hover:text-red-400'}`}>{confirmDeleteNote === selectedNote.id ? 'Sure?' : 'Delete'}</button>
                    <button onClick={() => setSelectedNote(null)}><XIcon size={24} className="text-slate-400"/></button>
                  </div>
                </div>
                <input className="text-2xl font-bold w-full outline-none mb-4" value={selectedNote.title} onChange={e => saveNote(selectedNote.id, { title: e.target.value })} placeholder="Untitled" />
                <div className="flex-1 flex flex-col min-h-0">
                  <TextEditor
                    key={selectedNote.id}
                    value={selectedNote.content || ''}
                    onSave={(v) => saveNote(selectedNote.id, { content: v })}
                    onTyping={registerTyping}
                    onCopy={() => awardProductivity(2, true)}
                    onFocusChange={(f) => { editingRef.current = f; }}
                    placeholder="Start writing your note…"
                    autoFocus={true}
                    minHeight="100%"
                  />
                </div>
              </div>
            </div>
          )}

          <div onClick={() => setShowArchive(true)} style={{ bottom: 'max(1.5rem, env(safe-area-inset-bottom))', left: 'max(1.5rem, env(safe-area-inset-left))' }} className="fixed bottom-6 left-6 card-glass py-2 px-4 rounded-full shadow-glow border border-white/60 flex items-center gap-4 cursor-pointer z-30">
            <div className="flex items-center gap-2"><Heart size={16} className="fill-pink-400 text-pink-400" /><span className="font-bold text-pink-500">{dailyHearts}</span><span className="text-[10px] uppercase text-pink-300 font-bold">today</span></div>
            <div className="w-px h-5 bg-pink-100" />
            <div className="flex items-center gap-2"><span>💋</span><span className="font-bold text-pink-500">{kissElo}</span><span className="text-[10px] uppercase text-pink-300 font-bold">elo</span></div>
          </div>

          {showArchive && (
            <div className="fixed inset-0 bg-white/90 z-50 flex items-center justify-center p-4">
              <div className="modal-card rounded-[2rem] p-5 md:p-8 w-full max-w-md max-h-[80vh] flex flex-col">
                <div className="flex justify-between items-center mb-6"><h2 className="text-2xl font-bold">Progress</h2><button onClick={() => { setShowArchive(false); setConfirmReset(false); }}><XIcon size={24}/></button></div>
                <div className="flex-1 overflow-y-auto space-y-6">
                  <div className="bg-pink-50/50 p-6 rounded-3xl">
                    <h3 className="text-pink-400 font-bold text-sm uppercase mb-4 tracking-widest">Last 7 Days</h3>
                    {(() => {
                      // Real activity: count tasks completed on each of the last 7 days
                      // (by completedAt; older tasks without it fall back to createdAt).
                      const startOf = d => { const x = new Date(d); x.setHours(0,0,0,0); return x.getTime(); };
                      const today = startOf(Date.now());
                      const days = [...Array(7)].map((_, i) => today - (6 - i) * 86400000);
                      const counts = days.map(day => tasks.filter(t => {
                        if (t.status !== 'completed') return false;
                        return startOf(t.completedAt || t.createdAt || 0) === day;
                      }).length);
                      const max = Math.max(1, ...counts);
                      return (
                        <div className="flex justify-between items-end h-32 gap-2">
                          {counts.map((c, i) => (
                            <div key={i} className="flex-1 flex flex-col items-center justify-end h-full">
                              <span className="text-[9px] mb-1 font-bold text-pink-400">{c || ''}</span>
                              <div className={`w-full rounded-full transition-all ${i===6 ? 'bg-pink-400' : 'bg-pink-200'}`} style={{height: `${Math.max((c/max)*100, 4)}%`}} />
                              <span className="text-[8px] mt-2 font-bold text-slate-400 uppercase tracking-tighter">{new Date(days[i]).toLocaleDateString([], { weekday: 'short' })}</span>
                            </div>
                          ))}
                        </div>
                      );
                    })()}
                  </div>
                  <div className="space-y-2">
                    {tasks.filter(t => t.status === 'completed').length === 0 && (
                      <div className="text-center text-sm text-pink-200 py-4">No completed tasks yet. Tap <span className="font-bold text-pink-300">History</span> once you finish some. ✨</div>
                    )}
                    {tasks.filter(t => t.status === 'completed')
                      .sort((a,b) => (b.completedAt || b.createdAt || 0) - (a.completedAt || a.createdAt || 0))
                      .slice(0, 8)
                      .map(t => (
                      <div key={t.id} className="flex justify-between p-3 card-glass border border-white/60 rounded-xl"><span className="text-slate-400 line-through truncate mr-4">{t.text}</span><HeartDisplay count={t.earnedHearts ? Math.min(5, t.earnedHearts) : getDisplayHearts(t)} isStale={false}/></div>
                    ))}
                    {tasks.filter(t => t.status === 'completed').length > 8 && (
                      <button onClick={() => { setShowArchive(false); setView('history'); }} className="w-full text-center text-xs font-bold text-pink-400 hover:text-pink-600 py-2">View all in History →</button>
                    )}
                  </div>
                </div>
                <div className="mt-6 pt-4 border-t border-pink-50">
                  <button onClick={handleResetStats} className={`w-full py-3 rounded-2xl font-bold transition-all ${confirmReset ? 'bg-red-400 text-white' : 'bg-pink-50 text-pink-400 hover:bg-pink-100'}`}>
                    {confirmReset ? 'Are you sure? (Click again)' : 'Reset All Stats'}
                  </button>
                </div>
              </div>
            </div>
          )}

          {showHelp && (
            <div className="fixed inset-0 bg-black/20 backdrop-blur-sm z-[60] flex items-center justify-center p-4" onClick={() => setShowHelp(false)}>
              <div className="modal-card rounded-[2rem] p-5 md:p-8 w-full max-w-lg flex flex-col max-h-[90vh]" onClick={e => e.stopPropagation()}>
                <div className="flex justify-between items-center mb-6">
                  <h2 className="text-2xl font-bold flex items-center gap-2"><Sparkles size={22} className="text-pink-400"/> Help</h2>
                  <button onClick={() => setShowHelp(false)} className="bg-pink-50 p-2 rounded-full"><XIcon size={20}/></button>
                </div>
                <div className="flex-1 overflow-y-auto space-y-6 pr-2 text-sm">
                  <section>
                    <h3 className="text-xs font-bold text-pink-300 uppercase mb-2 tracking-widest">Getting Around</h3>
                    <ul className="space-y-1.5 text-slate-600">
                      <li>💗 Type in the top box + <b>Enter</b> to capture a task. Use <b>#tags</b> inline.</li>
                      <li>💗 Tap a task to open the wide <b>editor</b>; click ♡ to complete it.</li>
                      <li>💗 <b>Notes</b> button opens standalone notes — <b>New</b> creates one (tracks created/modified).</li>
                      <li>💗 <b>Copy</b> icon on any task/note copies it to your clipboard.</li>
                      <li>💗 <b>Play</b> ranks priorities in a tournament. <b>Shop</b> spends hearts on themes & praise.</li>
                      <li>💗 <b>🎬</b> toggles GGTok live mode — your focus, typing & copying earn tokens.</li>
                    </ul>
                  </section>
                  <section>
                    <h3 className="text-xs font-bold text-pink-300 uppercase mb-2 tracking-widest">Editor Shortcuts (terminal-style)</h3>
                    <div className="grid grid-cols-2 gap-x-4 gap-y-1.5 font-mono text-xs text-slate-600">
                      <div><b>Ctrl+A</b> — line start</div><div><b>Ctrl+E</b> — line end</div>
                      <div><b>Ctrl+K</b> — kill to line end</div><div><b>Ctrl+U</b> — kill to line start</div>
                      <div><b>Ctrl+W</b> — delete word back</div><div><b>Ctrl+D</b> — delete char fwd</div>
                      <div><b>Ctrl+H</b> — delete char back</div><div><b>Ctrl+Y</b> — yank (paste killed)</div>
                      <div><b>Alt+B</b> — word back</div><div><b>Alt+F</b> — word forward</div>
                      <div><b>Ctrl+S</b> — save now</div><div><b>Ctrl+/</b> — this help</div>
                    </div>
                    <p className="text-[11px] text-pink-300 mt-2">Notes & task bodies autosave to the cloud as you type. ✨</p>
                  </section>
                </div>
              </div>
            </div>
          )}
          </div>
        </div>
      );
    }

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
