// Kid view — the hero of the product.
// Wired to real store: tap/drag a task tile → store.markAction; drag/drop into
// jars persists to the DB via store.adjustJar / transferJar; redeem fires
// store.redeemJar which spends the stars and logs a history row.

function KidView() {
  const s = window.useStore();
  const [kidId, setKidId] = React.useState(() => s.kids[0]?.id || null);

  // Resync kidId when kids list changes (e.g. on first hydrate)
  React.useEffect(() => {
    if (!kidId && s.kids[0]) setKidId(s.kids[0].id);
  }, [s.kids, kidId]);

  // Preload the bundled mascot chirps once so the first reaction is instant.
  // Stop any in-flight chirp on unmount or kid switch so it doesn't outlive
  // the mascot animation.
  React.useEffect(() => { window.preloadMascotSounds?.(); }, []);
  React.useEffect(() => () => { window.stopMascotSound?.(); }, [kidId]);

  const kid = s.kids.find(k => k.id === kidId) || s.kids[0];

  if (!kid) return null;
  const c = window.KID_COLORS_V3[kid.color] || window.KID_COLORS_V3.mango;
  const points = Math.max(0, kid.points || 0);

  // Real DB-backed jars for this kid, in display order.
  const kidJars = React.useMemo(
    () => s.jars.filter(j => j.kid_id === kid.id).slice().sort((a, b) => (a.sortOrder - b.sortOrder) || (a.createdAt || '').localeCompare(b.createdAt || '')),
    [s.jars, kid.id]
  );

  // Pile = total points minus stars currently sitting inside jars. kid.points
  // only drops when a jar is redeemed (handled by store.redeemJar).
  const jarred = kidJars.reduce((a, j) => a + (j.current || 0), 0);
  const pileCount = Math.max(0, points - jarred);

  // We render at most this many stars in the pile; if there are more, we hint
  // the kid to put some in a jar to make space.
  const PILE_MAX_VISIBLE = 20;

  // Stable star characters — each keeps its own pose, size, and animation
  // timing so they all dance independently. We only add/remove from the end
  // when pileCount changes, never re-shuffle existing stars.
  const [pileItems, setPileItems] = React.useState([]);
  const lastKidRef = React.useRef(kid.id);
  React.useEffect(() => {
    const kidChanged = lastKidRef.current !== kid.id;
    lastKidRef.current = kid.id;
    setPileItems(items => {
      const base = kidChanged ? [] : items;
      const target = Math.min(pileCount, PILE_MAX_VISIBLE);
      if (base.length === target) return kidChanged ? base : items;
      if (base.length > target)   return base.slice(0, target);
      const poses = window.STAR_POSES || ['happy'];
      // Spread repeats apart: a pose can't appear within RECENT_MEMORY of itself.
      // For 15 poses we remember the last ~7 so reuses are at least 8 stars away,
      // making the pile feel diverse even at 20 stars.
      const RECENT_MEMORY = Math.max(1, Math.min(7, poses.length - 1));
      const styles = ['sway', 'pulse', 'wobble', 'spin'];
      const next = base.slice();
      for (let i = next.length; i < target; i++) {
        const recentPoses = next.slice(-RECENT_MEMORY).map(s => s.pose);
        const posePool = poses.filter(p => !recentPoses.includes(p));
        const fromPoses = posePool.length > 0 ? posePool : poses;
        const pose = fromPoses[Math.floor(Math.random() * fromPoses.length)];
        // Also keep adjacent motion-styles different so it doesn't look synced.
        const prevStyle = next[next.length - 1]?.style;
        let style;
        for (let t = 0; t < 6; t++) {
          style = styles[Math.floor(Math.random() * styles.length)];
          if (style !== prevStyle) break;
        }
        next.push({
          id: Date.now() + i + Math.random(),
          pose,
          size: 70 + Math.round(Math.random() * 22),     // 70–92 (fits 20 in pile)
          dur:  1.0 + Math.random() * 1.0,               // 1.0–2.0s
          delay: Math.random() * 0.8,                    // 0–0.8s
          tilt: Math.round(8 + Math.random() * 10),      // 8°–18°
          dir:  Math.random() < 0.5 ? -1 : 1,            // rotation direction
          style,
        });
      }
      return next;
    });
  }, [pileCount, kid.id]);

  const [jarMoods, setJarMoods] = React.useState({});
  const jarMoodTimer = React.useRef(null);

  const burstJarMoods = React.useCallback((moods, duration = 1500) => {
    setJarMoods(moods);
    if (jarMoodTimer.current) clearTimeout(jarMoodTimer.current);
    jarMoodTimer.current = setTimeout(() => setJarMoods({}), duration);
  }, []);

  const previewJarMoods = React.useCallback((targetJarId) => {
    const moods = {};
    kidJars.forEach(j => {
      moods[j.id] = j.id === targetJarId ? 'excited' : 'envy';
    });
    setJarMoods(moods);
  }, [kidJars]);

  const triggerJarReceive = React.useCallback((targetJarId) => {
    const moods = {};
    kidJars.forEach(j => { moods[j.id] = j.id === targetJarId ? 'happy' : 'envy'; });
    burstJarMoods(moods, 1600);
  }, [kidJars, burstJarMoods]);

  const triggerJarTransfer = React.useCallback((fromId, toId) => {
    const moods = {};
    kidJars.forEach(j => {
      if (j.id === toId) moods[j.id] = 'happy';
      else if (j.id === fromId) moods[j.id] = 'sad';
      else moods[j.id] = 'envy';
    });
    burstJarMoods(moods, 1600);
  }, [kidJars, burstJarMoods]);

  const triggerAllJarsSad = React.useCallback(() => {
    const moods = {};
    kidJars.forEach(j => { moods[j.id] = 'sad'; });
    burstJarMoods(moods, 1800);
  }, [kidJars, burstJarMoods]);

  const triggerJarLose = React.useCallback((jarId) => {
    burstJarMoods({ [jarId]: 'sad' }, 1400);
  }, [burstJarMoods]);

  React.useEffect(() => () => {
    if (jarMoodTimer.current) clearTimeout(jarMoodTimer.current);
  }, []);

  const pileFull = pileCount > PILE_MAX_VISIBLE;

  const [dragKind, setDragKind] = React.useState(null);
  const [overTarget, setOverTarget] = React.useState(null);
  const [pulse, setPulse] = React.useState(null);
  const [mood, setMood] = React.useState('happy');
  const [bubble, setBubble] = React.useState("Look at all your stars!");
  const [dancing, setDancing] = React.useState(false);
  const [danceMove, setDanceMove] = React.useState('dance');
  const [danceOffset, setDanceOffset] = React.useState({ x: -700, y: 320 });
  // sadReaction: null | 'cry' | 'walk' | 'sit-back' — picked at random when
  // something bad happens so kids see different little stories each time.
  const [sadReaction, setSadReaction] = React.useState(null);
  // happyReaction: null | 'dance' | 'run' | 'style' — picked at random when
  // something good happens.
  const [happyReaction, setHappyReaction] = React.useState(null);
  // Stars currently animating out of the pile after a bad-task tap. Each
  // entry is a fully-described, absolutely-positioned falling star which is
  // garbage-collected from state once its animation ends.
  const [fallingStars, setFallingStars] = React.useState([]);
  const mascotWrapRef = React.useRef(null);
  const pileRef = React.useRef(null);

  const sayBriefly = (msg, m = 'cheer') => {
    setMood(m);
    setBubble(msg);
    clearTimeout(window.__suriBubbleTo);
    window.__suriBubbleTo = setTimeout(() => { setMood('happy'); setBubble("Look at all your stars!"); }, 2800);
  };

  const DANCE_MOVES = ['dance', 'kiss', 'flex', 'wink'];

  // celebrate() still rains stars + flies the corner mascot toward the pile.
  // The big overlay variants are an *additional* layer on top.
  const celebrate = (n) => {
    window.sprinkleStars({ count: 24 + n * 6 });
    if (mascotWrapRef.current && pileRef.current) {
      const m = mascotWrapRef.current.getBoundingClientRect();
      const p = pileRef.current.getBoundingClientRect();
      const dx = (p.left + p.width * 0.5) - (m.left + m.width / 2);
      const dy = (p.top  - 30)             - (m.top  + m.height / 2);
      setDanceOffset({ x: dx, y: dy });
    }
    const pick = DANCE_MOVES[Math.floor(Math.random() * DANCE_MOVES.length)];
    setDanceMove(pick);
    setDancing(true);
    clearTimeout(window.__suriDanceTo);
    window.__suriDanceTo = setTimeout(() => setDancing(false), 7100);
  };

  // ── Star drop animation ──────────────────────────────────────────────
  // When the kid taps a "bad" task that subtracts N points, we visually
  // launch N stars out of the pile, falling off the bottom of the screen.
  // Pure visuals — the actual point math happens in s.markAction.
  const STAR_POSES = window.STAR_POSES || ['happy','wink','peace','flex','sleepy'];
  const dropStarsFromPile = (count) => {
    if (count <= 0 || !pileRef.current) return;
    const r = pileRef.current.getBoundingClientRect();
    const startX = r.left + r.width * 0.5;
    const startY = r.top  + r.height * 0.4;
    const newStars = Array.from({ length: count }, (_, i) => ({
      id: `drop-${Date.now()}-${i}-${Math.random().toString(36).slice(2,7)}`,
      x: startX + (Math.random() - 0.5) * (r.width * 0.55),
      y: startY,
      drift: (Math.random() - 0.5) * 220,
      spin: (Math.random() > 0.5 ? 1 : -1) * (200 + Math.random() * 540),
      delay: i * 110 + Math.random() * 60,
      size: 40 + Math.random() * 10,
      pose: STAR_POSES[Math.floor(Math.random() * STAR_POSES.length)],
    }));
    setFallingStars(prev => [...prev, ...newStars]);
    const maxMs = (newStars[newStars.length - 1]?.delay || 0) + 2400;
    setTimeout(() => {
      const ids = new Set(newStars.map(s => s.id));
      setFallingStars(prev => prev.filter(s => !ids.has(s.id)));
    }, maxMs);
  };

  const SAD_VARIANTS = ['cry', 'walk', 'sit-back'];
  const SAD_DURATIONS = { cry: 4800, walk: 8100, 'sit-back': 5000 };
  // Each sad variant gets a matching mascot chirp pool.
  const SAD_SOUNDS = { cry: 'sniff', walk: 'aww', 'sit-back': 'ohno' };
  const goSad = () => {
    setDancing(false);
    setHappyReaction(null);
    const variant = SAD_VARIANTS[Math.floor(Math.random() * SAD_VARIANTS.length)];
    setSadReaction(variant);
    // Chirp loops for the whole reaction duration (minus a tiny tail).
    window.playMascotSound?.(SAD_SOUNDS[variant], { duration: SAD_DURATIONS[variant] - 200 });
    clearTimeout(window.__suriSadTo);
    window.__suriSadTo = setTimeout(() => setSadReaction(null), SAD_DURATIONS[variant]);
  };

  // 'corner' = the original fly-to-pile corner dance (no overlay) — kept
  // because it's adorable. Other three are the big screen-wide overlays.
  // Each variant appears once and maps to a *different* chirp so kids actually
  // hear the full happy chirp pool (cheer / oyii / oho) instead of cheer 60%
  // of the time.
  const HAPPY_VARIANTS = ['corner', 'dance', 'run', 'style'];
  const HAPPY_DURATIONS = { corner: 7200, dance: 5200, run: 7700, style: 8200 };
  const HAPPY_SOUNDS    = { corner: 'cheer', dance: 'oho', run: 'oyii', style: 'cheer' };
  const goHappy = () => {
    setSadReaction(null);
    const variant = HAPPY_VARIANTS[Math.floor(Math.random() * HAPPY_VARIANTS.length)];
    setHappyReaction(variant);
    // Chirp loops for the whole reaction duration (minus a tiny tail).
    window.playMascotSound?.(HAPPY_SOUNDS[variant], { duration: HAPPY_DURATIONS[variant] - 200 });
    clearTimeout(window.__suriHappyTo);
    window.__suriHappyTo = setTimeout(() => setHappyReaction(null), HAPPY_DURATIONS[variant]);
  };

  // Speak using SpeechSynthesis (the simple, free path)
  const speak = (text) => {
    if (s.settings?.speech === false) return;
    window.speak?.(text);
  };

  const handleTaskAction = async (task) => {
    const n = task.points; // signed
    try {
      await s.markAction({
        kidId: kid.id,
        amount: n,
        reason: task.label,
        emoji: task.emoji,
        kind: task.kind,
      });
      if (n > 0) {
        sayBriefly(`Plus ${n} ${n === 1 ? 'star' : 'stars'}!`, 'cheer');
        celebrate(n);
        goHappy();
      } else {
        sayBriefly(`Aw — ${Math.abs(n)} ${Math.abs(n) === 1 ? 'star' : 'stars'} gone…`, 'sad');
        dropStarsFromPile(Math.abs(n));
        goSad();
        triggerAllJarsSad();
      }
    } catch (e) {
      console.error(e);
    }
  };

  // Drag/drop:
  //   task tile  → pile / jar : log the task; if good + jar target, also stash
  //                              the freshly-earned stars in that jar.
  //   pile star  → jar         : adjustJar(jar, +1)
  //   jar star   → other jar   : transferJar(from, to, 1)
  //   jar star   → pile        : adjustJar(jar, -1)
  const onTaskDragStart = (e, task) => {
    setDragKind('task');
    e.dataTransfer.effectAllowed = 'copy';
    e.dataTransfer.setData('text/plain', JSON.stringify({ k: 'task', id: task.id }));
  };
  // source is either 'pile' (from the star pile) or a jar id.
  const onCoinDragStart = (e, source) => {
    setDragKind('coin');
    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/plain', JSON.stringify({ k: 'coin', source }));
  };
  const onDragEnd = () => {
    setDragKind(null);
    setOverTarget(null);
    setJarMoods({});
  };
  const onTargetOver = (e, target) => {
    e.preventDefault();
    setOverTarget(target);
    if (target !== 'pile' && (dragKind === 'coin' || dragKind === 'task')) {
      previewJarMoods(target);
    }
  };
  const onTargetLeave = () => {
    setOverTarget(null);
    if (dragKind === 'coin' || dragKind === 'task') setJarMoods({});
  };

  const onTargetDrop = async (e, target) => {
    e.preventDefault();
    setOverTarget(null);
    let data = {};
    try { data = JSON.parse(e.dataTransfer.getData('text/plain')); } catch (_) { return; }

    if (data.k === 'task') {
      const task = s.tasks.find(t => t.id === data.id);
      if (!task) return;
      await handleTaskAction(task);
      if (task.kind === 'good' && target !== 'pile') {
        const jar = kidJars.find(j => j.id === target);
        if (jar) {
          await s.adjustJar(target, Math.abs(task.points));
          sayBriefly('Saved straight into the jar!', 'proud');
          triggerJarReceive(target);
        }
      }
      setPulse(target);
      setTimeout(() => setPulse(null), 600);
    } else if (data.k === 'coin') {
      const fromPile = !data.source || data.source === 'pile';
      if (fromPile && target !== 'pile') {
        if (pileCount <= 0) return;
        await s.adjustJar(target, +1);
        sayBriefly('Star jarred!', 'proud');
        celebrate(1);
        triggerJarReceive(target);
        setPulse(target);
      }
      else if (!fromPile && target === 'pile') {
        await s.adjustJar(data.source, -1);
        sayBriefly('Back in the pile!', 'happy');
        triggerJarLose(data.source);
        setPulse('pile');
      }
      else if (!fromPile && target !== 'pile' && data.source !== target) {
        await s.transferJar(data.source, target, 1);
        sayBriefly('Moved!', 'proud');
        triggerJarTransfer(data.source, target);
        setPulse(target);
      }
      setTimeout(() => setPulse(null), 600);
    }
  };

  const handleRedeem = async (jar) => {
    if (jar.current <= 0) return;
    if (!window.confirm(`Mark "${jar.label}" as used? ${jar.current} ${jar.current === 1 ? 'star' : 'stars'} will be spent.`)) return;
    try {
      await s.redeemJar(jar.id);
      sayBriefly(`Enjoy your ${jar.label}!`, 'cheer');
      celebrate(2);
    } catch (e) {
      console.error(e);
    }
  };

  const kidTasks = s.tasks.filter(t => t.kid_id === kid.id);
  // Show the whole list — kids may have lots of tasks now. The tab UI
  // keeps it compact even when the lists are long.
  const goodTasks = kidTasks.filter(t => t.kind === 'good');
  const badTasks  = kidTasks.filter(t => t.kind === 'bad' );

  // 'good' | 'bad' — which list is currently visible
  const [taskTab, setTaskTab] = React.useState('good');

  const taskTints = [
    { bg: '#FFE3D3', stroke: '#FF6B2C', emoji: '#FF6B2C' },
    { bg: '#C2F0E3', stroke: '#00C896', emoji: '#00A176' },
    { bg: '#DAE0FE', stroke: '#6979F8', emoji: '#4E5DD8' },
    { bg: '#FFE0E8', stroke: '#FFB7C8', emoji: '#C73E8B' },
    { bg: '#FFF1B8', stroke: '#FFD23F', emoji: '#B98A00' },
    { bg: '#F8D2E7', stroke: '#C73E8B', emoji: '#C73E8B' },
  ];

  return (
    <div style={{ background: 'var(--paper)', minHeight: 'calc(100vh - 73px)', paddingBottom: 80 }}>
      <KidViewStyles />
      <ReactionStyles />

      <div className="kv-hero">
        <div className="kv-hero-bg-dots" />
        <div className="kv-hero-inner">
          {/* Compact kid switcher — just avatars, no name */}
          {s.kids.length > 1 && (
            <div className="kv-kid-pick">
              <span className="kv-kid-pick-label">Who is it?</span>
              {s.kids.map(k => {
                const kc = window.KID_COLORS_V3[k.color];
                const on = k.id === kid.id;
                return (
                  <button key={k.id} onClick={() => setKidId(k.id)}
                    title={k.name}
                    className={`kv-kid-dot ${on ? 'on' : ''}`}
                    style={{ '--ring': kc.hex }}>
                    <window.KidAvatar kid={k} />
                  </button>
                );
              })}
            </div>
          )}

          <div className="kv-greeting">
            <window.KidAvatar kid={kid} size="xl" />
            <div className="kv-greeting-text">
              <div className="kv-hi">Hi {kid.name} 👋</div>
              <h1 className="kv-headline">
                <span>You have </span>
                <span style={{ color: c.hex }}>{points}</span>
                <span> {points === 1 ? 'star' : 'stars'}</span>
              </h1>
              <div style={{ display: 'flex', gap: 10, marginTop: 14, flexWrap: 'wrap' }}>
                {kid.streak > 0 && (
                  <span className="chip chip-lg" style={{ background: 'rgba(255,210,63,0.2)', color: 'var(--butter)', backdropFilter: 'blur(8px)' }}>
                    🔥 {kid.streak} day streak
                  </span>
                )}
                <span className="chip chip-lg" style={{ background: 'rgba(255,255,255,0.08)', color: 'white', backdropFilter: 'blur(8px)' }}>
                  {Math.max(0, (kid.goalAt || kid.goal || 50) - points)} to next reward
                </span>
              </div>
            </div>
          </div>

          {/* Mascot — absolutely placed in the hero corner so it never wraps
              and its legs poke out cleanly into the cream area below.
              The base mascot fades only when a screen-wide overlay plays.
              For 'corner' happy variant we keep the corner mascot visible so
              the original fly-to-pile dance still gets its moment. */}
          <div className={`kv-mascot ${dancing ? 'is-dancing' : ''}`} ref={mascotWrapRef}
               style={(sadReaction || (happyReaction && happyReaction !== 'corner'))
                 ? { opacity: 0, pointerEvents: 'none', transition: 'opacity 200ms ease' }
                 : { transition: 'opacity 200ms ease' }}>
            <div
              className={dancing ? 'suri-dancing' : 'mascot-idle'}
              style={dancing ? { '--tx': `${danceOffset.x}px`, '--ty': `${danceOffset.y}px` } : undefined}
              key={dancing ? 'dance-' + danceMove : 'idle'}>
              <window.Suri
                mood={dancing ? danceMove : mood}
                size={140}
                color={c.hex} />
            </div>
            {!dancing && (
              <div className="kv-bubble pop-in" key={bubble}>
                <window.SuriBubble tail="left">{bubble}</window.SuriBubble>
              </div>
            )}
          </div>
        </div>
      </div>

      {/* Falling stars — one per minus point, launched from the pile and
          tumbling off the bottom of the screen. */}
      {fallingStars.length > 0 && (
        <div className="kv-falling-layer">
          {fallingStars.map(s => (
            <div key={s.id}
              className="kv-falling-star"
              style={{
                left: s.x, top: s.y,
                width: s.size, height: s.size,
                animationDelay: `${s.delay}ms`,
                '--dx': `${s.drift}px`,
                '--spin': `${s.spin}deg`,
              }}>
              <window.StarChar pose={s.pose} size={s.size} />
            </div>
          ))}
        </div>
      )}

      {/* Sad-reaction overlays — only one at a time */}
      {sadReaction === 'cry'      && <SadCry      color={c.hex} />}
      {sadReaction === 'walk'     && <SadWalk     color={c.hex} />}
      {sadReaction === 'sit-back' && <SadSitBack  color={c.hex} />}

      {/* Happy-reaction overlays — only one at a time */}
      {happyReaction === 'dance'  && <HappyDance  color={c.hex} kidName={kid.name} />}
      {happyReaction === 'run'    && <HappyRun    color={c.hex} />}
      {happyReaction === 'style'  && <HappyStyle  color={c.hex} />}

      <div className="kv-stage">
        <section className="kv-pile-tasks" style={{ marginBottom: 56 }}>
          <div>
            <div className="kv-section-head">
              <div>
                <div className="eyebrow">Your stars</div>
                <h2 className="h-display" style={{ fontSize: 42, marginTop: 8 }}>The star pile</h2>
              </div>
            </div>
            <div ref={pileRef} className={`kv-pile ${overTarget === 'pile' ? 'over' : ''} ${dragKind === 'task' ? 'highlight' : ''}`}
              onDragOver={(e) => onTargetOver(e, 'pile')}
              onDragLeave={onTargetLeave}
              onDrop={(e) => onTargetDrop(e, 'pile')}>
              <div className="kv-pile-count" style={{ color: c.hex }}>
                {pileCount}
              </div>
              <div className="kv-pile-stack">
                {pileItems.map((st) => (
                  <div key={st.id}
                    className={`kv-star kv-star--${st.style}`}
                    draggable
                    onDragStart={(e) => onCoinDragStart(e, 'pile')}
                    onDragEnd={onDragEnd}
                    style={{
                      width: st.size + 6, height: st.size + 6,
                      animationDuration: `${st.dur}s`,
                      animationDelay: `${st.delay}s`,
                      '--rot-a': `${-st.tilt * st.dir}deg`,
                      '--rot-b': `${ st.tilt * st.dir}deg`,
                    }}>
                    <window.StarChar pose={st.pose} size={st.size} />
                  </div>
                ))}
                {pileCount === 0 && (
                  <div className="kv-pile-empty">
                    Empty. Tap or drag a task tile →
                  </div>
                )}
              </div>
              {pileFull && (
                <div className="kv-pile-fullhint">
                  ✨ Wow, so many stars! Pop some into a jar below to make room for more.
                </div>
              )}
              <div className="kv-pile-cta">
                {dragKind === 'task' ? '✨ Drop here to earn' :
                  kidTasks.length === 0 ? 'Ask a grown-up to add tasks first.' :
                  pileCount > 0 ? 'Drag a star to a jar below ↓' : 'Do a task to earn stars ✨'}
              </div>
            </div>
          </div>

          <div>
            <div className="kv-section-head">
              <div>
                <div className="eyebrow">What did you do?</div>
                <h2 className="h-display" style={{ fontSize: 42, marginTop: 8 }}>Tap or drag</h2>
              </div>
              {/* Tab toggle — switches between long good / bad lists */}
              <div className="kv-task-tabs" role="tablist">
                <button
                  className={`kv-task-tab ${taskTab === 'good' ? 'on' : ''}`}
                  onClick={() => setTaskTab('good')}
                  role="tab" aria-selected={taskTab === 'good'}>
                  👍 Good <span className="kv-task-tab-count">{goodTasks.length}</span>
                </button>
                <button
                  className={`kv-task-tab kv-task-tab--bad ${taskTab === 'bad' ? 'on' : ''}`}
                  onClick={() => setTaskTab('bad')}
                  role="tab" aria-selected={taskTab === 'bad'}>
                  😟 Not so great <span className="kv-task-tab-count">{badTasks.length}</span>
                </button>
              </div>
            </div>
            {kidTasks.length === 0 ? (
              <div className="card" style={{ padding: 24, textAlign: 'center', color: 'var(--ink-soft)' }}>
                No tasks yet. <a href="#/tasks" style={{ color: 'var(--brand)', fontWeight: 700 }}>Add some →</a>
              </div>
            ) : taskTab === 'good' ? (
              goodTasks.length === 0 ? (
                <div className="kv-tasks-empty">No good tasks yet — add some from the Tasks page.</div>
              ) : (
                <div className="kv-tasks" key="good">
                  {goodTasks.map((t, i) => {
                    const tint = taskTints[i % taskTints.length];
                    return (
                      <button key={t.id}
                        draggable
                        onClick={() => handleTaskAction(t)}
                        onDragStart={(e) => onTaskDragStart(e, t)}
                        onDragEnd={onDragEnd}
                        className="kv-task-tile"
                        style={{ background: tint.bg, borderColor: tint.stroke }}>
                        <span className="kv-task-emoji">{t.emoji}</span>
                        <span className="kv-task-label">{t.label}</span>
                        <span className="kv-task-delta" style={{ color: tint.emoji }}>+{t.points}</span>
                      </button>
                    );
                  })}
                </div>
              )
            ) : (
              badTasks.length === 0 ? (
                <div className="kv-tasks-empty">No "not so great" tasks set up. Phew!</div>
              ) : (
                <div className="kv-tasks" key="bad">
                  {badTasks.map(t => (
                    <button key={t.id}
                      draggable
                      onClick={() => handleTaskAction(t)}
                      onDragStart={(e) => onTaskDragStart(e, t)}
                      onDragEnd={onDragEnd}
                      className="kv-task-tile kv-task-tile--bad"
                      style={{ background: 'var(--blush-soft)', borderColor: 'var(--alert)' }}>
                      <span className="kv-task-emoji">{t.emoji}</span>
                      <span className="kv-task-label">{t.label}</span>
                      <span className="kv-task-delta" style={{ color: 'var(--alert)' }}>{t.points}</span>
                    </button>
                  ))}
                </div>
              )
            )}
          </div>
        </section>

        <section>
          <div className="kv-section-head">
            <div>
              <div className="eyebrow">Your reward jars</div>
              <h2 className="h-display" style={{ fontSize: 42, marginTop: 8 }}>Save for what you want.</h2>
            </div>
            <div style={{ fontSize: 14, color: 'var(--ink-soft)', maxWidth: 320 }}>
              Drag stars into a jar to save them. When a jar is full, tap <strong>Use stars</strong> to redeem.
            </div>
          </div>

          {kidJars.length === 0 ? (
            <div style={{
              padding: '32px 22px', textAlign: 'center',
              background: 'var(--paper-soft)',
              borderRadius: 18, border: '1.5px dashed var(--rule-strong)',
              color: 'var(--ink-soft)', fontSize: 14, maxWidth: 560, margin: '0 auto',
            }}>
              No jars yet. Ask a grown-up to add some on your detail page.
            </div>
          ) : (
            <div className="kv-jars">
              {kidJars.map(j => {
                const bc = (window.KID_COLORS_V3 || {})[j.color] || window.KID_COLORS_V3.mango;
                const inJar = j.current || 0;
                const goal = j.goal || 1;
                const isOver  = overTarget === j.id;
                const isPulse = pulse === j.id;
                const fillPct = Math.min(1, inJar / Math.max(goal, 1));
                const ready = inJar >= goal;
                const pct = Math.min(100, (inJar / goal) * 100);
                const jarMood = jarMoods[j.id] || (isOver && (dragKind === 'coin' || dragKind === 'task') ? 'excited' : 'idle');

                return (
                  <div key={j.id}
                    className={`kv-jar mood-${jarMood} ${isOver ? 'over' : ''} ${isPulse ? 'pulse' : ''} ${ready ? 'ready' : ''}`}
                    onDragOver={(e) => onTargetOver(e, j.id)}
                    onDragLeave={onTargetLeave}
                    onDrop={(e) => onTargetDrop(e, j.id)}>
                    <div className="kv-jar-glow" style={{ background: bc.hex }} />
                    {ready && <div className="kv-ready-badge">🎉 Full!</div>}
                    {/* Make the visual jar itself draggable so you can pull a star out */}
                    <div className="kv-jar-illo"
                      draggable={inJar > 0}
                      onDragStart={(e) => inJar > 0 && onCoinDragStart(e, j.id)}
                      onDragEnd={onDragEnd}
                      style={{ cursor: inJar > 0 ? 'grab' : 'default' }}
                      title={inJar > 0 ? 'Drag a star out' : ''}>
                      <window.StarJar id={j.id} color={bc.hex} fillPct={fillPct} size={108} mood={jarMood} emoji={j.emoji} />
                    </div>
                    <div className="kv-jar-meta">
                      <div style={{ minWidth: 0 }}>
                        <div className="kv-jar-title">{j.label}</div>
                        {j.note && <div className="kv-jar-note">{j.note}</div>}
                      </div>
                    </div>
                    <div className="kv-jar-value" style={{ color: bc.hex }}>
                      {inJar}<span style={{ color: 'var(--ink-faint)', fontSize: '0.55em' }}> / {goal}</span>
                    </div>
                    <div className="prog" style={{ marginTop: 6 }}>
                      <div className="prog-fill" style={{ width: `${pct}%`, background: bc.hex }} />
                    </div>
                    {ready && (
                      <button onClick={() => handleRedeem(j)} className="btn btn-primary"
                        style={{ marginTop: 12, padding: '10px 16px', fontSize: 13, width: '100%' }}>
                        ✨ Use {inJar} {inJar === 1 ? 'star' : 'stars'}
                      </button>
                    )}
                  </div>
                );
              })}
            </div>
          )}
        </section>
      </div>
    </div>
  );
}

function KidViewStyles() {
  return <style>{`
    .kv-hero {
      background: var(--dusk); color: var(--paper);
      padding: 40px 64px 64px;
      position: relative; overflow: visible;
      z-index: 5;
    }
    .kv-hero::after {
      content: ''; position: absolute; bottom: -40px; left: 0; right: 0; height: 80px;
      background: var(--paper);
      border-radius: 50% 50% 0 0 / 100% 100% 0 0;
      z-index: 1;
    }
    .kv-hero-bg-dots {
      position: absolute; inset: 0;
      background-image: radial-gradient(circle, rgba(255,210,63,0.4) 1px, transparent 1px);
      background-size: 32px 32px; opacity: 0.5;
    }
    .kv-hero-inner {
      position: relative; z-index: 2;
      max-width: 1480px; margin: 0 auto;
      padding-right: 230px;
    }

    .kv-kid-pick {
      display: flex; align-items: center; gap: 10px; margin-bottom: 18px;
    }
    .kv-kid-pick-label {
      font-family: var(--font-display); font-weight: 500; font-size: 12px;
      letter-spacing: 0.14em; text-transform: uppercase;
      color: rgba(251,246,235,0.55); margin-right: 4px;
    }
    .kv-kid-dot {
      width: 38px; height: 38px;
      border-radius: 50%;
      padding: 0; line-height: 0;
      border: 2.5px solid transparent;
      transition: transform 180ms cubic-bezier(.4,1.4,.5,1), border-color 180ms ease, box-shadow 180ms ease;
      opacity: 0.55;
      filter: saturate(0.85);
    }
    .kv-kid-dot .kid-avatar { width: 32px; height: 32px; font-size: 14px; border-width: 0; }
    .kv-kid-dot:hover { opacity: 0.85; transform: translateY(-1px); }
    .kv-kid-dot.on {
      opacity: 1;
      filter: none;
      border-color: var(--ring);
      box-shadow: 0 0 0 4px color-mix(in srgb, var(--ring) 30%, transparent),
                  0 6px 14px -4px color-mix(in srgb, var(--ring) 60%, transparent);
      transform: translateY(-1px);
    }

    .kv-greeting { display: flex; align-items: center; gap: 32px; flex-wrap: wrap; }
    .kv-greeting-text { flex: 1; min-width: 0; }
    .kv-hi {
      font-family: var(--font-display); font-weight: 500; font-size: 18px;
      opacity: 0.7; letter-spacing: -0.01em;
    }
    .kv-headline {
      font-family: var(--font-chunky);
      font-size: clamp(48px, 7vw, 84px);
      color: white; margin: 6px 0 0;
      letter-spacing: -0.04em;
      line-height: 0.94;
    }

    .kv-mascot {
      position: absolute; top: 32px; right: 64px;
      display: flex; align-items: flex-end; gap: 14px;
      transition: opacity 360ms ease;
      z-index: 250;
      pointer-events: none;
    }
    .kv-mascot > * { pointer-events: auto; }
    .kv-mascot.is-dancing { z-index: 350; }
    .kv-bubble { position: relative; padding-bottom: 14px; }

    .kv-stage {
      max-width: 1680px; margin: -32px auto 0;
      padding: 24px 56px; position: relative; z-index: 3;
    }
    .kv-section-head {
      display: flex; align-items: flex-end; justify-content: space-between;
      margin-bottom: 22px; flex-wrap: wrap; gap: 14px;
    }
    /* Pill tab toggle that sits next to the section title */
    .kv-task-tabs {
      display: inline-flex; gap: 4px;
      background: var(--paper-2);
      border: 1.5px solid var(--rule);
      padding: 4px; border-radius: 999px;
      box-shadow: var(--shadow-1);
    }
    .kv-task-tab {
      display: inline-flex; align-items: center; gap: 8px;
      border: 0; background: transparent;
      padding: 10px 18px; border-radius: 999px;
      font-family: var(--font-display); font-weight: 600; font-size: 15px;
      color: var(--ink-soft); cursor: pointer;
      transition: background 200ms ease, color 200ms ease, transform 160ms ease;
      letter-spacing: -0.01em;
    }
    .kv-task-tab:hover:not(.on) { color: var(--ink); }
    .kv-task-tab.on {
      background: white; color: var(--ink);
      box-shadow: 0 2px 6px rgba(31,26,61,0.08);
    }
    .kv-task-tab--bad.on { color: var(--alert); }
    .kv-task-tab-count {
      font-family: var(--font-chunky);
      font-size: 13px;
      padding: 2px 8px;
      border-radius: 999px;
      background: rgba(31,26,61,0.08);
      color: inherit;
      min-width: 22px; text-align: center;
    }
    .kv-task-tab.on .kv-task-tab-count {
      background: rgba(31,26,61,0.06);
    }
    .kv-tasks-empty {
      padding: 22px; text-align: center;
      font-family: var(--font-display); color: var(--ink-soft);
      background: var(--paper-2); border-radius: 18px;
      border: 1.5px dashed var(--rule);
    }

    .kv-jars {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
      gap: 20px;
      justify-items: center;
    }
    .kv-jar {
      width: 100%;
      max-width: 220px;
      background: linear-gradient(180deg, var(--paper-2) 0%, var(--paper) 100%);
      border-radius: 999px 999px 40px 40px;
      padding: 22px 18px 20px;
      box-shadow: 0 6px 0 rgba(31, 26, 61, 0.07), var(--shadow-1);
      border: 2.5px dashed transparent;
      position: relative;
      overflow: hidden;
      transition: transform 240ms cubic-bezier(.4,1.4,.5,1), box-shadow 240ms ease;
      cursor: pointer;
      text-align: center;
    }
    .kv-jar:hover { transform: translateY(-5px) rotate(-2deg); box-shadow: 0 8px 0 rgba(31, 26, 61, 0.06), var(--shadow-2); }
    .kv-jar:hover .kv-jar-illo { animation: jar-wiggle 0.9s ease-in-out infinite; }
    .kv-jar.over { transform: translateY(-8px) scale(1.04); border-color: var(--mango); box-shadow: var(--shadow-3); }
    .kv-jar.mood-happy .kv-jar-illo,
    .kv-jar.mood-excited .kv-jar-illo { animation: jar-baby-dance 0.55s ease-in-out infinite; }
    .kv-jar.mood-envy .kv-jar-illo { animation: jar-baby-envy 0.85s ease-in-out infinite; }
    .kv-jar.mood-sad .kv-jar-illo { animation: jar-baby-sad 1.4s ease-out forwards; }
    .kv-jar.mood-happy { box-shadow: 0 0 0 2px rgba(255, 210, 63, 0.55), var(--shadow-2); }
    .kv-jar.mood-envy { filter: saturate(0.92); }
    .kv-jar.pulse { animation: bounce 520ms ease-out; }
    .kv-jar.ready { box-shadow: 0 0 0 3px var(--butter), var(--shadow-2); }

    @keyframes jar-baby-dance {
      0%, 100% { transform: translateY(0) rotate(-4deg) scale(1); }
      25%      { transform: translateY(-8px) rotate(4deg) scale(1.06); }
      50%      { transform: translateY(-2px) rotate(-2deg) scale(1.02); }
      75%      { transform: translateY(-10px) rotate(5deg) scale(1.08); }
    }
    @keyframes jar-baby-envy {
      0%, 100% { transform: translateX(0) rotate(0deg); }
      35%      { transform: translateX(3px) rotate(4deg); }
      70%      { transform: translateX(-2px) rotate(-3deg); }
    }
    @keyframes jar-baby-sad {
      0%   { transform: translateY(0) scale(1); }
      100% { transform: translateY(6px) scale(0.96); }
    }
    @keyframes jar-wiggle {
      0%, 100% { transform: rotate(-3deg); }
      50%      { transform: rotate(3deg); }
    }

    .kv-jar-glow {
      position: absolute; bottom: -80px; right: -80px;
      width: 180px; height: 180px; border-radius: 50%;
      opacity: 0.12; filter: blur(20px);
    }
    .kv-jar-floater {
      position: absolute; top: 14px; left: 14px;
      width: 36px; height: 36px;
      display: grid; place-items: center;
      animation: jar-floater-bob 3.2s ease-in-out infinite;
    }
    @keyframes jar-floater-bob {
      0%, 100% { transform: translateY(0) rotate(-4deg); }
      50%      { transform: translateY(-6px) rotate(6deg); }
    }
    .kv-ready-badge {
      position: absolute; top: 14px; right: 14px;
      background: var(--butter); color: var(--ink);
      font-family: var(--font-display); font-weight: 700; font-size: 12px;
      padding: 6px 12px; border-radius: 999px;
      animation: wiggle 1.2s ease-in-out infinite;
      letter-spacing: -0.01em;
    }
    .kv-jar-illo {
      display: flex; justify-content: center; align-items: center;
      height: 132px; margin: -2px 0 2px;
      transform-origin: bottom center;
    }
    .kv-jar-meta {
      display: flex; flex-direction: column; align-items: center;
      gap: 4px; margin-bottom: 10px;
    }
    .kv-jar-title {
      font-family: var(--font-display); font-weight: 700; font-size: 18px;
      letter-spacing: -0.02em; line-height: 1.15;
    }
    .kv-jar-note  { font-size: 12px; color: var(--ink-soft); margin-top: 2px; line-height: 1.3; }
    .kv-jar-value { font-family: var(--font-chunky); font-size: 34px; letter-spacing: -0.02em; line-height: 1; }

    .kv-pile-tasks {
      display: grid;
      grid-template-columns: minmax(260px, 0.7fr) minmax(0, 1.3fr);
      gap: 36px; margin-top: 56px;
    }
    .kv-pile {
      background: radial-gradient(ellipse at top, var(--butter-soft) 0%, var(--paper-2) 60%);
      border-radius: 28px; padding: 28px 24px;
      border: 2px dashed rgba(31,26,61,0.18);
      box-shadow: var(--shadow-1);
      transition: all 240ms ease;
      min-height: 320px; display: flex; flex-direction: column;
    }
    .kv-pile.highlight { border-color: var(--mango); background: radial-gradient(ellipse at top, var(--mango-soft) 0%, var(--paper-2) 60%); }
    .kv-pile.over { transform: translateY(-6px); box-shadow: var(--shadow-2); border-color: var(--mango); }
    .kv-pile-count { font-family: var(--font-chunky); font-size: 96px; line-height: 0.85; letter-spacing: -0.04em; text-align: center; margin-bottom: 8px; }
    .kv-pile-stack { flex: 1; display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; align-content: flex-start; }
    .kv-pile-empty { font-size: 14px; color: var(--ink-faint); font-style: italic; padding: 16px; }
    .kv-pile-cta { margin-top: 14px; text-align: center; font-family: var(--font-display); font-weight: 500; font-size: 13px; color: var(--ink-soft); letter-spacing: -0.005em; }
    .kv-star {
      --rot-a: -6deg;
      --rot-b:  6deg;
      display: grid; place-items: center; cursor: grab;
      transition: transform 160ms cubic-bezier(.4,1.4,.5,1);
      filter: drop-shadow(0 2px 4px rgba(31,26,61,0.15));
      animation-iteration-count: infinite;
      animation-timing-function: ease-in-out;
      transform-origin: center;
    }
    .kv-star:hover {
      transform: rotate(12deg) scale(1.22);
      animation-play-state: paused;
      filter: drop-shadow(0 4px 10px rgba(31,26,61,0.22));
      z-index: 10;
    }
    .kv-star:active { cursor: grabbing; }

    /* Four in-place motion styles — picked randomly per star,
       no two adjacent stars share the same style/pose */
    .kv-star--sway   { animation-name: kv-star-sway;   }
    .kv-star--pulse  { animation-name: kv-star-pulse;  }
    .kv-star--wobble { animation-name: kv-star-wobble; }
    .kv-star--spin   { animation-name: kv-star-spin;   }

    @keyframes kv-star-sway {
      0%, 100% { transform: rotate(var(--rot-a)); }
      50%      { transform: rotate(var(--rot-b)); }
    }
    @keyframes kv-star-pulse {
      0%, 100% { transform: scale(0.94) rotate(var(--rot-a)); }
      50%      { transform: scale(1.08) rotate(var(--rot-b)); }
    }
    @keyframes kv-star-wobble {
      0%, 100% { transform: rotate(var(--rot-a)) scale(1); }
      25%      { transform: rotate(0deg)        scale(1.06); }
      50%      { transform: rotate(var(--rot-b)) scale(1); }
      75%      { transform: rotate(0deg)        scale(1.06); }
    }
    @keyframes kv-star-spin {
      0%   { transform: rotate(calc(var(--rot-a) - 8deg)) scale(1); }
      50%  { transform: rotate(calc(var(--rot-b) + 8deg)) scale(1.04); }
      100% { transform: rotate(calc(var(--rot-a) - 8deg)) scale(1); }
    }

    /* Respect motion-reduced preference */
    @media (prefers-reduced-motion: reduce) {
      .kv-star { animation: none !important; transform: rotate(var(--rot-a)); }
    }

    .kv-pile-fullhint {
      margin: 14px 6px 6px;
      padding: 12px 16px;
      background: var(--butter-soft);
      border: 1.5px dashed var(--butter);
      border-radius: 18px;
      font-family: var(--font-display);
      font-weight: 600; font-size: 14px;
      color: var(--ink);
      letter-spacing: -0.005em;
      text-align: center;
    }

    /* Multi-column tile grid — handles long task lists by wrapping onto
       more columns as the panel width grows. */
    .kv-tasks {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
      gap: 12px;
    }
    .kv-task-tile {
      display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 16px;
      padding: 16px 20px; border: 1.5px solid; border-radius: 20px;
      cursor: grab; text-align: left;
      transition: transform 200ms cubic-bezier(.4,1.4,.5,1), box-shadow 200ms ease;
      box-shadow: var(--shadow-1);
    }
    .kv-task-tile:hover  { transform: translateY(-3px); box-shadow: var(--shadow-2); }
    .kv-task-tile:active { cursor: grabbing; transform: translateY(0); }
    .kv-task-emoji {
      font-size: 32px; width: 52px; height: 52px;
      display: grid; place-items: center;
      background: rgba(255,255,255,0.7);
      border-radius: 16px; flex: none;
    }
    .kv-task-label { font-family: var(--font-display); font-weight: 600; font-size: 17px; letter-spacing: -0.015em; color: var(--ink); }
    .kv-task-delta { font-family: var(--font-chunky); font-size: 28px; letter-spacing: -0.02em; }

    /* Falling-star drop effect — fires when a bad-task minus-N is logged.
       Each star is fixed-positioned at the pile centre and animates down
       and off the bottom of the screen with random drift and spin. */
    .kv-falling-layer {
      position: fixed; inset: 0;
      pointer-events: none; z-index: 420;
    }
    .kv-falling-star {
      --dx: 0px;
      --spin: 360deg;
      position: fixed;
      transform: translate(-50%, -50%);
      filter: drop-shadow(0 4px 8px rgba(31,26,61,0.22));
      animation: kv-star-drop 2.2s cubic-bezier(.55,.05,.55,1) forwards;
      will-change: transform, opacity;
    }
    @keyframes kv-star-drop {
      0%   { transform: translate(-50%, -50%) rotate(0deg) scale(1.15); opacity: 0; }
      8%   { transform: translate(-50%, -50%) rotate(20deg)  scale(1.25); opacity: 1; }
      35%  { transform: translate(calc(-50% + (var(--dx) * 0.35)), 20vh) rotate(calc(var(--spin) * 0.35)) scale(1.05); opacity: 1; }
      70%  { transform: translate(calc(-50% + (var(--dx) * 0.75)), 55vh) rotate(calc(var(--spin) * 0.75)) scale(0.9);  opacity: 0.95; }
      100% { transform: translate(calc(-50% + var(--dx)), 110vh) rotate(var(--spin)) scale(0.55); opacity: 0; }
    }
    @media (prefers-reduced-motion: reduce) {
      .kv-falling-star { animation-duration: 0.001ms !important; opacity: 0; }
    }

    @media (max-width: 1000px) {
      .kv-hero { padding: 28px 22px 64px; }
      .kv-stage { padding: 24px 22px; }
      .kv-pile-tasks { grid-template-columns: 1fr; }
      .kv-hero-inner { padding-right: 110px; }
      .kv-mascot { top: 20px; right: 20px; transform: scale(0.78); transform-origin: top right; }
      .kv-mascot .kv-bubble { display: none; }
    }
    @media (max-width: 640px) {
      .kv-hero-inner { padding-right: 0; }
      .kv-mascot { transform: scale(0.6); top: 12px; right: 8px; }
    }
  `}</style>;
}

// ── Sad reactions ──────────────────────────────────────────────────────────
// Three little stories that happen at random when something bad is logged.
// None of them shrink the mascot.

function SadCry({ color }) {
  return (
    <div className="sad-cry">
      <div className="sad-cry-bubble">
        <window.SuriBubble>I'm so sad… 😢</window.SuriBubble>
      </div>
      <div className="sad-cry-mascot">
        <window.Suri mood="sad" size={170} color={color} />
        <div className="sad-tear sad-tear--l" />
        <div className="sad-tear sad-tear--r" />
        <div className="sad-tear sad-tear--l sad-tear--late" />
        <div className="sad-tear sad-tear--r sad-tear--late" />
      </div>
    </div>
  );
}

function SadWalk({ color }) {
  return (
    <div className="sad-walk">
      <div className="sad-walk-track">
        <div className="sad-walk-zig">
          <div className="sad-walk-suri">
            <window.Suri mood="sad" size={130} color={color} />
          </div>
          <div className="sad-walk-cloud">·  ·  ·</div>
        </div>
      </div>
    </div>
  );
}

// ── Happy reactions ────────────────────────────────────────────────────────

function HappyDance({ color, kidName }) {
  return (
    <div className="happy-dance">
      <div className="happy-dance-bubble">
        <window.SuriBubble>Yay! Look at me, {kidName}!</window.SuriBubble>
      </div>
      <div className="happy-dance-body">
        <div className="happy-dance-suri">
          <window.Suri mood="dance" size={160} color={color} />
        </div>
      </div>
      <div className="happy-confetti">
        <span style={{ '--i': 0 }}>★</span>
        <span style={{ '--i': 1 }}>✦</span>
        <span style={{ '--i': 2 }}>♥</span>
        <span style={{ '--i': 3 }}>★</span>
        <span style={{ '--i': 4 }}>✿</span>
        <span style={{ '--i': 5 }}>✦</span>
      </div>
    </div>
  );
}

function HappyRun({ color }) {
  return (
    <div className="happy-run">
      <div className="happy-run-track">
        <div className="happy-run-zig">
          <div className="happy-run-suri">
            <window.Suri mood="happy" size={140} color={color} />
          </div>
        </div>
        <div className="speed-line speed-line--1" />
        <div className="speed-line speed-line--2" />
        <div className="speed-line speed-line--3" />
      </div>
    </div>
  );
}

function HappyStyle({ color }) {
  return (
    <div className="happy-style">
      <div className="happy-style-track">
        <div className="happy-style-zig">
          <div className="happy-style-suri">
            {/* hat + shades live INSIDE the mascot wrapper so they ride along
                with every wobble, oscillation and traversal */}
            <window.Suri mood="happy" size={140} color={color} />
            <div className="happy-style-hat">🎩</div>
            <div className="happy-style-shades">🕶️</div>
          </div>
          <div className="happy-style-sparkle s1">✨</div>
          <div className="happy-style-sparkle s2">✨</div>
          <div className="happy-style-sparkle s3">✨</div>
        </div>
      </div>
    </div>
  );
}

function SadSitBack({ color }) {
  return (
    <div className="sad-sitback">
      <div className="sad-sitback-hat">🎩</div>
      <div className="sad-sitback-body">
        <window.Suri mood="sad" size={130} color={color} />
      </div>
      <div className="sad-sitback-puddle" />
    </div>
  );
}

function ReactionStyles() {
  return <style>{`

    /* Tears used by SadCry */
    .sad-tear {
      position: absolute;
      width: 10px; height: 14px;
      background: linear-gradient(180deg, #B3DCFB 0%, #6CB4E8 100%);
      border-radius: 50% 50% 50% 50% / 35% 35% 65% 65%;
      box-shadow: inset -1px -2px 2px rgba(31,26,61,0.15);
      opacity: 0;
      animation: sad-tear-drop 2.4s ease-in infinite;
    }
    .sad-tear--l { left: 38%; top: 46%; }
    .sad-tear--r { left: 56%; top: 46%; }
    .sad-tear--late { animation-delay: 1.1s; opacity: 0; }
    @keyframes sad-tear-drop {
      0%   { transform: translateY(0)    scale(0.6); opacity: 0; }
      15%  { transform: translateY(4px)  scale(1);   opacity: 1; }
      85%  { transform: translateY(80px) scale(1);   opacity: 1; }
      100% { transform: translateY(120px) scale(0.7); opacity: 0; }
    }

    /* SadCry — large, centred-on-screen */
    .sad-cry {
      position: fixed;
      top: 50%; left: 50%;
      transform: translate(-50%, -50%);
      z-index: 400;
      display: flex; flex-direction: column; align-items: center; gap: 10px;
      pointer-events: none;
      animation: sad-cry-in 380ms cubic-bezier(.4,1.4,.5,1) both,
                 sad-cry-out 360ms ease 4s forwards;
    }
    .sad-cry-mascot {
      position: relative;
      animation: sad-cry-shake 1.4s ease-in-out infinite;
    }
    .sad-cry-bubble { animation: sad-bubble-bob 2.4s ease-in-out infinite; }
    @keyframes sad-cry-in {
      from { opacity: 0; transform: translate(-50%, -45%) scale(0.85); }
      to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
    }
    @keyframes sad-cry-out {
      to { opacity: 0; transform: translate(-50%, -55%) scale(0.95); }
    }
    @keyframes sad-cry-shake {
      0%, 100% { transform: rotate(-3deg); }
      50%      { transform: rotate( 3deg); }
    }
    @keyframes sad-bubble-bob {
      0%, 100% { transform: translateY(0); }
      50%      { transform: translateY(-6px); }
    }

    /* SadWalk — meanders all over the screen on a wandering S-curve.
       The track visits several (X, Y) waypoints across the full viewport.
       ease-in-out smooths the direction changes. */
    .sad-walk {
      position: fixed;
      top: 0; bottom: 0; left: 0; right: 0;
      pointer-events: none; z-index: 380;
      overflow: hidden;
    }
    .sad-walk-track {
      position: absolute; bottom: 80px; left: 0;
      width: 180px;
      will-change: transform;
      animation: sad-walk-wander 7.8s ease-in-out forwards;
    }
    .sad-walk-zig {
      display: flex; flex-direction: column; align-items: center; gap: 4px;
      animation: sad-walk-bob 1.6s ease-in-out infinite;
    }
    .sad-walk-suri { transform-origin: bottom center; }
    .sad-walk-cloud {
      font-family: var(--font-display); font-weight: 700; font-size: 24px;
      color: var(--ink-faint); letter-spacing: 4px;
      animation: sad-walk-dots 2.4s ease-in-out infinite;
    }
    /* Wanders right → upper-mid → lower-left → upper-right → exits left.
       For a sad walk we keep the dips smaller; visits less of the top half. */
    @keyframes sad-walk-wander {
      0%   { transform: translate3d(110vw,   0,   0); }
      18%  { transform: translate3d( 78vw, -22vh, 0); }
      36%  { transform: translate3d( 55vw,  -4vh, 0); }
      54%  { transform: translate3d( 38vw, -30vh, 0); }
      72%  { transform: translate3d( 18vw, -10vh, 0); }
      90%  { transform: translate3d( -5vw, -22vh, 0); }
      100% { transform: translate3d(-25vw,   0,   0); }
    }
    @keyframes sad-walk-bob {
      0%, 100% { transform: translateY(0)    rotate(-3deg); }
      50%      { transform: translateY(-4px) rotate( 3deg); }
    }
    @keyframes sad-walk-dots {
      0%, 100% { opacity: 0.4; transform: translateY(0); }
      50%      { opacity: 1;   transform: translateY(-3px); }
    }

    /* SadSitBack — top-right, hat coming off, facing away */
    .sad-sitback {
      position: fixed;
      top: 90px; right: 10%;
      z-index: 380; pointer-events: none;
      animation: sad-sitback-in 420ms cubic-bezier(.4,1.4,.5,1) both,
                 sad-sitback-out 420ms ease 4.1s forwards;
    }
    .sad-sitback-body {
      transform: scaleX(-1) rotate(-12deg) translateY(6px);
      transform-origin: bottom center;
      filter: drop-shadow(0 2px 6px rgba(31,26,61,0.18));
      animation: sad-sitback-sob 2.6s ease-in-out infinite;
    }
    .sad-sitback-hat {
      position: absolute;
      top: -16px; left: -22px;
      font-size: 38px;
      transform-origin: bottom center;
      animation: sad-sitback-hatdrop 2.4s ease-in-out infinite;
    }
    .sad-sitback-puddle {
      position: absolute;
      bottom: -8px; left: 50%;
      width: 80px; height: 10px;
      background: radial-gradient(ellipse at center, rgba(108, 180, 232, 0.45), rgba(108, 180, 232, 0));
      transform: translateX(-50%);
    }
    @keyframes sad-sitback-in {
      from { opacity: 0; transform: translateY(-10px); }
      to   { opacity: 1; transform: translateY(0); }
    }
    @keyframes sad-sitback-out {
      to { opacity: 0; transform: translateY(-6px); }
    }
    @keyframes sad-sitback-sob {
      0%, 100% { transform: scaleX(-1) rotate(-14deg) translateY(7px); }
      50%      { transform: scaleX(-1) rotate(-10deg) translateY(4px); }
    }
    @keyframes sad-sitback-hatdrop {
      0%, 100% { transform: rotate(-18deg) translate(0, 0); }
      50%      { transform: rotate(-12deg) translate(-4px, 2px); }
    }

    /* ── HappyDance — gentle in-place sway, no traversal ─────────────────── */
    .happy-dance {
      position: fixed; top: 50%; left: 50%;
      transform: translate(-50%, -50%);
      z-index: 400; pointer-events: none;
      display: flex; flex-direction: column; align-items: center; gap: 12px;
      animation: happy-dance-in 480ms cubic-bezier(.4,1.4,.5,1) both,
                 happy-out      460ms ease 4.8s forwards;
    }
    .happy-dance-body {
      position: relative; width: 220px; height: 240px;
      display: flex; align-items: center; justify-content: center;
    }
    .happy-dance-suri {
      animation: happy-dance-sway 1.6s ease-in-out infinite;
      transform-origin: bottom center;
    }
    .happy-dance-bubble { animation: sad-bubble-bob 2.4s ease-in-out infinite; }
    @keyframes happy-dance-in {
      from { opacity: 0; transform: translate(-50%, -45%) scale(0.85); }
      to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
    }
    @keyframes happy-out {
      to { opacity: 0; transform: translate(-50%, -55%) scale(0.95); }
    }
    /* Wobble side-to-side with a small lift — easy on the eyes */
    @keyframes happy-dance-sway {
      0%, 100% { transform: translate(0, 0)      rotate(-6deg); }
      25%      { transform: translate(-10px, -4px) rotate(-3deg); }
      50%      { transform: translate(0, -10px)  rotate( 6deg); }
      75%      { transform: translate( 10px, -4px) rotate( 3deg); }
    }

    /* Confetti specks fountaining around the dancer */
    .happy-confetti {
      position: absolute; top: 0; left: 0; right: 0; bottom: 0;
      pointer-events: none;
    }
    .happy-confetti span {
      position: absolute;
      top: 50%; left: 50%;
      font-size: 22px;
      color: var(--mango, #FFA94D);
      opacity: 0;
      animation: confetti-burst 2.6s ease-out infinite;
      animation-delay: calc(var(--i) * 0.35s);
    }
    .happy-confetti span:nth-child(odd) { color: var(--berry, #C73E8B); }
    .happy-confetti span:nth-child(3n)  { color: var(--mint, #6CBF9C); }
    @keyframes confetti-burst {
      0%   { transform: translate(-50%, -50%) scale(0.4); opacity: 0; }
      30%  { opacity: 1; }
      100% {
        transform:
          translate(calc(-50% + (var(--i) * 60px - 150px)),
                    calc(-50% - 120px - (var(--i) * 12px)))
          scale(1.1) rotate(180deg);
        opacity: 0;
      }
    }

    /* ── HappyRun — bounds all around the screen, corner to corner ───────── */
    .happy-run {
      position: fixed; top: 0; bottom: 0; left: 0; right: 0;
      pointer-events: none; z-index: 380; overflow: hidden;
    }
    .happy-run-track {
      position: absolute; bottom: 80px; left: 0;
      width: 200px; height: 200px;
      will-change: transform;
      animation: happy-run-wander 7.4s ease-in-out forwards;
    }
    .happy-run-zig {
      width: 100%; height: 100%;
      display: flex; align-items: flex-end; justify-content: center;
      /* Small fast bob layered on top of the big wander */
      animation: happy-run-bob 1.0s ease-in-out infinite;
    }
    .happy-run-suri {
      animation: happy-run-tilt 1.4s ease-in-out infinite;
      transform-origin: bottom center;
    }
    /* Big screen wander — visits low-right → top-mid → mid → top-left → exit
       Y in vh = relative to viewport height, so the path reaches the top. */
    @keyframes happy-run-wander {
      0%   { transform: translate3d(-25vw,   0,   0); }
      14%  { transform: translate3d(  5vw, -15vh, 0); }
      28%  { transform: translate3d( 22vw, -55vh, 0); }
      42%  { transform: translate3d( 40vw, -10vh, 0); }
      56%  { transform: translate3d( 58vw, -62vh, 0); }
      70%  { transform: translate3d( 75vw, -25vh, 0); }
      84%  { transform: translate3d( 92vw, -50vh, 0); }
      100% { transform: translate3d(115vw,   0,   0); }
    }
    @keyframes happy-run-bob {
      0%, 100% { transform: translateY(0); }
      50%      { transform: translateY(-8px); }
    }
    @keyframes happy-run-tilt {
      0%, 100% { transform: rotate(-5deg); }
      50%      { transform: rotate( 5deg); }
    }

    .speed-line {
      position: absolute;
      background: linear-gradient(90deg, rgba(31,26,61,0.0), rgba(31,26,61,0.35));
      border-radius: 999px; height: 4px; width: 60px;
      right: 95%;
      animation: speed-fade 1.2s ease-in-out infinite;
    }
    .speed-line--1 { top: 38%; animation-delay: 0.0s; }
    .speed-line--2 { top: 52%; width: 90px; animation-delay: 0.35s; }
    .speed-line--3 { top: 66%; width: 50px; animation-delay: 0.70s; }
    @keyframes speed-fade {
      0%, 100% { opacity: 0; transform: translateX(0); }
      50%      { opacity: 1; transform: translateX(-16px); }
    }

    /* ── HappyStyle — slow strut wandering across the whole screen ───────── */
    .happy-style {
      position: fixed; top: 0; bottom: 0; left: 0; right: 0;
      pointer-events: none; z-index: 380; overflow: hidden;
    }
    .happy-style-track {
      position: absolute; bottom: 80px; left: 0;
      width: 200px; height: 220px;
      will-change: transform;
      animation: happy-style-wander 8.0s ease-in-out forwards;
    }
    .happy-style-zig {
      position: relative;
      width: 100%; height: 100%;
      display: flex; align-items: flex-end; justify-content: center;
      animation: happy-style-bob 2.0s ease-in-out infinite;
    }
    .happy-style-suri {
      position: relative;
      width: 140px; height: 140px;
      animation: happy-style-strut 2.0s ease-in-out infinite;
      transform-origin: bottom center;
    }
    /* hat sits on top of Suri's head (Suri's SVG head ~12% from top) */
    .happy-style-hat {
      position: absolute;
      top: -14px;
      left: 50%;
      transform: translateX(-50%) rotate(-10deg);
      font-size: 38px;
      z-index: 3;
      filter: drop-shadow(0 2px 3px rgba(0,0,0,0.25));
      pointer-events: none;
    }
    /* shades sit on the eye band (Suri's eyes are at ~49% of its height) */
    .happy-style-shades {
      position: absolute;
      top: 45%;
      left: 50%;
      transform: translate(-50%, -50%);
      font-size: 30px;
      z-index: 4;
      filter: drop-shadow(0 1px 1px rgba(0,0,0,0.35));
      animation: style-shades-flash 2.8s ease-in-out infinite;
      pointer-events: none;
    }
    /* Lazy strut path — visits more of the lower-screen runway plus a couple
       of show-off climbs near the top, then peace-out off-right. */
    @keyframes happy-style-wander {
      0%   { transform: translate3d(-25vw,   0,   0); }
      15%  { transform: translate3d( 10vw, -25vh, 0); }
      30%  { transform: translate3d( 28vw,  -5vh, 0); }
      45%  { transform: translate3d( 45vw, -45vh, 0); }
      60%  { transform: translate3d( 62vw, -10vh, 0); }
      78%  { transform: translate3d( 82vw, -38vh, 0); }
      100% { transform: translate3d(115vw,   0,   0); }
    }
    @keyframes happy-style-bob {
      0%, 100% { transform: translateY(0); }
      50%      { transform: translateY(-6px); }
    }
    @keyframes happy-style-strut {
      0%, 100% { transform: rotate(-4deg); }
      50%      { transform: rotate( 4deg); }
    }
    @keyframes style-shades-flash {
      0%, 90%, 100% { filter: drop-shadow(0 1px 1px rgba(0,0,0,0.35)); }
      92%, 96%      { filter: drop-shadow(0 0 12px rgba(255,255,255,0.95)) brightness(1.6); }
    }
    .happy-style-sparkle {
      position: absolute; font-size: 22px;
      animation: style-sparkle 2.4s ease-in-out infinite;
    }
    .happy-style-sparkle.s1 { top: 10%; left: -8%;  animation-delay: 0s;   }
    .happy-style-sparkle.s2 { top: 30%; right: -8%; animation-delay: 0.6s; }
    .happy-style-sparkle.s3 { top: 60%; left: -4%;  animation-delay: 1.2s; }
    @keyframes style-sparkle {
      0%, 100% { opacity: 0; transform: scale(0.6); }
      50%      { opacity: 1; transform: scale(1.2); }
    }

    @media (max-width: 720px) {
      .sad-cry,
      .happy-dance { transform: translate(-50%, -50%) scale(0.85); }
      .sad-sitback { right: 18px; top: 70px; }
      .sad-walk-track,
      .happy-run-track,
      .happy-style-track { width: 160px; }
    }

    @media (prefers-reduced-motion: reduce) {
      .sad-cry, .sad-walk-track, .sad-walk-zig, .sad-sitback,
      .sad-cry-mascot, .sad-walk-suri,
      .sad-sitback-body, .sad-sitback-hat, .sad-cry-bubble,
      .happy-dance, .happy-dance-body, .happy-dance-suri, .happy-dance-bubble,
      .happy-run-track, .happy-run-zig, .happy-run-suri,
      .happy-style-track, .happy-style-zig, .happy-style-suri,
      .happy-confetti span, .speed-line,
      .happy-style-shades, .happy-style-sparkle {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
      }
    }
  `}</style>;
}

window.KidView = KidView;
