import { useDelayedFlag } from '@/hooks/useDelayedFlag';
import { useState, useEffect, useRef, useLayoutEffect } from 'react';
import { Helmet } from 'react-helmet-async';
import { useTranslation } from 'react-i18next';
import { motion, AnimatePresence } from 'framer-motion';
import { useNavigate, useLocation } from 'react-router-dom';
import { PREVENTION_MESSAGES } from '@/stores/appStore';
import ScratchCard from '@/components/ScratchCard';
import { supabase } from '@/integrations/supabase/client';
import { loadFamilyData } from '@/lib/familyService';
import ChildNavBar from '@/components/ChildNavBar';
import ChildAvatar from '@/components/ChildAvatar';
import HungerGauge from '@/components/HungerGauge';
import MissionCard from '@/components/missions/MissionCard';
import { translateMissionTitle, translateMissionHint } from '@/lib/missionI18n';
import MissionRatingOverlay from '@/components/missions/MissionRatingOverlay';
import PreventionOverlay from '@/components/missions/PreventionOverlay';
import MissionSupportsOverlay from '@/components/missions/MissionSupportsOverlay';
import { computeSupportLevel, consumeSupportSuppression } from '@/lib/missionSupport';
import CelebrationOverlay from '@/components/celebrations/CelebrationOverlay';
import NewDayCelebration, { shouldShowNewDay, markNewDaySeen } from '@/components/celebrations/NewDayCelebration';
import { useMissionCelebration } from '@/hooks/useMissionCelebration';
import { loadInventory } from '@/lib/inventoryService';
import { resolveEquippedItems } from '@/lib/shopItems';
import { ensureDailyMissions } from '@/lib/dailyMissions';
import { playTap, playCoinCollect, playMissionStart, playMissionSuccess, inferMissionDomain, childSpeak } from '@/lib/soundEffects';
import { useEntranceAnimation } from '@/hooks/useEntranceAnimation';
import { getCachedFamilyData, primeFamilyData } from '@/lib/familyPrefetch';
import { getCachedEquipped, setCachedEquipped, prefetchEquipped, equippedSignature } from '@/lib/equippedCache';

// Show only missions belonging to today's local calendar day on the child board.
// Validated/expired/awaiting missions from previous days must never appear here.
const isTodayLocal = (dateStr?: string | null) => {
  if (!dateStr) return false;
  const d = new Date(dateStr);
  const n = new Date();
  return d.getFullYear() === n.getFullYear() && d.getMonth() === n.getMonth() && d.getDate() === n.getDate();
};
const filterTodayMissions = (list: any[]) =>
  (list || []).filter((m: any) => isTodayLocal(m.created_at));

const ChildMissionBoard = () => {
  const { t, i18n } = useTranslation();
  // Localized single-letter weekday labels, Monday → Sunday.
  const weekdayNarrow = (() => {
    try {
      const fmt = new Intl.DateTimeFormat(i18n.language, { weekday: 'narrow' });
      // 2024-01-01 was a Monday → walk 7 days
      return Array.from({ length: 7 }, (_, i) => fmt.format(new Date(2024, 0, 1 + i)));
    } catch {
      return ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
    }
  })();
  const navigate = useNavigate();
  const location = useLocation();
  // Hydrate synchronously from prefetch cache so the dashboard renders
  // immediately when arriving from ProfilePicker / ChildPinScreen.
  const initialHydration = (() => {
    if (typeof window === 'undefined') return null;
    try {
      const selected = localStorage.getItem('stell_selected_profile');
      if (!selected) return null;
      const profile = JSON.parse(selected);
      const cached = getCachedFamilyData();
      const c = cached?.children?.find((ch: any) => ch.id === profile.id);
      return c ? { child: c, parent: cached?.parent } : null;
    } catch { return null; }
  })();
  const [child, setChild] = useState<any>(initialHydration?.child ?? null);
  const [parentAvatar, setParentAvatar] = useState<string | undefined>(initialHydration?.parent?.avatar);
  const [parentName, setParentName] = useState<string | undefined>(initialHydration?.parent?.name);
  const [missions, setMissions] = useState<any[]>(initialHydration ? filterTodayMissions(initialHydration.child.missions || []) : []);
  const [loading, setLoading] = useState(!initialHydration);
  const [showPrevention, setShowPrevention] = useState<string | null>(null);
  const [showScratch, setShowScratch] = useState<string | null>(null);
  const [showRating, setShowRating] = useState<string | null>(null);
  const [showSupports, setShowSupports] = useState<string | null>(null);
  const [equippedItems, setEquippedItems] = useState<any[]>(
    () => (initialHydration?.child?.id ? getCachedEquipped(initialHydration.child.id) ?? [] : [])
  );
  const [updatingAvatar, setUpdatingAvatar] = useState(false);
  const [muted, setMuted] = useState(false);
  const [starsBounce, setStarsBounce] = useState(false);
  const [diamondsBounce, setDiamondsBounce] = useState(false);
  const [streakBounce, setStreakBounce] = useState(false);
  // Layout fallback: when the streak banner can't fit horizontally (very small
  // screens, long translations, etc.), we stack the 7-day row beneath the
  // streak number instead of inline so Sunday's flame never gets clipped.
  const streakBannerRef = useRef<HTMLDivElement | null>(null);
  const [streakStacked, setStreakStacked] = useState(false);
  useLayoutEffect(() => {
    const el = streakBannerRef.current;
    if (!el) return;
    const check = () => {
      // Temporarily un-stack to measure the natural inline width.
      const prev = el.dataset.stacked;
      el.dataset.stacked = 'false';
      const overflow = el.scrollWidth > el.clientWidth + 1;
      if (prev !== undefined) el.dataset.stacked = prev;
      setStreakStacked(s => (s !== overflow ? overflow : s));
    };
    check();
    const ro = new ResizeObserver(check);
    ro.observe(el);
    window.addEventListener('resize', check);
    return () => { ro.disconnect(); window.removeEventListener('resize', check); };
  }, []);
  const [showNewDay, setShowNewDay] = useState(false);
  
  const [flameLightUp, setFlameLightUp] = useState(false);
  const { celebration, dismiss: dismissCelebration } = useMissionCelebration(child?.id);
  const entrance = useEntranceAnimation('mission-board');

  useEffect(() => {
    if (!celebration || !child) return;
    const refresh = async () => {
      const data = await loadFamilyData();
      if (!data) return;
      const c = data.children.find((ch: any) => ch.id === child.id);
      if (c) { setChild(c); setMissions(filterTodayMissions(c.missions || [])); }
    };
    refresh();
  }, [celebration]);

  useEffect(() => {
    if (!child?.id) return;
    const channel = supabase
      .channel(`child-dashboard-${child.id}`)
      .on('postgres_changes', { event: 'UPDATE', schema: 'public', table: 'children', filter: `id=eq.${child.id}` }, (payload) => {
        const updated = payload.new as any;
        setUpdatingAvatar(true);
        setChild((prev: any) => {
          if (!prev) return prev;
          if (updated.stars !== prev.stars) { setStarsBounce(true); setTimeout(() => setStarsBounce(false), 600); playCoinCollect(); }
          if (updated.diamonds !== prev.diamonds) { setDiamondsBounce(true); setTimeout(() => setDiamondsBounce(false), 600); playCoinCollect(); }
          if (updated.streak !== prev.streak) { setStreakBounce(true); setTimeout(() => setStreakBounce(false), 600); playCoinCollect(); }
          return { ...prev, stars: updated.stars, diamonds: updated.diamonds, streak: updated.streak, level: updated.level, xp: updated.xp, avatar: updated.avatar, name: updated.name, fullness: updated.fullness };
        });
        setTimeout(() => setUpdatingAvatar(false), 400);
      })
      .on('postgres_changes', { event: '*', schema: 'public', table: 'missions', filter: `child_id=eq.${child.id}` }, async () => {
        const data = await loadFamilyData();
        if (!data) return;
        const c = data.children.find((ch: any) => ch.id === child.id);
        if (c) setMissions(filterTodayMissions(c.missions || []));
      })
      .on('postgres_changes', { event: '*', schema: 'public', table: 'child_inventory', filter: `child_id=eq.${child.id}` }, async () => {
        setUpdatingAvatar(true);
        const fresh = await prefetchEquipped(child.id);
        setEquippedItems(prev => equippedSignature(prev) === equippedSignature(fresh) ? prev : fresh);
        setTimeout(() => setUpdatingAvatar(false), 400);
      })
      .subscribe();
    return () => { supabase.removeChannel(channel); };
  }, [child?.id]);

  // Timezone-aware daily reset.
  // Fires at the user's local midnight, and is resilient to:
  //  - DST transitions (each schedule recomputes from `new Date()`)
  //  - Device sleep / setTimeout drift (visibility/focus checks)
  //  - Timezone changes (traveling, manual TZ change) — re-check IANA zone
  //  - Locale changes — recompute on i18n language change
  useEffect(() => {
    if (!child?.id) return;
    let timer: ReturnType<typeof setTimeout> | null = null;
    let cancelled = false;

    const localDayKey = (d = new Date()) =>
      `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;
    const getTz = () => {
      try { return Intl.DateTimeFormat().resolvedOptions().timeZone || ''; } catch { return ''; }
    };

    let lastDayKey = localDayKey();
    let lastTz = getTz();

    const runReset = async () => {
      if (cancelled) return;
      const dailyMissions = await ensureDailyMissions(child.id);
      if (cancelled) return;
      setMissions(filterTodayMissions(dailyMissions));
      lastDayKey = localDayKey();
    };

    const schedule = () => {
      if (timer) clearTimeout(timer);
      const now = new Date();
      // Next local midnight in the *current* timezone — recomputed each schedule
      // so DST shifts are handled correctly.
      const nextMidnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 5);
      const ms = Math.max(1000, nextMidnight.getTime() - now.getTime());
      timer = setTimeout(async () => {
        await runReset();
        schedule();
      }, ms);
    };

    const checkDrift = async () => {
      if (cancelled) return;
      const tz = getTz();
      const dayKey = localDayKey();
      const tzChanged = tz && tz !== lastTz;
      const dayChanged = dayKey !== lastDayKey;
      if (tzChanged) lastTz = tz;
      if (dayChanged || tzChanged) {
        await runReset();
        schedule();
      }
    };

    schedule();

    const onVisible = () => { if (document.visibilityState === 'visible') checkDrift(); };
    document.addEventListener('visibilitychange', onVisible);
    window.addEventListener('focus', checkDrift);
    window.addEventListener('online', checkDrift);
    // i18next emits 'languageChanged' on locale switch
    i18n.on?.('languageChanged', checkDrift);

    return () => {
      cancelled = true;
      if (timer) clearTimeout(timer);
      document.removeEventListener('visibilitychange', onVisible);
      window.removeEventListener('focus', checkDrift);
      window.removeEventListener('online', checkDrift);
      i18n.off?.('languageChanged', checkDrift);
    };
  }, [child?.id, i18n]);

  useEffect(() => {
    const load = async () => {
      const selected = localStorage.getItem('stell_selected_profile');
      if (!selected) { navigate('/profiles'); return; }
      const profile = JSON.parse(selected);
      const data = await loadFamilyData();
      if (!data) { navigate('/profiles'); return; }
      primeFamilyData(data);
      const c = data.children.find((ch: any) => ch.id === profile.id);
      if (!c) { navigate('/profiles'); return; }

      // ── Paint the right frame immediately ──
      // Use whatever child + mission data we already have so the Kids Home
      // renders the correct layout, avatar, and outfit on the first frame.
      // Everything below runs in the background and updates state when ready.
      setChild(c);
      setMissions(filterTodayMissions(c.missions || []));
      setParentAvatar(data.parent?.avatar);
      setParentName(data.parent?.name);

      // Equipped outfit: prefer cached snapshot so the avatar never appears
      // "naked" on first paint, then reconcile against the server.
      const cachedEq = getCachedEquipped(c.id);
      if (cachedEq && cachedEq.length > 0) setEquippedItems(cachedEq);
      setLoading(false);

      // Daily new-day + flame celebrations
      if (shouldShowNewDay(c.id)) setShowNewDay(true);
      const flameDayKey = `stell_flame_lit_${c.id}_${new Date().toDateString()}`;
      if (!sessionStorage.getItem(flameDayKey)) {
        sessionStorage.setItem(flameDayKey, '1');
        setFlameLightUp(true);
        setTimeout(() => setFlameLightUp(false), 2000);
      }

      // ── Background work: hunger decay, missions ensure, inventory ──
      // Run independently so none of them block the visible frame.
      void (async () => {
        try {
          const { applyDailyDecay } = await import('@/lib/hungerService');
          const decayed = await applyDailyDecay(c.id, {
            fullness: c.fullness ?? 50,
            last_fed_check: c.last_fed_check ?? new Date().toISOString().slice(0, 10),
          });
          setChild((prev: any) => prev && prev.id === c.id
            ? { ...prev, fullness: decayed.fullness, last_fed_check: decayed.last_fed_check }
            : prev);
        } catch { /* non-fatal */ }
      })();

      void (async () => {
        try {
          const dailyMissions = await ensureDailyMissions(c.id);
          const { data: freshMissions } = await supabase
            .from('missions')
            .select('*')
            .eq('child_id', c.id)
            .order('created_at', { ascending: false });
          const finalMissions = (freshMissions && freshMissions.length >= dailyMissions.length)
            ? freshMissions
            : dailyMissions;
          setMissions(filterTodayMissions(finalMissions));
        } catch { /* non-fatal */ }
      })();

      // Validate the cached equipped outfit against the latest server-side
      // inventory on every Kids Home entry. The cached snapshot already
      // painted instantly above; we only re-render the avatar when the
      // signature actually drifted (e.g. items bought/equipped on another
      // device), which avoids gratuitous re-renders on the hot path.
      void (async () => {
        const fresh = await prefetchEquipped(c.id);
        setEquippedItems(prev => equippedSignature(prev) === equippedSignature(fresh) ? prev : fresh);
      })();
    };
    load();
  }, [navigate, location.key]);

  // When the tab regains focus, re-validate the equipped cache so a change
  // made on another device (or while the tab was hidden) is reflected
  // without waiting for a realtime ping.
  useEffect(() => {
    if (!child?.id) return;
    const revalidate = async () => {
      if (document.visibilityState !== 'visible') return;
      const fresh = await prefetchEquipped(child.id);
      setEquippedItems(prev => equippedSignature(prev) === equippedSignature(fresh) ? prev : fresh);
    };
    document.addEventListener('visibilitychange', revalidate);
    window.addEventListener('focus', revalidate);
    return () => {
      document.removeEventListener('visibilitychange', revalidate);
      window.removeEventListener('focus', revalidate);
    };
  }, [child?.id]);

  const showMbLoader = useDelayedFlag(loading || !child);
  if (loading || !child) {
    return showMbLoader ? (
      <div className="min-h-screen bg-background flex items-center justify-center">
        <motion.div animate={{ scale: [1, 1.3, 1], rotate: [0, 180, 360] }} transition={{ duration: 1.5, repeat: Infinity }} className="text-5xl">⭐</motion.div>
      </div>
    ) : null;
  }

  const ageGroup: 'young' | 'mid' | 'teen' = child.age <= 6 ? 'young' : child.age <= 10 ? 'mid' : 'teen';

  const getPreventionMsg = (title: string) => {
    const typeMap: Record<string, string> = {
      brush: 'brushTeeth', teeth: 'brushTeeth', eat: 'eatFruits', fruit: 'eatFruits',
      read: 'reading', dressed: 'getDressed', tidy: 'tidyRoom',
      screen: 'eveningRoutine', evening: 'eveningRoutine', sport: 'sport',
    };
    const lower = title.toLowerCase();
    let type = '';
    for (const [kw, t] of Object.entries(typeMap)) { if (lower.includes(kw)) { type = t; break; } }
    if (!type) return t('kidsBoard.letsDoMission', { title });
    const msgs = PREVENTION_MESSAGES[type];
    if (!msgs) return '';
    const ageKey = child.age <= 6 ? '3-6' : child.age <= 10 ? '6-10' : '10-14';
    const arr = msgs[ageKey] || msgs['all'];
    return arr ? arr[0] : '';
  };

  const handleMissionTap = (mission: any) => {
    if (mission.status !== 'active') return;
    // Habit-typed start cue — kids learn to recognize the routine by ear.
    playMissionStart(mission.title);
    const localizedTitle = translateMissionTitle(t, mission.title);
    const localizedHint = translateMissionHint(t, mission.hint);
    // Cheerful, locale-correct voiceover — reads the full mission (title +
    // step-by-step hint) so kids hear exactly what to do in their own
    // language. childSpeak picks the per-language voice and applies warm,
    // celebratory prosody.
    const intro = t('kidsBoard.letsDoMission', { title: localizedTitle });
    const spoken = localizedHint ? `${intro} ${localizedHint}` : intro;
    childSpeak(spoken, undefined, { celebratory: true, age: child.age });
    const msg = getPreventionMsg(localizedTitle);
    const supportLevel = computeSupportLevel(missions, child?.id);
    setShowPrevention(msg);


    const proceed = () => {
      setShowPrevention(null);
      if (supportLevel > 0) {
        setShowSupports(mission.id);
      } else {
        if (child?.id) consumeSupportSuppression(child.id);
        setShowScratch(mission.id);
      }
    };
    // Wait for TTS to finish speaking before moving on (cap at 12s safety net).
    const start = Date.now();
    const tick = () => {
      const synth = (typeof window !== 'undefined') ? window.speechSynthesis : null;
      const stillSpeaking = synth ? (synth.speaking || synth.pending) : false;
      const elapsed = Date.now() - start;
      // Minimum 1.2s so kids see the mascot, then wait until voice ends.
      if (elapsed < 1200 || (stillSpeaking && elapsed < 12000)) {
        setTimeout(tick, 250);
      } else {
        proceed();
      }
    };
    setTimeout(tick, 1200);
  };

  const onScratchComplete = async (missionId: string) => {
    const mission = missions.find(m => m.id === missionId);
    // Habit-typed success motif (falls back to a friendly default if unknown).
    playMissionSuccess(mission?.title);
    setShowScratch(null);
    setShowRating(missionId);
    await supabase.from('missions').update({
      status: 'awaiting_validation',
      completed_at: new Date().toISOString(),
    }).eq('id', missionId);
    // Build per-locale mission label so parent devices receive the
    // notification in their own language regardless of the child's UI lang.
    // We only include a locale entry when the translation key actually
    // resolved (i.e. it's not just the raw English fallback). English is
    // always included and acts as the canonical fallback on the server.
    const i18nMod = await import('@/i18n');
    const i18n = i18nMod.default;
    // Need every locale's bundle to produce per-locale push labels; lazy-load
    // any that weren't part of the boot set (active + fr + en).
    await i18nMod.ensureAllLocales();
    const SUPPORTED = ['fr','en','de','it','es','ar','pt','pl','ja','zh-CN'] as const;
    const mission_label_by_locale: Record<string, string> = {};
    let mission_label_fallback: string | undefined;
    if (mission) {
      const emoji = mission.emoji ?? '✨';
      const rawTitle = mission.title ?? '';
      const enTitle = translateMissionTitle(i18n.getFixedT('en'), rawTitle);
      mission_label_by_locale['en'] = `${emoji} ${enTitle}`;
      mission_label_fallback = mission_label_by_locale['en'];
      for (const loc of SUPPORTED) {
        if (loc === 'en') continue;
        const tt = i18n.getFixedT(loc);
        const translated = translateMissionTitle(tt, rawTitle);
        // Skip locales where no translation was found (translator returns
        // the raw input). The server will fall back to English for them,
        // which is the clearest signal that a key is missing.
        if (translated && translated !== rawTitle) {
          mission_label_by_locale[loc] = `${emoji} ${translated}`;
        }
      }
    }
    // Fire-and-forget push to parents (non-blocking)
    supabase.functions
      .invoke('notify-mission-completed', {
        body: {
          child_id: child.id,
          mission_id: missionId,
          mission_label_by_locale,
          mission_label_fallback,
        },
      })
      .catch(() => {});
  };

  const rateMission = (missionId: string) => {
    setShowRating(null);
    setMissions(prev => prev.map(m => m.id === missionId ? { ...m, status: 'awaiting_validation' } : m));
  };

  const activeMissions = missions.filter(m => m.status === 'active');
  const pendingMissions = missions.filter(m => m.status === 'awaiting_validation');
  const doneMissions = missions.filter(m => m.status === 'validated');
  const completedToday = pendingMissions.length + doneMissions.length;
  const totalMissions = missions.length;
  const progressPercent = totalMissions > 0 ? (completedToday / totalMissions) * 100 : 0;

  const greetingEmoji = ageGroup === 'young' ? '🌈' : '👋';
  const timeOfDay = new Date().getHours();
  const greeting = timeOfDay < 12 ? t('kidHome.greetingMorning') : timeOfDay < 18 ? t('kidHome.greetingAfternoon') : t('kidHome.greetingEvening');

  return (
    <>
      <Helmet>
        <title>{t('seo.child.title')}</title>
        <meta name="description" content={t('seo.child.description')} />
        <meta property="og:title" content={t('seo.child.title')} />
        <meta property="og:description" content={t('seo.child.description')} />
        <meta property="og:url" content="https://star.stellkey.com/child" />
        <link rel="canonical" href="https://star.stellkey.com/child" />
      </Helmet>
      <div className="child-ui h-[100dvh] flex flex-col overflow-hidden relative"
      style={{
        background: ageGroup === 'young'
          ? 'linear-gradient(180deg, hsl(42 100% 96%) 0%, hsl(42 100% 92%) 30%, hsl(199 92% 95%) 100%)'
          : ageGroup === 'mid'
          ? 'linear-gradient(180deg, hsl(199 92% 96%) 0%, hsl(280 60% 96%) 100%)'
          : 'linear-gradient(180deg, hsl(220 20% 97%) 0%, hsl(220 20% 93%) 100%)',
      }}>

      {/* Floating decorations — age-adapted */}
      {ageGroup === 'young' && (
        <>
          {['⭐', '🌈', '☁️', '🦋', '🌸', '✨'].map((e, i) => (
            <motion.span key={`deco-${i}`}
              className="absolute pointer-events-none z-0"
              style={{ left: `${8 + i * 15}%`, top: `${10 + (i % 3) * 25}%`, fontSize: `${18 + (i % 3) * 6}px` }}
              animate={{ y: [0, -15, 0], rotate: [0, (i % 2 ? 10 : -10), 0], opacity: [0.3, 0.6, 0.3] }}
              transition={{ duration: 4 + i, repeat: Infinity, delay: i * 0.8 }}
            >{e}</motion.span>
          ))}
        </>
      )}
      {ageGroup === 'mid' && (
        <>
          {['💫', '⚡', '🔮', '🎯', '🌟'].map((e, i) => (
            <motion.span key={`deco-${i}`}
              className="absolute pointer-events-none z-0"
              style={{ left: `${5 + i * 18}%`, top: `${8 + (i % 3) * 30}%`, fontSize: '16px' }}
              animate={{ y: [0, -10, 0], opacity: [0.2, 0.5, 0.2] }}
              transition={{ duration: 5 + i, repeat: Infinity, delay: i * 1.2 }}
            >{e}</motion.span>
          ))}
        </>
      )}

      {/* Floating hearts */}
      {Array.from({ length: 4 }).map((_, i) => (
        <motion.span key={`heart-${i}`}
          className="absolute pointer-events-none z-0"
          style={{ left: `${15 + i * 20}%`, bottom: -20, fontSize: '18px' }}
          animate={{
            y: [0, -500 - Math.random() * 200],
            x: [0, (i % 2 === 0 ? 1 : -1) * (15 + Math.random() * 30)],
            opacity: [0, 0.5, 0],
            scale: [0.6, 1, 0.6],
          }}
          transition={{ duration: 8 + Math.random() * 3, repeat: Infinity, delay: i * 2.5 + Math.random() * 2 }}
        >
          {['❤️', '🧡', '💛', '💚'][i]}
        </motion.span>
      ))}

      {/* New Day celebration */}
      {showNewDay && child && (
        <NewDayCelebration
          childName={child.name} childAge={child.age} streak={child.streak}
          onDismiss={() => { setShowNewDay(false); markNewDaySeen(child.id); }}
        />
      )}

      {/* ── Hero Header (sticky at top) ── */}
      <motion.div className="relative z-20 px-4 pt-3 pb-2 backdrop-blur-md flex-shrink-0"
        style={{
          background: 'hsl(0 0% 100% / 0.55)',
          borderBottom: '1px solid hsl(0 0% 0% / 0.06)',
          boxShadow: '0 4px 12px -6px hsl(0 0% 0% / 0.1)',
        }}
        initial={{ opacity: 0, y: -10 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration: 0.3, ease: 'easeOut' }}>

        {/* Top bar: greeting + stats (streak inline with stars/diamonds) */}
        <div className="flex items-center justify-between mb-1.5">
          <motion.div className="flex items-center gap-2"
            initial={{ opacity: 0, x: -10 }} animate={{ opacity: 1, x: 0 }}
            transition={{ duration: 0.3, delay: 0.1 }}>
            <motion.div className="cursor-pointer" onClick={() => { playTap(); navigate('/child/profile'); }}
              whileTap={{ scale: 0.9 }}>
              <ChildAvatar avatar={child.avatar} name={child.name} equippedItems={equippedItems} size="sm" loading={updatingAvatar} />
            </motion.div>
            <div className="flex flex-col">
              <span className={`font-black ${ageGroup === 'young' ? 'text-base' : 'text-sm'} text-foreground leading-tight`}>
                {greetingEmoji} {greeting}, {child.name}!
              </span>
              <span className="text-[10px] text-muted-foreground font-bold">
                {completedToday === totalMissions && totalMissions > 0
                  ? t('kidHome.allDoneToday')
                  : t('kidHome.missionsLeft', { count: totalMissions - completedToday })}
              </span>
            </div>
          </motion.div>

          <button onClick={() => setMuted(!muted)} className="text-sm p-1 opacity-50 hover:opacity-100 transition-opacity">
            {muted ? '🔇' : '🔊'}
          </button>
        </div>

        {/* ── Streak HERO banner — the star of the show ── */}
        <motion.div
          ref={streakBannerRef}
          className={`flex ${streakStacked ? 'flex-col items-stretch gap-1.5' : 'items-center gap-2'} mb-2 rounded-3xl px-2.5 py-2 relative overflow-hidden max-w-full w-full box-border`}
          initial={{ opacity: 0, y: 8, scale: 0.95 }} animate={{ opacity: 1, y: 0, scale: 1 }}
          transition={{ duration: 0.4, delay: 0.12, type: 'spring', stiffness: 320, damping: 18 }}
          style={{
            background: child.streak > 0
              ? 'linear-gradient(135deg, hsl(25 100% 88%), hsl(42 100% 86%), hsl(15 100% 88%))'
              : 'linear-gradient(135deg, hsl(0 0% 95%), hsl(0 0% 92%))',
            border: child.streak > 0 ? '4px solid hsl(25 100% 65%)' : '4px solid hsl(0 0% 80%)',
            boxShadow: child.streak > 0
              ? '0 5px 0 0 hsl(25 100% 50% / 0.4), 0 0 24px hsl(25 100% 60% / 0.4)'
              : '0 4px 0 0 hsl(0 0% 70% / 0.3)',
          }}>
          {child.streak > 0 && (
            <motion.div
              className="absolute -left-3 top-1/2 -translate-y-1/2 w-20 h-20 rounded-full pointer-events-none"
              style={{ background: 'radial-gradient(circle, hsl(42 100% 65% / 0.65), transparent 70%)' }}
              animate={{ scale: [1, 1.3, 1], opacity: [0.6, 1, 0.6] }}
              transition={{ duration: 1.6, repeat: Infinity }}
            />
          )}

          <motion.div
            animate={streakBounce ? { scale: [1, 1.5, 1], rotate: [0, -10, 10, 0] } : {}}
            style={{ transformOrigin: 'center', willChange: 'transform' }}
            className="flex items-center gap-2 relative z-10 min-w-0 shrink">
            <motion.span
              className="text-4xl inline-block shrink-0"
              style={{
                filter: child.streak > 0
                  ? 'drop-shadow(0 2px 8px hsl(25 100% 50% / 0.7))'
                  : 'grayscale(0.8) opacity(0.5)',
                transformOrigin: 'center',
                willChange: 'transform',
              }}
              animate={child.streak > 0 ? { scale: [1, 1.2, 1], rotate: [-6, 6, -6], y: [0, -2, 0] } : {}}
              transition={{ duration: 1.4, repeat: Infinity, ease: 'easeInOut' }}
            >🔥</motion.span>
            <div className="flex flex-col items-start leading-none min-w-0">
              <span
                className="font-black text-3xl"
                style={{
                  color: child.streak > 0 ? 'hsl(15 100% 32%)' : 'hsl(0 0% 50%)',
                  textShadow: child.streak > 0 ? '0 2px 0 hsl(42 100% 80%), 0 3px 6px hsl(25 100% 40% / 0.3)' : 'none',
                }}>{child.streak}</span>
              <span className="font-black text-[10px] uppercase tracking-wider mt-0.5 max-w-full truncate"
                style={{ color: child.streak > 0 ? 'hsl(15 100% 30%)' : 'hsl(0 0% 50%)' }}>
                {child.streak === 0 ? t('kidHome.lightItUp') : t('kidHome.dayStreak')}
              </span>
            </div>
          </motion.div>

          <div className={`flex items-center relative z-10 max-w-full ${streakStacked ? 'w-full justify-between gap-0.5' : 'gap-1 ml-auto shrink-0 min-w-0'}`}>
            {weekdayNarrow.map((day, i) => {
              const streakDay = child.streak % 7;
              const isLit = child.streak >= 7 || i < streakDay || (streakDay === 0 && child.streak > 0 && i < 7);
              const isTodayDay = i === (new Date().getDay() + 6) % 7;
              const justLit = isTodayDay && flameLightUp;
              const todayGlowing = isTodayDay && !isLit;
              return (
                <motion.div key={i} className="relative flex flex-col items-center"
                  initial={{ scale: 0 }} animate={{ scale: 1 }}
                  transition={{ delay: 0.18 + i * 0.05, type: 'spring', stiffness: 400, damping: 15 }}>
                  {isTodayDay && (
                    <motion.div
                      className="absolute w-8 h-8 rounded-full pointer-events-none -z-10"
                      style={{
                        background: 'radial-gradient(circle, hsl(42 100% 65% / 0.85), transparent 70%)',
                        top: '-2px',
                      }}
                      animate={{ scale: [1, 1.4, 1], opacity: [0.7, 1, 0.7] }}
                      transition={{ duration: 1.2, repeat: Infinity, ease: 'easeInOut' }}
                    />
                  )}
                  {/* Day frame — always visible so kids see all 7 weekdays even if not lit */}
                  <div
                    className="w-7 h-7 rounded-lg flex items-center justify-center"
                    style={{
                      background: (isLit || justLit)
                        ? 'hsl(42 100% 92%)'
                        : isTodayDay
                        ? 'hsl(42 100% 96%)'
                        : 'hsl(0 0% 100% / 0.55)',
                      border: (isLit || justLit)
                        ? '2px solid hsl(25 100% 65%)'
                        : isTodayDay
                        ? '2px dashed hsl(25 100% 60%)'
                        : '2px solid hsl(0 0% 80%)',
                      boxShadow: (isLit || justLit)
                        ? '0 2px 0 0 hsl(25 100% 50% / 0.35)'
                        : '0 1px 0 0 hsl(0 0% 0% / 0.06)',
                    }}
                  >
                    <motion.span
                      className={`text-base ${isLit || justLit || todayGlowing ? '' : 'opacity-30 grayscale'}`}
                      style={{
                        filter: (isLit || justLit || todayGlowing)
                          ? 'drop-shadow(0 1px 3px hsl(25 100% 50% / 0.6))'
                          : 'none',
                      }}
                      animate={justLit ? { scale: [0.3, 1.9, 1.1], filter: ['brightness(1)', 'brightness(3)', 'brightness(1.1)'] }
                        : todayGlowing ? { scale: [1, 1.2, 1], rotate: [-6, 6, -6] }
                        : isLit ? { scale: [1, 1.15, 1], y: [0, -2, 0] } : {}}
                      transition={justLit ? { duration: 0.9, delay: 0.2 + i * 0.05 }
                        : todayGlowing ? { duration: 1.1, repeat: Infinity, ease: 'easeInOut' }
                        : isLit ? { duration: 1.4, repeat: Infinity, delay: i * 0.14 } : {}}
                    >🔥</motion.span>
                  </div>
                  <span className={`text-[10px] font-black leading-none mt-1 ${isTodayDay ? 'text-foreground' : 'text-muted-foreground/60'}`}>
                    {day}
                  </span>
                  {isTodayDay && (
                    <motion.div className="absolute -bottom-2 w-2 h-2 rounded-full bg-duo-orange"
                      animate={{ scale: [1, 1.6, 1] }} transition={{ duration: 1.2, repeat: Infinity }} />
                  )}
                </motion.div>
              );
            })}
          </div>
        </motion.div>

        {/* ── Stars + Diamonds + Level row ── */}
        <motion.div className="flex items-center gap-1.5 mb-1.5"
          initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.3, delay: 0.22 }}>
          <div className="flex-1" />
          {/* Stars */}
          <motion.div animate={starsBounce ? { scale: [1, 1.4, 1] } : {}}
            className="flex items-center gap-0.5 rounded-full px-2 py-0.5"
            style={{ background: 'hsl(42 100% 92%)', border: '2px solid hsl(42 100% 80%)' }}>
            <span className="text-xs">⭐</span>
            <span className="font-black text-[11px]" style={{ color: 'hsl(42 100% 30%)' }}>{child.stars}</span>
          </motion.div>
          {/* Diamonds */}
          <motion.div animate={diamondsBounce ? { scale: [1, 1.4, 1] } : {}}
            className="flex items-center gap-0.5 rounded-full px-2 py-0.5"
            style={{ background: 'hsl(199 92% 92%)', border: '2px solid hsl(199 92% 75%)' }}>
            <span className="text-xs">💎</span>
            <span className="font-black text-[11px]" style={{ color: 'hsl(199 92% 30%)' }}>{child.diamonds}</span>
          </motion.div>
          {/* Level */}
          <div className="flex items-center gap-0.5 rounded-full px-2 py-0.5"
            style={{ background: 'hsl(280 60% 94%)', border: '2px solid hsl(280 60% 80%)' }}>
            <span className="text-xs">🎖️</span>
            <span className="font-black text-[11px]" style={{ color: 'hsl(280 60% 30%)' }}>{t('kidHome.levelShort')}{child.level}</span>
          </div>
        </motion.div>
      </motion.div>

      {/* ── Scrollable content ── */}
      <div className="flex-1 overflow-y-auto overflow-x-hidden pb-24">

      {/* ── Avatar Showcase ── */}
      <motion.div className="relative z-10 flex flex-col items-center pt-2 pb-1 mb-0"
        initial={{ scale: 0.8, opacity: 0 }} animate={{ scale: 1, opacity: 1 }}
        transition={{ type: 'spring', stiffness: 300, damping: 20, delay: 0.1 }}>
        <motion.div className="cursor-pointer" whileTap={{ scale: 0.95 }}
          onClick={() => { playTap(); navigate('/child/shop'); }}>
          <ChildAvatar avatar={child.avatar} name={child.name} equippedItems={equippedItems} size="lg" animate showEffects fullBody />
        </motion.div>

        {/* Tummy gauge — visible at home so kids understand they need to feed */}
        <motion.div
          initial={{ opacity: 0, y: 6 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ delay: 0.3 }}
          onClick={() => { playTap(); navigate('/child/shop'); }}
          className="mt-2 w-[210px] max-w-[78vw] cursor-pointer px-3 py-1.5 rounded-2xl bg-card/90 backdrop-blur stell-shadow border-2 border-border"
        >
          <HungerGauge value={child.fullness ?? 50} size="sm" />
        </motion.div>

        {/* Currently wearing chips — makes equipped gear visible at a glance */}
        {equippedItems.length > 0 && (
          <motion.div
            initial={{ opacity: 0, y: 4 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.25 }}
            className="flex items-center gap-1 mt-1 flex-wrap justify-center max-w-[80%]">
            {equippedItems.slice(0, 6).map((item, i) => (
              <motion.span key={item.id}
                initial={{ scale: 0 }} animate={{ scale: 1 }}
                transition={{ delay: 0.3 + i * 0.05, type: 'spring', stiffness: 400, damping: 18 }}
                className="text-sm rounded-full w-6 h-6 flex items-center justify-center border-2"
                style={{
                  background: 'hsl(0 0% 100% / 0.85)',
                  borderColor: 'hsl(108 68% 60%)',
                  boxShadow: '0 2px 0 0 hsl(108 68% 45% / 0.3)',
                }}>
                {item.emoji}
              </motion.span>
            ))}
          </motion.div>
        )}

        <div className="mt-1 flex items-center gap-2">
          <motion.button
            initial={{ opacity: 0, y: 5 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.2 }}
            whileTap={{ scale: 0.92, y: 2 }}
            onClick={() => { playTap(); navigate('/child/shop'); }}
            className="flex items-center gap-1 text-[10px] font-black text-white rounded-full px-3 py-1 border-2 border-transparent"
            style={{ background: 'hsl(108 68% 46%)', boxShadow: '0 3px 0 0 hsl(108 68% 32%)' }}
          >
            <span>👗</span> {equippedItems.length > 0 ? t('kidHome.changeOutfit') : t('kidHome.dressUp')}
          </motion.button>
          <motion.button
            initial={{ opacity: 0, y: 5 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.25 }}
            whileTap={{ scale: 0.92, y: 2 }}
            onClick={() => { playTap(); navigate('/child/profile'); }}
            className="flex items-center gap-1 text-[10px] font-black text-white rounded-full px-3 py-1 border-2 border-transparent"
            style={{ background: 'hsl(280 60% 55%)', boxShadow: '0 3px 0 0 hsl(280 60% 38%)' }}
          >
            <span>😊</span> {t('kidHome.changeMyFace')}
          </motion.button>
        </div>
      </motion.div>

      {/* ── Progress Bar ── */}
      <div className="relative z-10 px-4 mb-1.5">
        <div className="relative rounded-full overflow-hidden h-3.5 border-2"
          style={{ background: 'hsl(108 68% 92%)', borderColor: 'hsl(108 68% 75%)' }}>
          <motion.div className="h-full rounded-full relative overflow-hidden"
            style={{ background: 'linear-gradient(90deg, hsl(108 68% 50%), hsl(108 68% 42%))' }}
            initial={{ width: 0 }} animate={{ width: `${progressPercent}%` }}
            transition={{ duration: 0.8, ease: 'easeOut' }}>
            <motion.div animate={{ x: ['-100%', '200%'] }}
              transition={{ duration: 2, repeat: Infinity, repeatDelay: 5 }}
              className="absolute inset-0 bg-gradient-to-r from-transparent via-white/40 to-transparent" />
          </motion.div>
          <div className="absolute inset-0 flex items-center justify-center">
            <span className="text-[10px] font-black text-foreground drop-shadow-sm">
              {completedToday}/{totalMissions}
            </span>
          </div>
        </div>
      </div>

      {/* ── Section Title ── */}
      <div className="relative z-10 px-4 mb-2">
        <motion.h2 initial={{ opacity: 0, y: 5 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.15 }}
          className={`font-black ${ageGroup === 'young' ? 'text-lg' : 'text-base'} text-foreground`}>
          {ageGroup === 'young' ? t('kidHome.todaysAdventures') : t('kidHome.myMissions')}
        </motion.h2>
      </div>

      {/* ── Mission Grid ── */}
      <motion.div className="relative z-10 px-2 sm:px-4 flex-1"
        key={`grid-${missions.length}`}
        variants={entrance.container} initial="hidden" animate="show">
        <div className="grid gap-1.5 sm:gap-2 auto-rows-fr grid-cols-2 sm:grid-cols-3">
          {/* Render in stable order: keep original mission position so grid doesn't shuffle on validation */}
          {missions
            .filter(m => m.status === 'active' || m.status === 'awaiting_validation' || m.status === 'validated')
            .map((mission, i) => {
              const isActive = mission.status === 'active';
              const isPending = mission.status === 'awaiting_validation';
              return (
                <motion.div key={mission.id} layout="position" variants={entrance.item} initial="hidden" animate="show" transition={{ layout: { type: 'spring', stiffness: 300, damping: 28 } }} className="h-full">
                  <MissionCard
                    mission={mission}
                    index={i}
                    ageGroup={ageGroup}
                    onTap={isActive ? handleMissionTap : () => {}}
                    parentName={isPending ? parentName : undefined}
                    parentAvatar={isPending ? parentAvatar : undefined}
                  />
                </motion.div>
              );
            })}
        </div>

        {/* All done celebration */}
        {totalMissions > 0 && completedToday === totalMissions && (
          <motion.div initial={{ opacity: 0, scale: 0.9 }} animate={{ opacity: 1, scale: 1 }}
            transition={{ delay: 0.3, type: 'spring' }}
            className="mt-6 text-center rounded-2xl p-5 border-2"
            style={{
              background: 'linear-gradient(135deg, hsl(42 100% 94%), hsl(108 68% 94%))',
              borderColor: 'hsl(42 100% 75%)',
              boxShadow: '0 4px 0 0 hsl(42 100% 70% / 0.3)',
            }}>
            <motion.span className="text-5xl block mb-2"
              animate={{ rotate: [0, 10, -10, 0], scale: [1, 1.1, 1] }}
              transition={{ duration: 2, repeat: Infinity }}>🏆</motion.span>
            <p className={`font-black ${ageGroup === 'young' ? 'text-lg' : 'text-base'}`}
              style={{ color: 'hsl(42 100% 30%)' }}>
              {ageGroup === 'young' ? t('kidHome.youDidIt') : t('kidHome.allMissionsComplete')}
            </p>
            <p className="text-xs font-bold mt-1" style={{ color: 'hsl(42 100% 40%)' }}>
              {t('kidHome.comeBackTomorrow')}
            </p>
          </motion.div>
        )}

        {missions.length === 0 && (
          <motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }}
            className="text-center mt-12 rounded-2xl p-6 border-2"
            style={{
              background: 'hsl(0 0% 100% / 0.7)',
              borderColor: 'hsl(220 14% 88%)',
              boxShadow: '0 4px 0 0 hsl(220 14% 84% / 0.3)',
            }}>
            <motion.span className="text-6xl block mb-4" animate={{ y: [0, -8, 0] }} transition={{ duration: 2, repeat: Infinity }}>
              🌙
            </motion.span>
            <p className={`font-black text-muted-foreground ${ageGroup === 'young' ? 'text-lg' : 'text-sm'}`}>
              {ageGroup === 'young' ? t('kidHome.noMissionsSoon') : t('kidHome.noMissionsNow')}
            </p>
            <p className="text-xs text-muted-foreground font-bold mt-1">{t('kidHome.parentWillSetUp')}</p>
          </motion.div>
        )}
      </motion.div>
      </div>{/* end scrollable content */}

      {/* Overlays */}
      <AnimatePresence>
        {showPrevention && <PreventionOverlay message={showPrevention} ageGroup={ageGroup} />}
      </AnimatePresence>
      <AnimatePresence>
        {showSupports && (() => {
          const m = missions.find(x => x.id === showSupports);
          if (!m) return null;
          const supportLevel = computeSupportLevel(missions, child?.id);
          if (supportLevel === 0) return null;
          return (
            <MissionSupportsOverlay
              mission={m}
              childId={child?.id || ''}
              ageGroup={ageGroup}
              supportLevel={supportLevel as 1 | 2}
              onContinue={() => {
                const id = showSupports;
                setShowSupports(null);
                setShowScratch(id);
              }}
            />
          );
        })()}
      </AnimatePresence>
      <AnimatePresence>
        {showScratch && (() => {
          const m = missions.find(x => x.id === showScratch);
          const domain = inferMissionDomain(m?.title ?? '');
          return (
            <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
              className="fixed inset-0 z-50 bg-foreground/60 backdrop-blur-sm flex items-center justify-center p-4">
              <ScratchCard onComplete={() => onScratchComplete(showScratch)} stars={3} diamonds={1} childAge={child.age} domain={domain} />
            </motion.div>
          );
        })()}
      </AnimatePresence>
      <AnimatePresence>
        {showRating && <MissionRatingOverlay ageGroup={ageGroup} onRate={() => rateMission(showRating!)} />}
      </AnimatePresence>

      {celebration && (
        <CelebrationOverlay missionTitle={celebration.missionTitle} missionEmoji={celebration.missionEmoji}
          starsEarned={celebration.starsEarned} diamondsEarned={celebration.diamondsEarned}
          ageGroup={ageGroup} onDismiss={dismissCelebration} />
      )}

      <ChildNavBar ageGroup={ageGroup} parentAvatar={parentAvatar} />
    </div>
    </>
  );
};

export default ChildMissionBoard;