// screen-home.jsx — Home v2. Watt-led, glanceable, illustration-first.
//
// Structure (vertically, top → bottom):
//   1. Watt + greeting strip (mood = on-track / over)
//   2. Big monthly savings card — ring + animated $ counter
//   3. Live tip (or quiet state)
//   4. Animated appliance row — tap to toggle, visual state, no labels needed
//   5. Today's missions preview (2 quick items + "see all")

function HomeScreen({ c, points, setTab, devices, setDevices,
  recDone, setRecDone, missions, completeMission,
  monthSpent, monthBudget, heroStyle = 'bold' }) {
  // Live clock — drives the daily battery so it drains through the day.
  const [now, setNow] = React.useState(() => new Date());
  React.useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 30000);
    return () => clearInterval(id);
  }, []);
  const dayFraction = (now.getHours() * 60 + now.getMinutes()) / 1440;

  // Daily budget derived from the monthly goal. Spend tracks slightly ahead of
  // pace so the household reads as "doing well" by default.
  const dailyBudget = monthBudget / 30; // ≈ $3.00 / day
  const spentToday = +(dailyBudget * Math.min(0.97, dayFraction * 0.86)).toFixed(2);
  const remainingToday = +(dailyBudget - spentToday).toFixed(2);
  const usedFraction = spentToday / dailyBudget;

  // Real daily-battery numbers from GET /household/usage/summary. Until they
  // load (or if the call fails) we fall back to the local simulation above, so
  // the card still animates and never blocks on the network.
  const [usage, setUsage] = React.useState(null);
  // Refresh the battery numbers on mount and every 30s (paused while the tab is
  // hidden). Until the first success we fall back to the local simulation above.
  React.useEffect(() => {
    let alive = true;
    const load = async () => {
      if (document.hidden) return;
      try { const u = await SaveHorAPI.getUsageSummary(); if (alive) setUsage(u); }
      catch (e) { /* keep last-known / simulation; session/CORS errors surface at login */ }
    };
    load();
    const id = setInterval(load, 30000);
    const onVis = () => { if (!document.hidden) load(); };
    document.addEventListener('visibilitychange', onVis);
    return () => { alive = false; clearInterval(id); document.removeEventListener('visibilitychange', onVis); };
  }, []);
  const live = usage != null;
  const percentLeft = live ? Math.max(0, Math.min(100, usage.percent_left)) : null;

  const ahead = live ? percentLeft > 20 : usedFraction <= dayFraction + 0.02;
  const empty = live ? percentLeft <= 0 : remainingToday <= 0;

  // Real controllable plug — GET /household/devices + /household/readings/latest.
  // Replaces the first appliance tile below with live watts/state + a working
  // toggle. Falls back to the simulated tile if the calls fail.
  const [plug, setPlug] = React.useState(null);
  const [toggling, setToggling] = React.useState(false);
  // After an in-app toggle, trust the command over polling for a short window so
  // a lagging reading can't flicker the tile back before the backend catches up.
  const suppressPollUntil = React.useRef(0);
  React.useEffect(() => {
    let alive = true;
    Promise.all([SaveHorAPI.getDevices(), SaveHorAPI.getLatestReadings()])
      .then(([devs, readings]) => {
        if (!alive || !Array.isArray(devs)) return;
        const dev = devs.find((d) => d.is_controllable) || devs[0];
        if (!dev) return;
        const r = (readings && readings[dev.id]) || {};
        setPlug({
          id: dev.id,
          label: dev.label || (dev.appliance_types && dev.appliance_types.name) || 'Plug',
          watts: Math.round(r.apower || 0),
          on: !!r.output,
          controllable: !!dev.is_controllable,
        });
      })
      .catch(() => { /* keep the simulated first tile */ });
    return () => { alive = false; };
  }, []);

  // Poll the latest reading every 5s so the tile reflects reality no matter how
  // the plug was changed (app, Shelly app, physical button). Always refresh
  // watts; only adopt the reading's on/off once the post-toggle window has
  // passed, so an in-app tap isn't flickered back by a lagging reading. Paused
  // while the tab is hidden.
  const plugId = plug && plug.id;
  React.useEffect(() => {
    if (!plugId) return;
    let alive = true;
    const tick = async () => {
      if (document.hidden) return;
      try {
        const readings = await SaveHorAPI.getLatestReadings();
        if (!alive) return;
        const r = readings && readings[plugId];
        if (!r) return;
        const adoptOnOff = Date.now() > suppressPollUntil.current;
        setPlug((p) => (p && p.id === plugId ? {
          ...p,
          watts: Math.round(r.apower || 0),
          on: adoptOnOff ? !!r.output : p.on,
        } : p));
      } catch (e) { /* keep last-known values; never stop the interval */ }
    };
    const id = setInterval(tick, 5000);
    const onVis = () => { if (!document.hidden) tick(); };
    document.addEventListener('visibilitychange', onVis);
    return () => { alive = false; clearInterval(id); document.removeEventListener('visibilitychange', onVis); };
  }, [plugId]);

  const togglePlug = async () => {
    if (!plug || toggling || !plug.controllable) return;
    setToggling(true);
    const next = !plug.on;
    setPlug((p) => ({ ...p, on: next }));                 // optimistic flip
    suppressPollUntil.current = Date.now() + 4000;        // trust the tap briefly
    try {
      await SaveHorAPI.toggleDevice(plug.id);
      // Watts/on reconcile via the 5s poll once the backend catches up.
    } catch (e) {
      suppressPollUntil.current = 0;                      // let polling correct now
      setPlug((p) => ({ ...p, on: !next }));               // revert on failure
    } finally {
      setToggling(false);
    }
  };

  const mood = empty ? 'worried' : ahead ? 'happy' : 'focused';
  const greeting = empty ? 'Over for today' : ahead ? "You're doing great" : 'Ease off a little';

  // Live wattage — sum of every device currently on
  const onDevices = devices.filter((d) => d.on);
  const liveWatts = onDevices.reduce((s, d) => s + d.watts, 0);

  // Live drain — the odometer ticks down in real time from the current draw so
  // the user watches ~$X.XX creep toward the next cent, like a real meter.
  const [drained, setDrained] = React.useState(0);
  const liveWattsRef = React.useRef(liveWatts);
  liveWattsRef.current = liveWatts;
  React.useEffect(() => {
    // True cost drain using real elapsed time.
    //   $/hour = kW × $0.30/kWh ;  $/ms = that / 3,600,000
    // At ~1.3 kW that's ≈ $0.38/hr, so a single cent takes ~95s — the
    // continuously-rolling last wheel makes that slow drift visible & honest.
    let last = performance.now();
    const id = setInterval(() => {
      const now = performance.now();
      const dtMs = now - last;
      last = now;
      const dollars = (liveWattsRef.current / 1000) * 0.30 * (dtMs / 3600000);
      setDrained((d) => d + dollars);
    }, 250);
    return () => clearInterval(id);
  }, []);
  const liveRemaining = Math.max(0, remainingToday - drained);
  const liveSpent = Math.min(dailyBudget, spentToday + drained);
  // Month-to-date saving — nudges up in step with the real daily drain.
  const savedMonth = (24 + drained).toFixed(2);

  const toggleDevice = (key) => setDevices((prev) =>
    prev.map((d) => d.key === key && !d.lockedOn ? { ...d, on: !d.on } : d)
  );
  // Toggle an entire kind at once (used by the grouped PowerFlow chips).
  const toggleKind = (kind) => setDevices((prev) => {
    const anyOn = prev.some((d) => d.kind === kind && d.on && !d.lockedOn);
    return prev.map((d) => d.kind === kind && !d.lockedOn ? { ...d, on: !anyOn } : d);
  });

  const animRemaining = useCountUp(remainingToday, { duration: 700, decimals: 2 });
  const animWatts = useCountUp(liveWatts, { duration: 400 });
  const [statusOpen, setStatusOpen] = React.useState(false);

  // Pick mission preview (max 2 active/available)
  const todayPreview = missions.filter((m) => m.cadence === 'today' && m.state !== 'done').slice(0, 2);
  const doneToday = missions.filter((m) => m.cadence === 'today' && m.state === 'done').length;
  const totalToday = missions.filter((m) => m.cadence === 'today').length;

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 11, paddingBottom: 100 }}>

      {/* HEADER STRIP — Watt + greeting + points */}
      <div style={{ padding: '12px 20px 0', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, minWidth: 0 }}>
          <Watt size={48} color={c.primary} bg={c.primarySoft} mood={mood} />
          <div style={{ minWidth: 0 }}>
            <div style={{ fontSize: 12, fontWeight: 600, color: c.textLight, letterSpacing: 0.3 }}>SAVEHOR · MAY</div>
            <div style={{ fontSize: 17, fontWeight: 700, color: c.text, letterSpacing: -0.3, marginTop: 1 }}>{greeting}</div>
          </div>
        </div>
        <button onClick={() => setTab('play')} style={{
          display: 'flex', alignItems: 'center', gap: 6, height: 36, padding: '0 12px',
          background: c.primarySoft, border: 'none', borderRadius: 999,
          color: c.primaryDk, fontFamily: 'inherit', fontWeight: 700, fontSize: 13,
          fontVariantNumeric: 'tabular-nums', cursor: 'pointer', flexShrink: 0
        }}>
          <IcCoins size={15} color={c.primary} />
          {points.toLocaleString()}
        </button>
      </div>

      {/* HERO — daily budget battery, drains through the day */}
      <div style={{ padding: '0 20px' }}>
        {(() => {
          const bold = heroStyle === 'bold';
          // On the bold dark-green card, swap the text/surface tokens for a
          // light-on-dark set so child labels stay legible.
          const hc = bold ? {
            ...c,
            primary: '#6FC79A',
            primarySoft: 'rgba(255,255,255,0.14)',
            text: '#FFFFFF',
            textMuted: 'rgba(255,255,255,0.66)',
            textLight: 'rgba(255,255,255,0.5)',
            border: 'rgba(255,255,255,0.18)',
            borderSoft: 'rgba(255,255,255,0.12)',
            surface: 'rgba(255,255,255,0.10)',
            surfaceAlt: 'rgba(255,255,255,0.10)',
          } : c;
          return (
        <Card c={c} style={{
          padding: '16px 18px 18px',
          background: bold ? c.primaryDk : c.surface,
          border: bold ? 'none' : `1px solid ${c.borderSoft}`,
          boxShadow: bold ? '0 8px 28px rgba(35,83,58,0.28)' : undefined,
        }}>
          {/* ONE number + status word — the battery carries the rest visually */}
          <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 12, marginBottom: 12 }}>
            <div>
              {live ? (
                <>
                  <div style={{ display: 'flex', alignItems: 'baseline', gap: 4, color: hc.text }}>
                    <span style={{ fontSize: 40, fontWeight: 800, letterSpacing: -1.5, lineHeight: 1 }}>{Math.round(percentLeft)}</span>
                    <span style={{ fontSize: 22, fontWeight: 700, letterSpacing: -0.5, opacity: 0.75 }}>%</span>
                  </div>
                  <div style={{ fontSize: 12.5, fontWeight: 600, color: hc.textMuted, marginTop: 3 }}>
                    of today's allowance left · {usage.today_kwh.toFixed(2)} kWh used
                  </div>
                </>
              ) : (
                <>
                  <div style={{ display: 'flex', alignItems: 'baseline', gap: 4, color: hc.text }}>
                    <span style={{ fontSize: 40, fontWeight: 800, letterSpacing: -1.5, lineHeight: 1 }}>${Math.floor(liveRemaining)}</span>
                    <span style={{ fontSize: 22, fontWeight: 700, letterSpacing: -0.5, opacity: 0.75 }}>.{String(Math.round((liveRemaining % 1) * 100)).padStart(2, '0')}</span>
                  </div>
                  <div style={{ fontSize: 12.5, fontWeight: 600, color: hc.textMuted, marginTop: 3 }}>left to spend today</div>
                </>
              )}
            </div>
            <button onClick={() => setStatusOpen(true)} style={{
              display: 'inline-flex', alignItems: 'center', gap: 6, padding: '8px 12px', borderRadius: 999,
              background: bold ? 'rgba(255,255,255,0.16)' : (empty ? c.dangerSoft : ahead ? c.goodSoft : c.warnSoft), flexShrink: 0,
              border: 'none', cursor: 'pointer', fontFamily: 'inherit'
            }}>
              {empty ?
              <IcWarn size={14} color={bold ? '#FFB4A6' : c.danger} /> :
              ahead ? <IcLeaf size={14} color={bold ? '#A8E0BE' : '#2F6147'} /> : <IcClock size={14} color={bold ? '#F2D58A' : '#7C5A1B'} />}
              <span style={{ fontSize: 13, fontWeight: 700, color: bold ? '#fff' : (empty ? '#7A2A1F' : ahead ? '#2F6147' : '#7C5A1B') }} data-comment-anchor="0643e686f3-span-92-15">
                {empty ? 'Over' : ahead ? 'On track' : 'Watch it'}
              </span>
            </button>
          </div>

          <BudgetBattery c={hc}
            remaining={live ? percentLeft : liveRemaining}
            budget={live ? 100 : dailyBudget}
            dayFraction={dayFraction} height={72} />

          {/* LIVE POWER FLOW — the only other thing on the card: what's drawing now */}
          <div style={{ marginTop: 14 }}>
            <PowerFlow c={hc} menuC={c} devices={devices} kinds={DEVICE_KINDS(c)} onToggleKind={toggleKind} onToggleDevice={toggleDevice} />
          </div>
        </Card>
          );
        })()}
      </div>

      {/* LIVE TIP — only show if there's something actionable */}
      <div style={{ padding: '0 20px' }}>
        {recDone ?
        <Card kind="tinted" c={c} style={{ padding: 14, display: 'flex', alignItems: 'center', gap: 12 }}>
            <div style={{ width: 36, height: 36, borderRadius: 10, background: c.goodSoft, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <IcCheck size={20} color={c.good} />
            </div>
            <div style={{ flex: 1, fontSize: 14, fontWeight: 600, color: c.text }}>Nice — that's +25 pts</div>
            <Chip tone="primary" size="sm" c={c} icon={<IcCoins size={11} color={c.primaryDk} />}>+25</Chip>
          </Card> :

        <TipHero c={c} onDo={() => setRecDone(true)} onSkip={() => setRecDone(true)} />
        }
      </div>

      {/* APPLIANCES — expressive icons, tap to toggle */}
      <div style={{ padding: '0 20px' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8, gap: 12 }}>
          <div style={{ fontSize: 15, fontWeight: 700, color: c.text, letterSpacing: -0.2, whiteSpace: 'nowrap' }}>
            Right now
            <span style={{ fontSize: 12, fontWeight: 600, color: c.textLight, marginLeft: 6 }}>{onDevices.length} on</span>
          </div>
        </div>
        {/* RUNWAY — plain-language read of how the current draw is tracking */}
        {(() => {
          const costPerHour = (liveWatts / 1000) * 0.30;
          const hrsToMidnight = Math.max(0.1, 24 - (now.getHours() + now.getMinutes() / 60));
          const runwayHrs = costPerHour > 0 ? remainingToday / costPerHour : Infinity;
          const coasts = runwayHrs >= hrsToMidnight;
          const fmt = (h) => {
            if (!isFinite(h)) return '∞';
            if (h >= 24) return '24h+';
            const H = Math.floor(h); const M = Math.round((h - H) * 60);
            return H >= 1 ? `${H}h ${M}m` : `${M}m`;
          };
          const tone = empty ? { bg: c.dangerSoft, fg: c.danger, ic: c.danger }
                     : coasts ? { bg: c.goodSoft, fg: '#2F6147', ic: c.good }
                     : { bg: c.warnSoft, fg: '#7C5A1B', ic: c.warn };
          return (
            <div style={{
              display: 'flex', alignItems: 'center', gap: 8, padding: '8px 12px',
              background: tone.bg, borderRadius: 12, marginBottom: 10,
            }}>
              <IcClock size={15} color={tone.ic} style={{ flexShrink: 0 }} />
              <div style={{ flex: 1, minWidth: 0, fontSize: 12.5, fontWeight: 600, color: c.text, lineHeight: 1.3 }}>
                {empty ? (
                  <span style={{ color: tone.fg }}>Budget spent — more adds to the bill</span>
                ) : liveWatts === 0 ? (
                  <span style={{ color: tone.fg }}>Idle — budget holding steady</span>
                ) : coasts ? (
                  <span>Coasting — <span style={{ color: tone.fg, fontWeight: 700 }}>lasts all day</span></span>
                ) : (
                  <span><span style={{ color: tone.fg, fontWeight: 700, fontVariantNumeric: 'tabular-nums' }}>{fmt(runwayHrs)}</span> left at this rate</span>
                )}
              </div>
              <Tooltip c={c} text={`Based on your current draw of ${liveWatts}W at $0.30/kWh, measured against the $${remainingToday.toFixed(2)} left in today's budget. Switch things off to extend it.`} />
            </div>
          );
        })()}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8 }}>
          {devices.map((d, i) => (
            (i === 0 && plug) ? (
              <AppCard key="live-plug" c={c}
                device={{ key: plug.id, name: plug.label, kind: 'ac',
                          watts: plug.watts, on: plug.on, lockedOn: !plug.controllable }}
                onClick={togglePlug} />
            ) : (
              <AppCard key={d.key} c={c} device={d} onClick={() => toggleDevice(d.key)} />
            )
          ))}
          <AddDeviceCard c={c} />
        </div>
      </div>

      {/* TODAY'S MISSIONS PREVIEW */}
      <div style={{ padding: '0 20px' }}>
        <button onClick={() => setTab('play')}
        style={{
          width: '100%', textAlign: 'left', border: 'none', background: 'transparent',
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          padding: 0, marginBottom: 8, cursor: 'pointer', fontFamily: 'inherit', gap: 12
        }}>
          <div style={{ fontSize: 15, fontWeight: 700, color: c.text, letterSpacing: -0.2, whiteSpace: 'nowrap' }}>Today's quests</div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, fontWeight: 600, color: c.textMuted, flex: '0 1 auto', minWidth: 0 }}>
            <div style={{ width: 60, flexShrink: 0 }}>
              <Bar value={doneToday} max={totalToday} c={c} height={4} bg={c.borderSoft} />
            </div>
            <span style={{ fontVariantNumeric: 'tabular-nums', whiteSpace: 'nowrap' }}>{doneToday}/{totalToday}</span>
            <IcChevR size={14} color={c.textLight} />
          </div>
        </button>
        {todayPreview.length === 0 ? (
          <Card c={c} kind="highlighted" style={{ padding: 16, display: 'flex', alignItems: 'center', gap: 12 }}>
            <Watt size={36} color={c.primary} bg="#fff" mood="cheer" />
            <div style={{ fontSize: 14, fontWeight: 600, color: c.primaryDk }}>All done for today!</div>
          </Card>
        ) : (
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
            {todayPreview.map((m) => (
              <QuestTile key={m.id} c={c} m={m} onComplete={() => completeMission(m.id)} wide={todayPreview.length === 1} />
            ))}
          </div>
        )}
      </div>

      {statusOpen &&
      <StatusSheet c={c} state={empty ? 'over' : ahead ? 'ontrack' : 'watch'}
        remaining={remainingToday} budget={dailyBudget} spent={spentToday}
        onClose={() => setStatusOpen(false)} />
      }
    </div>);

}

// AppCard — expressive appliance tile with animation + tap toggle.
// Driven by a device object: { key, name, kind, watts, on, lockedOn }.
const DEVICE_KINDS = (c) => ({
  ac:     { Icon: IcAC,     Art: ACArt,          tone: { bg: c.primarySoft, fg: c.primary } },
  water:  { Icon: IcWater,  Art: WaterHeaterArt, tone: { bg: c.skySoft,     fg: '#345070' } },
  fridge: { Icon: IcFridge, Art: FridgeArt,      tone: { bg: c.sand,        fg: '#5A4A22' } },
});

function AppCard({ c, device, onClick }) {
  const k = DEVICE_KINDS(c)[device.kind];
  const Art = k.Art;
  const on = device.on;
  const locked = device.lockedOn;

  return (
    <button onClick={locked ? undefined : onClick} disabled={locked}
    style={{
      background: c.surface,
      border: `1.5px solid ${on ? c.primary : c.borderSoft}`,
      borderRadius: 16, padding: '8px 6px 10px', position: 'relative',
      display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4,
      cursor: locked ? 'default' : 'pointer', fontFamily: 'inherit',
      minHeight: 102, transition: 'border-color .2s', opacity: on ? 1 : 0.7
    }}>
      {/* single status signal — green dot = on */}
      <div style={{ position: 'absolute', top: 8, right: 8, display: 'flex', alignItems: 'center', gap: 3 }}>
        {locked && <IcLock size={10} color={c.textLight} />}
        <div style={{ width: 7, height: 7, borderRadius: 4, background: on ? c.primary : c.border }} />
      </div>
      <div style={{ height: 46, display: 'flex', alignItems: 'center', justifyContent: 'center', filter: on ? 'none' : 'grayscale(0.7)', opacity: on ? 1 : 0.6 }}>
        <Art size={46} state={on ? 'on' : 'off'} tone={k.tone} />
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 0, maxWidth: '100%' }}>
        <div style={{ fontSize: 11.5, fontWeight: 600, color: c.textMuted, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: '100%' }}>{device.name}</div>
        <div style={{ fontSize: 13.5, fontWeight: 700, color: on ? c.primary : c.textLight, fontVariantNumeric: 'tabular-nums' }}>
          {on ? `${device.watts}W` : 'Off'}
        </div>
      </div>
    </button>);

}

// AddDeviceCard — dashed placeholder tile to pair a new smart plug.
function AddDeviceCard({ c }) {
  return (
    <button style={{
      background: 'transparent', border: `1.5px dashed ${c.border}`,
      borderRadius: 16, padding: '8px 6px 10px', cursor: 'pointer', fontFamily: 'inherit',
      display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 6,
      minHeight: 102, color: c.textLight,
    }}>
      <div style={{ width: 34, height: 34, borderRadius: 11, background: c.surfaceAlt, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <IcPlus size={18} color={c.textMuted} />
      </div>
      <div style={{ fontSize: 11.5, fontWeight: 600, color: c.textMuted }}>Add device</div>
    </button>
  );
}

// MissionRow — used by Home preview AND by Play tab.
function MissionRow({ c, m, onComplete, compact }) {
  const done = m.state === 'done';
  const active = m.state === 'active';
  const Icon = m.icon;

  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 12,
      padding: compact ? '12px 14px' : '14px 16px',
      background: done ? c.surfaceAlt : c.surface,
      border: `1px solid ${active ? c.accent + '55' : c.borderSoft}`,
      borderRadius: 14, opacity: done ? 0.75 : 1
    }}>
      <div style={{
        width: 38, height: 38, borderRadius: 11,
        background: done ? c.goodSoft : active ? c.accentSoft : c.primarySoft,
        display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0
      }}>
        {done ? <IcCheck size={18} color={c.good} /> : <Icon size={18} color={active ? '#8A5A1E' : c.primary} />}
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 14.5, fontWeight: 600, color: c.text, textDecoration: done ? 'line-through' : 'none', textDecorationColor: c.textLight }}>
          {m.title}
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 4 }}>
          <span style={{ fontSize: 11.5, fontWeight: 700, color: c.primary, fontVariantNumeric: 'tabular-nums', display: 'inline-flex', alignItems: 'center', gap: 3 }}>
            <IcCoins size={11} color={c.primary} />+{m.points}
          </span>
          {active && m.timeLeft &&
          <span style={{ fontSize: 11.5, fontWeight: 600, color: '#8A5A1E', display: 'inline-flex', alignItems: 'center', gap: 3 }}>
              <IcClock size={11} color="#8A5A1E" />{m.timeLeft}
            </span>
          }
        </div>
      </div>
      {!done &&
      <Btn kind={active ? 'accent' : 'primary'} size="sm" c={c} onClick={onComplete} icon={<IcCheck size={14} color="#fff" />} />
      }
    </div>);

}

// StatusSheet — explains the daily-budget pace status when the chip is tapped.
function StatusSheet({ c, state, remaining, budget, spent, onClose }) {
  const cfg = {
    ontrack: { title: "You're on track", Icon: IcLeaf, fg: '#2F6147', bg: c.goodSoft,
      line: "You've used less than expected for this time of day. Keep it up and you'll beat your monthly goal." },
    watch:   { title: 'Watch your pace', Icon: IcClock, fg: '#7C5A1B', bg: c.warnSoft,
      line: "You're using power a little faster than planned. Ease off now and you'll still finish the day on budget." },
    over:    { title: "Today's budget is spent", Icon: IcWarn, fg: '#7A2A1F', bg: c.dangerSoft,
      line: "You've used your full budget for today. Anything more adds to this month's bill — try a quick quest to claw it back." },
  }[state];
  const pct = Math.max(0, Math.min(100, Math.round((remaining / budget) * 100)));
  return (
    <div onClick={onClose} style={{
      position: 'absolute', inset: 0, background: 'rgba(20,30,25,0.42)', zIndex: 120,
      display: 'flex', alignItems: 'flex-end', justifyContent: 'center', animation: 'm-fadeup .18s ease',
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width: '100%', background: c.surface, borderRadius: '24px 24px 0 0',
        padding: '12px 24px 28px', animation: 'sh-slideup .26s ease',
      }}>
        <div style={{ width: 36, height: 4, background: c.border, borderRadius: 2, margin: '0 auto 18px' }} />
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
          <div style={{ width: 48, height: 48, borderRadius: 14, background: cfg.bg, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <cfg.Icon size={24} color={cfg.fg} />
          </div>
          <div>
            <div style={{ fontSize: 19, fontWeight: 700, color: c.text, letterSpacing: -0.3 }}>{cfg.title}</div>
            <div style={{ fontSize: 12.5, color: c.textMuted, marginTop: 1 }}>${remaining.toFixed(2)} of ${budget.toFixed(2)} left · {pct}%</div>
          </div>
        </div>
        <p style={{ fontSize: 14.5, color: c.text, lineHeight: 1.55, margin: '0 0 16px' }}>{cfg.line}</p>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          <ExplainRow c={c} Icon={IcBolt} title="Daily budget" value={`$${budget.toFixed(2)}`} sub="From your monthly savings goal" />
          <ExplainRow c={c} Icon={IcClock} title="Used so far" value={`$${spent.toFixed(2)}`} sub="Updates live as appliances run" />
          <ExplainRow c={c} Icon={IcRefresh} title="Resets" value="Midnight" sub="A fresh budget every day" />
        </div>
        <Btn kind="primary" size="lg" full c={c} onClick={onClose} style={{ marginTop: 18 }}>Got it</Btn>
      </div>
    </div>
  );
}

function ExplainRow({ c, Icon, title, value, sub }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '12px 14px', background: c.surfaceAlt, borderRadius: 14 }}>
      <div style={{ width: 34, height: 34, borderRadius: 10, background: c.surface, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
        <Icon size={17} color={c.textMuted} />
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 13.5, fontWeight: 600, color: c.text }}>{title}</div>
        <div style={{ fontSize: 11.5, color: c.textMuted, marginTop: 1 }}>{sub}</div>
      </div>
      <div style={{ fontSize: 15, fontWeight: 700, color: c.text, fontVariantNumeric: 'tabular-nums' }}>{value}</div>
    </div>
  );
}

// Default device set for a 4-room HDB flat — three aircons across rooms,
// a water heater, and an always-on fridge.
const DEFAULT_DEVICES = [
  { key: 'ac-living', name: 'Living',  kind: 'ac',     watts: 1180, on: true  },
  { key: 'ac-bed',    name: 'Bedroom', kind: 'ac',     watts: 1100, on: false },
  { key: 'ac-study',  name: 'Study',   kind: 'ac',     watts: 980,  on: false },
  { key: 'water',     name: 'Heater',  kind: 'water',  watts: 2100, on: false },
  { key: 'fridge',    name: 'Fridge',  kind: 'fridge', watts: 95,   on: true, lockedOn: true },
];

// TipHero — one clean visual tile: a thermostat ring showing the target temp,
// the action in a few words, reward badges, and a full-width Set button.
function TipHero({ c, onDo, onSkip }) {
  const [pressed, setPressed] = React.useState(false);
  const target = 25, now = 24;

  // Full ring gauge (270° sweep), knob at the target position.
  const size = 96, stroke = 9, R = (size - stroke) / 2, C = size / 2;
  const circ = 2 * Math.PI * R;
  const sweep = 0.75;              // 270° visible
  const gap = circ * (1 - sweep);
  const rot = 90 + (1 - sweep) * 180;
  // Map a temperature (18–30°C) onto the arc fraction.
  const tempFrac = (temp) => (temp - 18) / (30 - 18);
  const nowFrac = tempFrac(now), targetFrac = tempFrac(target);

  return (
    <div style={{ background: c.accentSoft, borderRadius: 22, padding: 16 }}>
      {/* header row */}
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}>
        <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, padding: '4px 10px', borderRadius: 999, background: c.surface }}>
          <IcSparkle size={13} color={c.accent} />
          <span style={{ fontSize: 10.5, fontWeight: 800, color: '#8A5A1E', letterSpacing: 0.5 }}>QUICK WIN</span>
        </div>
        <button onClick={onSkip} aria-label="Dismiss"
          style={{ width: 28, height: 28, borderRadius: 14, border: 'none', background: 'rgba(0,0,0,0.05)', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' }}>
          <IcX size={15} color={c.textMuted} />
        </button>
      </div>

      {/* body: simple temp visual + text */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
        {/* clean thermostat chip: now → goal */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, background: c.surface, borderRadius: 16, padding: '12px 14px', flexShrink: 0 }}>
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
            <span style={{ fontSize: 26, fontWeight: 800, color: c.sky, letterSpacing: -1, lineHeight: 1 }}>{now}°</span>
            <span style={{ fontSize: 9, fontWeight: 700, color: c.textLight, letterSpacing: 0.3, marginTop: 3 }}>NOW</span>
          </div>
          <IcArrowR size={16} color={c.textLight} />
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
            <span style={{ fontSize: 26, fontWeight: 800, color: c.accent, letterSpacing: -1, lineHeight: 1 }}>{target}°</span>
            <span style={{ fontSize: 9, fontWeight: 700, color: c.accent, letterSpacing: 0.3, marginTop: 3 }}>GOAL</span>
          </div>
        </div>

        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, color: c.textMuted, fontSize: 12.5, fontWeight: 600 }}>
            <IcAC size={14} color={c.textMuted} /> Aircon
          </div>
          <div style={{ fontSize: 20, fontWeight: 800, color: c.text, marginTop: 5, lineHeight: 1.1, letterSpacing: -0.4 }}>
            One degree warmer
          </div>
          <div style={{ display: 'flex', gap: 6, marginTop: 10 }}>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, padding: '4px 9px', borderRadius: 999, background: c.goodSoft }}>
              <IcArrowD size={12} color="#2F6147" />
              <span style={{ fontSize: 12.5, fontWeight: 800, color: '#2F6147', fontVariantNumeric: 'tabular-nums' }}>$0.90</span>
            </span>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, padding: '4px 9px', borderRadius: 999, background: c.primarySoft }}>
              <IcCoins size={12} color={c.primary} />
              <span style={{ fontSize: 12.5, fontWeight: 800, color: c.primaryDk, fontVariantNumeric: 'tabular-nums' }}>+25</span>
            </span>
          </div>
        </div>
      </div>

      {/* full-width action */}
      <button
        onPointerDown={() => setPressed(true)}
        onPointerUp={() => setPressed(false)}
        onPointerLeave={() => setPressed(false)}
        onClick={onDo}
        style={{
          width: '100%', marginTop: 14, height: 48, border: 'none', borderRadius: 14,
          background: c.accent, color: '#fff', fontFamily: 'inherit', fontWeight: 800, fontSize: 15,
          display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, cursor: 'pointer',
          transform: pressed ? 'scale(0.98)' : 'scale(1)', transition: 'transform .12s',
          boxShadow: '0 4px 12px rgba(224,162,78,0.32)',
        }}>
        <IcCheck size={18} color="#fff" /> Set aircon to 25°
      </button>
    </div>
  );
}

// QuestTile — glanceable, visual quest chip for the Home preview. Mirrors the
// appliance tiles above it: one big icon, a 2-word action, the reward, and the
// whole tile is tappable to mark it done. No timers or long copy.
function QuestTile({ c, m, onComplete, wide }) {
  const Icon = m.icon;
  const done = m.state === 'done';
  const expired = m.state === 'expired';
  const active = m.state === 'active';
  const tone = done ? { bg: c.primarySoft, fg: c.primary }
    : active ? { bg: c.accentSoft, fg: c.accent }
    : { bg: c.primarySoft, fg: c.primary };

  const iconBox = (
    <div style={{ width: 46, height: 46, borderRadius: 14, background: tone.bg, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
      {Icon && <Icon size={26} color={tone.fg} />}
    </div>
  );
  const label = (
    <div style={{ fontSize: 14, fontWeight: 700, color: c.text, lineHeight: 1.15, letterSpacing: -0.2 }}>{m.short || m.title}</div>
  );
  const reward = (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 3, fontSize: 12.5, fontWeight: 800, color: done ? c.primary : c.primaryDk, fontVariantNumeric: 'tabular-nums' }}>
      <IcCoins size={12} color={c.primary} />+{m.points}
    </span>
  );
  const check = done ? (
    <div style={{ width: 24, height: 24, borderRadius: 999, background: c.primary, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
      <IcCheck size={13} color="#fff" />
    </div>
  ) : (
    <div style={{ width: 24, height: 24, borderRadius: 999, border: `2px solid ${active ? c.accent : c.border}`, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
      <IcCheck size={13} color={active ? c.accent : c.textLight} />
    </div>
  );

  const base = {
    gridColumn: wide ? '1 / -1' : 'auto',
    background: c.surface, border: `1.5px solid ${done ? c.primary + '44' : active ? c.accent + '55' : c.borderSoft}`,
    borderRadius: 16, cursor: done || expired ? 'default' : 'pointer', fontFamily: 'inherit',
    opacity: expired ? 0.45 : 1,
    WebkitTapHighlightColor: 'transparent',
  };
  const tap = done || expired ? undefined : onComplete;

  if (wide) {
    return (
      <button onClick={tap} style={{ ...base, padding: '12px 14px', display: 'flex', alignItems: 'center', gap: 12, textAlign: 'left' }}>
        {iconBox}
        <div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 4 }}>
          {label}
          {reward}
        </div>
        {check}
      </button>
    );
  }
  return (
    <button onClick={tap} style={{ ...base, padding: '13px 12px', position: 'relative', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8, textAlign: 'center' }}>
      <div style={{ position: 'absolute', top: 9, right: 9 }}>{check}</div>
      {iconBox}
      {label}
      {reward}
    </button>
  );
}

Object.assign(window, { HomeScreen, AppCard, MissionRow, QuestTile, StatusSheet, TipHero, DEFAULT_DEVICES, DEVICE_KINDS });