// Hi-fi Dashboard — parameterized by theme tokens from hifi-themes.jsx.
// Layout: sidebar | (hero photo with overlay) → KPI row → mid row → bottom row.
// Data comes from combined-data.jsx (ASSETS / ROUTINES / TODOS / CONTACTS).

const { FONT_DISPLAY, FONT_BODY, FONT_MONO } = window.HIFI;

// ---------- Small atoms ----------

const Card = ({ t, children, style = {}, pad = 18 }) => (
  <div style={{
    background: t.card,
    border: `1px solid ${t.border}`,
    borderRadius: 14,
    padding: pad,
    boxShadow: t.shadowCard,
    ...style,
  }}>{children}</div>
);

const Pill = ({ t, color, bg, children, style = {} }) => (
  <span style={{
    display: 'inline-flex', alignItems: 'center', gap: 5,
    padding: '2px 9px',
    background: bg,
    color,
    fontSize: 11,
    fontWeight: 500,
    letterSpacing: 0.1,
    borderRadius: 999,
    whiteSpace: 'nowrap',
    ...style,
  }}>{children}</span>
);

const StatusDot = ({ color, size = 8 }) => (
  <span style={{
    display: 'inline-block', width: size, height: size, borderRadius: '50%',
    background: color, flex: '0 0 auto',
  }}/>
);

const StatusBar = ({ color, h = 28 }) => (
  <span style={{
    display: 'inline-block', width: 3, height: h, borderRadius: 2,
    background: color, flex: '0 0 auto',
  }}/>
);

// ---------- Sidebar ----------

const DashNavRow = ({ t, icon, label, active, badge }) => (
  <div style={{
    display: 'flex', alignItems: 'center', gap: 12,
    padding: '9px 12px',
    borderRadius: 10,
    background: active ? t.hover : 'transparent',
    color: active ? t.ink : t.inkSoft,
    fontWeight: active ? 600 : 500,
    fontSize: 14,
    cursor: 'default',
  }}>
    <HIcon kind={icon} size={18} style={{ color: active ? t.ink : t.inkSoft }}/>
    <span style={{ flex: 1 }}>{label}</span>
    {badge != null && (
      <span style={{
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        minWidth: 20, height: 20, padding: '0 6px',
        background: t.accent, color: t.accentInk,
        borderRadius: 999, fontSize: 11, fontWeight: 600,
        fontFamily: FONT_MONO,
      }}>{badge}</span>
    )}
  </div>
);

// A small pill that opens the print-ready report. Disabled when the user
// hasn't entered any data yet — nothing to report on.
const ReportPill = ({ t }) => {
  const has = window.HubReport && window.HubReport.totalRecords() > 0;
  const disabled = !has;
  const handle = () => {
    if (disabled || !window.HubReport) return;
    window.HubReport.open();
  };
  return (
    <span
      onClick={handle}
      title={disabled
        ? 'Add assets, routines, or contacts to enable the report'
        : 'Generate a print-ready PDF report'}
      style={{
        display: 'inline-flex', alignItems: 'center', gap: 6,
        padding: '8px 14px',
        background: 'rgba(255,255,255,0.95)',
        border: `1px solid rgba(255,255,255,0.6)`,
        color: disabled ? t.inkSoft : t.ink,
        borderRadius: 10, fontSize: 13, fontWeight: 600,
        cursor: disabled ? 'not-allowed' : 'pointer',
        pointerEvents: 'auto',
        opacity: disabled ? 0.55 : 1,
      }}>
      <HIcon kind="file" size={14}/> Report
    </span>
  );
};

const HifiSidebar = ({ t, overdueN, todoN }) => (
  <div style={{
    width: 220, flex: '0 0 auto',
    background: t.paper,
    borderRight: `1px solid ${t.border}`,
    display: 'flex', flexDirection: 'column',
    padding: '20px 14px',
  }}>
    {/* Logo */}
    <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '0 4px 22px' }}>
      <HIcon kind="kingsleigh" size={26} style={{ color: t.ink }}/>
      <span style={{ fontFamily: FONT_DISPLAY, fontSize: 19, color: t.ink, lineHeight: 1, letterSpacing: -0.3, fontWeight: 600 }}>Kingsleigh</span>
    </div>
    {/* Nav */}
    <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
      <DashNavRow t={t} icon="home" label="Dashboard" active />
      <DashNavRow t={t} icon="grid" label="Assets" />
      <DashNavRow t={t} icon="repeat" label="Routines" badge={overdueN}/>
      <DashNavRow t={t} icon="phone" label="Contacts" />
      <DashNavRow t={t} icon="check" label="To-Dos" badge={todoN}/>
    </div>

    <div style={{ flex: 1 }}/>

    <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
      <DashNavRow t={t} icon="settings" label="Settings" />
    </div>
    {/* User block */}
    <div style={{
      marginTop: 12, padding: '10px 8px',
      borderTop: `1px solid ${t.border}`,
      display: 'flex', alignItems: 'center', gap: 10,
    }}>
      <div style={{
        width: 30, height: 30, borderRadius: '50%',
        background: `linear-gradient(135deg, ${t.accent} 0%, ${t.accentDeep} 100%)`,
        flex: '0 0 auto',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        color: t.accentInk, fontWeight: 600, fontSize: 13,
      }}>R</div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 13, color: t.ink, fontWeight: 600, lineHeight: 1.1 }}>Robert</div>
        <div style={{ fontSize: 11, color: t.inkSoft, lineHeight: 1.2 }}>Homeowner</div>
      </div>
      <HIcon kind="chevd" size={14} style={{ color: t.inkSoft }}/>
    </div>
  </div>
);

// ---------- Hero photo with overlay ----------

const HifiHero = ({ t, heroId, overdueN, soonN, nav }) => (
  <div style={{
    position: 'relative', height: 500, flexShrink: 0,
    margin: '18px 18px 0',
    borderRadius: 16,
    overflow: 'hidden',
    background: t.inkSoft,
  }}>
    <HeroImage t={t}/>
    {/* Scrim — a black gradient rising from the bottom, just tall enough to seat
        the greeting copy. Top half of the hero stays untouched. */}
    <div style={{
      position: 'absolute', left: 0, right: 0, bottom: 0,
      height: '55%',
      background: 'linear-gradient(to top, rgba(0,0,0,0.72) 0%, rgba(0,0,0,0.55) 35%, rgba(0,0,0,0.15) 75%, rgba(0,0,0,0) 100%)',
      pointerEvents: 'none',
    }}/>
    {/* Top-right actions */}
    <div className="hh-hide-mobile" style={{
      position: 'absolute', top: 16, right: 18,
      display: 'flex', alignItems: 'center', gap: 10,
      pointerEvents: 'none',
    }}>
      <div onClick={nav && (() => nav.openSearch && nav.openSearch())} style={{
        display: 'flex', alignItems: 'center', gap: 8,
        background: 'rgba(255,255,255,0.95)',
        backdropFilter: 'blur(8px)',
        border: `1px solid rgba(255,255,255,0.6)`,
        padding: '7px 12px',
        borderRadius: 10,
        width: 260, color: t.inkSoft, fontSize: 13,
        cursor: nav ? 'pointer' : 'default', pointerEvents: nav ? 'auto' : 'none',
      }}>
        <HIcon kind="search" size={14}/>
        <span style={{ flex: 1 }}>Search anything…</span>
        <span style={{
          fontFamily: FONT_MONO, fontSize: 10,
          padding: '1px 5px', background: t.borderSoft,
          color: t.inkSoft, borderRadius: 4, whiteSpace: 'nowrap',
        }}>{(typeof navigator !== 'undefined' && /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent)) ? '⌘K' : 'Ctrl K'}</span>
      </div>
      <span onClick={nav && (() => nav.openAdd('asset'))} style={{
        display: 'inline-flex', alignItems: 'center', gap: 6,
        padding: '8px 14px',
        background: t.accent, color: t.accentInk,
        borderRadius: 10, fontSize: 13, fontWeight: 600,
        cursor: nav ? 'pointer' : 'default', pointerEvents: nav ? 'auto' : 'none',
      }}>
        <HIcon kind="plus" size={14}/> Quick add
      </span>
      <ReportPill t={t}/>
    </div>
    {/* Bottom-left greeting */}
    <DashGreeting t={t} overdueN={overdueN} soonN={soonN}/>
  </div>
);

// ---------- KPI cards ----------

// Greeting that swaps morning / afternoon / evening based on the local clock,
// and shows today's date next to it.
const DashGreeting = ({ t, overdueN, soonN }) => {
  const [now, setNow] = React.useState(() => new Date());
  // Re-tick every 5 minutes so a session left open across noon updates itself.
  React.useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 5 * 60 * 1000);
    return () => clearInterval(id);
  }, []);
  const h = now.getHours();
  const partOfDay = h < 12 ? 'morning' : h < 17 ? 'afternoon' : 'evening';
  const name = (window.Account && window.Account.current().name.split(' ')[0]) || 'there';
  const dateLabel = now.toLocaleDateString(undefined, { weekday: 'short', month: 'short', day: 'numeric' });
  return (
    <div style={{
      position: 'absolute', left: 24, bottom: 22, color: '#fff',
      textShadow: '0 1px 12px rgba(0,0,0,0.4)',
    }}>
      <div style={{
        fontFamily: FONT_DISPLAY,
        fontSize: 'clamp(22px, 5.4vw, 40px)',
        lineHeight: 1.05,
        letterSpacing: -1.2, fontWeight: 600,
      }}>Good {partOfDay}, {name}</div>
      <div style={{
        marginTop: 8, fontSize: 13, color: 'rgba(255,255,255,0.85)',
        display: 'flex', alignItems: 'center', gap: 10,
        fontFamily: FONT_BODY,
      }}>
        <span>{dateLabel}</span>
        {overdueN > 0 && <><span style={{ opacity: 0.5 }}>·</span><span style={{ color: '#ff9a8f' }}>{overdueN} overdue</span></>}
        {soonN > 0 && <><span style={{ opacity: 0.5 }}>·</span><span>{soonN} due soon</span></>}
      </div>
    </div>
  );
};

const KPICard = ({ t, value, label, sub, color = null, trend = 'up', trendValue, onClick }) => (
  <Card t={t} pad={18} style={{ display: 'flex', flexDirection: 'column', gap: 6 }} onClick={onClick}>
    <div style={{
      fontFamily: FONT_DISPLAY, fontSize: 52, lineHeight: 0.95,
      color: color || t.ink, fontWeight: 500, letterSpacing: -1.8,
      fontVariantNumeric: 'tabular-nums',
    }}>{value}</div>
    <div style={{ fontSize: 14, color: t.ink, fontWeight: 500, marginTop: 4 }}>{label}</div>
    {trendValue && value !== 0 ? (
      <div style={{
        display: 'flex', alignItems: 'center', gap: 4,
        fontSize: 12, color: t.inkSoft, fontFamily: FONT_MONO,
      }}>
        <HIcon kind={trend === 'up' ? 'arrow-up-right' : 'arrow-down-right'} size={11} style={{
          color: trend === 'up' ? t.ok : t.overdue,
        }}/>
        <span>{trendValue}</span>
        <span>{sub}</span>
      </div>
    ) : (
      <div style={{ fontSize: 12, color: t.inkFaint, fontFamily: FONT_MONO }}>{value === 0 ? 'All clear' : '\u00a0'}</div>
    )}
  </Card>
);

const KPIRow = ({ t, overdueN, soonN, todoN, contactN, nav }) => (
  <div className="hh-cols-4" style={{
    display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 14,
    padding: '14px 18px 0',
  }}>
    <KPICard t={t} value={overdueN} label="Overdue" color={t.overdue} trend="up" trendValue="20%" sub="vs last month" onClick={nav && (() => nav.go('routines'))}/>
    <KPICard t={t} value={soonN} label="Due soon" color={t.soon} trend="down" trendValue="10%" sub="vs last month" onClick={nav && (() => nav.go('routines'))}/>
    <KPICard t={t} value={todoN} label="Open to-dos" trend="up" trendValue="8%" sub="vs last month" onClick={nav && (() => nav.go('todos'))}/>
    <KPICard t={t} value={contactN} label="Contacts" trend="up" trendValue="2" sub="new" onClick={nav && (() => nav.go('contacts'))}/>
  </div>
);

// ---------- Mid row: Upcoming · Donut · Calendar ----------

const PanelHeader = ({ t, title, action }) => (
  <div style={{
    display: 'flex', alignItems: 'center', justifyContent: 'space-between',
    marginBottom: 14,
  }}>
    <div style={{ fontSize: 14, fontWeight: 600, color: t.ink }}>{title}</div>
    {action}
  </div>
);

const ViewAllLink = ({ t, onClick }) => (
  <span onClick={onClick} style={{ fontSize: 12, color: t.inkSoft, cursor: onClick ? 'pointer' : 'default' }}>View all →</span>
);

const UpcomingPanel = ({ t, nav }) => {
  const today = new Date(); today.setHours(0, 0, 0, 0);

  // Build a flat, sorted list of upcoming entries: routine-due AND reminder.
  const entries = [];
  ROUTINES.forEach(r => {
    if (r.date) {
      const due = new Date(r.date + 'T00:00:00');
      if (!isNaN(due) && due >= today) {
        entries.push({ key: r.id + ':due', kind: 'due', routine: r, when: due });
      }
    }
    if (window.Hub && window.Hub.reminderDate) {
      const rd = window.Hub.reminderDate(r);
      if (rd && rd >= today) {
        entries.push({ key: r.id + ':rem', kind: 'reminder', routine: r, when: rd });
      }
    }
  });
  entries.sort((a, b) => a.when - b.when);
  const upcoming = entries.slice(0, 6);

  const overdueRoutines = ROUTINES
    .filter(r => r.status === 'overdue' && r.date)
    .sort((a, b) => new Date(a.date) - new Date(b.date)); // oldest first

  const daysAway = (d) => {
    const diff = Math.round((d - today) / 86400000);
    if (diff === 0) return 'today';
    if (diff === 1) return 'tomorrow';
    return `in ${diff} days`;
  };
  const daysLate = (d) => {
    const diff = Math.round((today - d) / 86400000);
    return `${diff} day${diff === 1 ? '' : 's'} late`;
  };

  const Row = ({ icon, iconBg, iconColor, dot, title, sub, onClick }) => (
    <div onClick={onClick} style={{
      display: 'flex', alignItems: 'center', gap: 12,
      cursor: onClick ? 'pointer' : 'default',
    }}>
      {icon ? (
        <div style={{
          width: 28, height: 28, borderRadius: 7, flex: '0 0 auto',
          background: iconBg, color: iconColor,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}><HIcon kind={icon} size={14}/></div>
      ) : (
        <StatusDot color={dot} size={9}/>
      )}
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 13, color: t.ink, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{title}</div>
        <div style={{ fontSize: 12, color: t.inkSoft, marginTop: 2, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{sub}</div>
      </div>
    </div>
  );

  return (
    <Card t={t} pad={18}>
      <PanelHeader t={t} title="Upcoming" action={<ViewAllLink t={t} onClick={nav && (() => nav.go('routines'))}/>}/>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        {upcoming.length === 0 && overdueRoutines.length === 0 && (
          <div style={{ fontSize: 13, color: t.inkFaint, padding: '14px 0' }}>Nothing upcoming — you’re all caught up.</div>
        )}

        {upcoming.length === 0 && overdueRoutines.length > 0 && (
          <div style={{ fontSize: 13, color: t.inkFaint, padding: '4px 0' }}>Nothing coming up next.</div>
        )}

        {upcoming.map(e => {
          const r = e.routine;
          const a = findAsset(r.asset);
          const assetName = a ? a.name : '';
          if (e.kind === 'reminder') {
            return (
              <Row key={e.key}
                icon="bell"
                iconBg={t.infoBg} iconColor={t.info || t.inkSoft}
                title={`Reminder: ${r.task}`}
                sub={`${assetName ? assetName + ' · ' : ''}${daysAway(e.when)}`}
                onClick={nav && (() => nav.openRoutine(r.id))}/>
            );
          }
          const c = r.status === 'soon' ? t.soon : t.ok;
          return (
            <Row key={e.key}
              dot={c}
              title={r.task}
              sub={`${assetName ? assetName + ' · ' : ''}${r.nextLabel}`}
              onClick={nav && (() => nav.openRoutine(r.id))}/>
          );
        })}

        {overdueRoutines.length > 0 && (
          <>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginTop: 4 }}>
              <div style={{ flex: 1, height: 1, background: t.border }}/>
              <span style={{ fontSize: 10.5, color: t.overdue, fontWeight: 600, letterSpacing: 0.8, fontFamily: FONT_MONO }}>
                OVERDUE · {overdueRoutines.length}
              </span>
              <div style={{ flex: 1, height: 1, background: t.border }}/>
            </div>
            {overdueRoutines.map(r => {
              const a = findAsset(r.asset);
              const assetName = a ? a.name : '';
              const due = new Date(r.date + 'T00:00:00');
              return (
                <Row key={r.id + ':od'}
                  dot={t.overdue}
                  title={r.task}
                  sub={`${assetName ? assetName + ' · ' : ''}${daysLate(due)}`}
                  onClick={nav && (() => nav.openRoutine(r.id))}/>
              );
            })}
          </>
        )}
      </div>
    </Card>
  );
};

const OverviewPanel = ({ t, overdueN, soonN, todoN, assetN, contactN, nav }) => {
  const rows = [
    { label: 'Overdue',    value: overdueN, color: t.overdue, target: 'routines' },
    { label: 'Due soon',   value: soonN,    color: t.soon,    target: 'routines' },
    { label: 'Open to-dos', value: todoN,   color: t.info || t.inkSoft, target: 'todos' },
    { label: 'Assets',     value: assetN,   color: t.ink,     target: 'assets' },
    { label: 'Contacts',   value: contactN, color: t.ink,     target: 'contacts' },
  ];
  return (
    <Card t={t} pad={18}>
      <PanelHeader t={t} title="Overview" />
      <div style={{ display: 'flex', flexDirection: 'column' }}>
        {rows.map((r, i) => (
          <div key={r.label}
            onClick={nav && (() => nav.go(r.target))}
            style={{
              display: 'flex', alignItems: 'baseline', justifyContent: 'space-between',
              padding: '11px 0',
              borderTop: i === 0 ? 'none' : `1px solid ${t.borderSoft || t.border}`,
              cursor: nav ? 'pointer' : 'default',
            }}>
            <span style={{ fontSize: 13.5, color: t.ink, fontWeight: 500 }}>{r.label}</span>
            <span style={{
              fontFamily: FONT_DISPLAY,
              fontSize: 26, lineHeight: 1,
              color: r.value === 0 ? t.inkFaint : r.color,
              fontWeight: 500, letterSpacing: -0.8,
              fontVariantNumeric: 'tabular-nums',
            }}>{r.value}</span>
          </div>
        ))}
      </div>
    </Card>
  );
};

const CalendarPanel = ({ t, events, nav }) => {
  // May 2026 starts Friday. 7×5 grid. Today = 27.
  const startOffset = 5;
  const days = Array.from({ length: 35 }, (_, i) => i - startOffset + 1);
  return (
    <Card t={t} pad={18}>
      <PanelHeader t={t} title="Calendar" action={<span onClick={nav && (() => nav.go('routines'))} style={{ fontSize: 12, color: t.inkSoft, cursor: nav ? 'pointer' : 'default' }}>May 2026 →</span>}/>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 4 }}>
        {['S','M','T','W','T','F','S'].map((d, i) => (
          <div key={'h'+i} style={{ textAlign: 'center', fontSize: 10, color: t.inkFaint, fontWeight: 600, letterSpacing: 0.5, padding: '0 0 4px' }}>{d}</div>
        ))}
        {days.map((d, i) => {
          const inMonth = d > 0 && d <= 31;
          const isToday = d === 27;
          const has = inMonth && events[d];
          return (
            <div key={i} style={{
              aspectRatio: '1', display: 'flex', alignItems: 'center', justifyContent: 'center',
              borderRadius: 7,
              background: isToday ? t.accent : 'transparent',
              color: isToday ? t.accentInk : (inMonth ? t.ink : t.inkFaint),
              fontSize: 12, fontWeight: isToday ? 600 : 400,
              fontFamily: FONT_MONO,
              fontVariantNumeric: 'tabular-nums',
              position: 'relative',
            }}>
              {inMonth ? d : ''}
              {has && !isToday && (
                <span style={{
                  position: 'absolute', bottom: 4,
                  width: 4, height: 4, borderRadius: '50%',
                  background: has,
                }}/>
              )}
            </div>
          );
        })}
      </div>
    </Card>
  );
};

// ---------- Bottom row ----------

const PriorityBars = ({ t, nav }) => {
  const high = TODOS.filter(x => x.priority === 'High').length;
  const norm = TODOS.filter(x => x.priority === 'Normal').length;
  const info = TODOS.filter(x => x.type === 'Info Gap').length;
  const data = [
    { label: 'High',     value: high, color: t.overdue },
    { label: 'Normal',   value: norm, color: t.soon },
    { label: 'Info gaps', value: info, color: t.info },
  ];
  const max = Math.max(...data.map(d => d.value)) || 1;
  return (
    <Card t={t} pad={18}>
      <PanelHeader t={t} title="To-Dos by Priority" action={<ViewAllLink t={t} onClick={nav && (() => nav.go('todos'))}/>}/>
      <div style={{ display: 'flex', alignItems: 'flex-end', gap: 22, height: 110, paddingTop: 4 }}>
        {data.map((d, i) => (
          <div key={i} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8 }}>
            <div style={{ position: 'relative', height: 92, width: '100%', maxWidth: 64, display: 'flex', alignItems: 'flex-end' }}>
              <div style={{
                width: '100%', height: `${(d.value / max) * 100}%`,
                background: d.color, borderRadius: '6px 6px 0 0',
                position: 'relative',
              }}>
                <div style={{
                  position: 'absolute', top: -20, left: 0, right: 0, textAlign: 'center',
                  fontFamily: FONT_DISPLAY, fontSize: 18, color: t.ink, fontWeight: 600,
                  letterSpacing: -0.5,
                }}>{d.value}</div>
              </div>
            </div>
            <div style={{ fontSize: 11, color: t.inkSoft, fontWeight: 500 }}>{d.label}</div>
          </div>
        ))}
      </div>
    </Card>
  );
};

// ---------- Reminders this week (replaces the old activity feed) ----------

const RemindersThisWeekPanel = ({ t, nav }) => {
  const today = new Date(); today.setHours(0, 0, 0, 0);
  const horizon = new Date(today); horizon.setDate(horizon.getDate() + 7);
  const items = (window.Hub && window.Hub.reminderDate)
    ? ROUTINES
        .map(r => { const rd = window.Hub.reminderDate(r); return rd ? { r, rd } : null; })
        .filter(x => x && x.rd >= today && x.rd <= horizon)
        .sort((a, b) => a.rd - b.rd)
    : [];
  const fmt = (d) => d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
  const daysAway = (d) => {
    const diff = Math.round((d - today) / 86400000);
    return diff === 0 ? 'today' : diff === 1 ? 'tomorrow' : `in ${diff} days`;
  };
  return (
    <Card t={t} pad={18}>
      <PanelHeader t={t} title="Reminders this week" action={<ViewAllLink t={t} onClick={nav && (() => nav.go('routines'))}/>}/>
      {items.length === 0 ? (
        <div style={{ fontSize: 13, color: t.inkFaint, padding: '8px 0', display: 'flex', alignItems: 'center', gap: 8 }}>
          <HIcon kind="bell" size={14}/> No reminders coming up in the next 7 days.
        </div>
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
          {items.slice(0, 5).map(({ r, rd }) => {
            const a = findAsset(r.asset);
            return (
              <div key={r.id} onClick={nav && (() => nav.openRoutine(r.id))} style={{ display: 'flex', alignItems: 'flex-start', gap: 12, cursor: nav ? 'pointer' : 'default' }}>
                <div style={{
                  width: 30, height: 30, borderRadius: 8, flex: '0 0 auto',
                  border: `1.5px dashed ${t.info || t.inkSoft}`,
                  background: t.infoBg, color: t.info || t.inkSoft,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}><HIcon kind="bell" size={14}/></div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13, color: t.ink, fontWeight: 500 }}>{r.task}</div>
                  <div style={{ fontSize: 12, color: t.inkSoft, marginTop: 2 }}>
                    {a ? a.name + ' · ' : ''}due {r.nextLabel} · reminder {daysAway(rd)}
                  </div>
                </div>
                <span style={{
                  fontFamily: FONT_MONO, fontSize: 11, color: t.inkSoft,
                  border: `1px solid ${t.border}`, background: t.card,
                  padding: '3px 8px', borderRadius: 999, flex: '0 0 auto',
                }}>{fmt(rd)}</span>
              </div>
            );
          })}
          {items.length > 5 && (
            <div style={{ fontSize: 12, color: t.inkFaint, paddingLeft: 42 }}>+{items.length - 5} more</div>
          )}
        </div>
      )}
    </Card>
  );
};

// ---------- First-run / welcome dashboard ----------

const FirstRunStep = ({ t, n, icon, title, body, action, onClick }) => (
  <div onClick={onClick} style={{
    display: 'flex', gap: 14, alignItems: 'flex-start', padding: '16px 18px',
    border: `1px solid ${t.border}`, borderRadius: 12, background: t.card, cursor: 'pointer',
  }}>
    <span style={{
      width: 34, height: 34, borderRadius: 9, flex: '0 0 auto',
      background: t.inputBg, border: `1px solid ${t.border}`, color: t.inkSoft,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
    }}><HIcon kind={icon} size={17}/></span>
    <div style={{ flex: 1, minWidth: 0 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
        <span style={{ fontFamily: FONT_MONO, fontSize: 11, color: t.inkFaint, fontWeight: 600 }}>STEP {n}</span>
      </div>
      <div style={{ fontSize: 15, color: t.ink, fontWeight: 600, marginTop: 2 }}>{title}</div>
      <div style={{ fontSize: 13, color: t.inkSoft, marginTop: 3, lineHeight: 1.45 }}>{body}</div>
      <div style={{ fontSize: 13, color: t.accentDeep, fontWeight: 500, marginTop: 9, display: 'flex', alignItems: 'center', gap: 5 }}>{action} <HIcon kind="chev" size={13}/></div>
    </div>
  </div>
);

const FirstRunDashboard = ({ t, heroId, nav }) => {
  const name = window.Account ? window.Account.current().name.split(' ')[0] : 'there';
  return (
    <>
      <HifiHero t={t} heroId={heroId} overdueN={0} soonN={0} nav={nav}/>
      <div style={{ padding: '20px 18px 28px', display: 'grid', gridTemplateColumns: '1fr', gap: 14, maxWidth: 760 }}>
        <Card t={t} pad={24}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 6 }}>
            <span style={{ width: 44, height: 44, borderRadius: 12, background: t.accent, color: t.accentInk, display: 'flex', alignItems: 'center', justifyContent: 'center', flex: '0 0 auto' }}><HIcon kind="kingsleigh" size={24}/></span>
            <div>
              <div style={{ fontFamily: FONT_DISPLAY, fontSize: 24, fontWeight: 600, letterSpacing: -0.5, color: t.ink }}>Welcome to your Hub, {name}</div>
              <div style={{ fontSize: 14, color: t.inkSoft, marginTop: 2 }}>Let’s get your home set up. Three steps and you’re running.</div>
            </div>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginTop: 16 }}>
            <FirstRunStep t={t} n={1} icon="grid"
              title="Add your first asset"
              body="Start with one thing you maintain — your furnace, hot tub, or a key appliance."
              action="Add an asset" onClick={() => nav.openAdd('asset')}/>
            <FirstRunStep t={t} n={2} icon="file"
              title="Attach its documents"
              body="Drop the manual, warranty, or a photo of the rating plate onto the asset so it's all in one place."
              action="See how it works" onClick={() => nav.go('assets')}/>
            <FirstRunStep t={t} n={3} icon="users"
              title="Invite your household"
              body="Add the people who share the upkeep, then assign routines and to-dos to them."
              action="Manage household" onClick={() => nav.go('profile')}/>
          </div>
        </Card>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, color: t.inkFaint, fontSize: 12.5, paddingLeft: 4 }}>
          <HIcon kind="activity" size={14}/> Your dashboard fills in automatically as you add assets and routines.
        </div>
      </div>
    </>
  );
};

// ---------- Composition ----------

// Body only (no sidebar) — used inside the full app shell, which supplies the
// persistent clickable sidebar. `nav` is optional; when present, rows/cards click through.
const HifiDashboardBody = ({ theme: t, heroId, nav }) => {
  const overdue = ROUTINES.filter(r => r.status === 'overdue');
  const soon    = ROUTINES.filter(r => r.status === 'soon');
  const todos   = TODOS.filter(td => td.status !== 'Done');
  // Map day-of-month → dominant color. Routines win; reminders fall in as a
  // muted info color only on days without a routine dot.
  const events = {};
  ROUTINES.forEach(r => {
    if (r.day != null && r.day >= 1 && r.day <= 31) {
      const c = r.status === 'overdue' ? t.overdue : r.status === 'soon' ? t.soon : t.ok;
      if (!events[r.day]) events[r.day] = c;
    }
  });
  // Reminders for routines whose reminder falls in the current month.
  const todayCal = new Date(); todayCal.setHours(0, 0, 0, 0);
  if (window.Hub && window.Hub.reminderDate) {
    ROUTINES.forEach(r => {
      const rd = window.Hub.reminderDate(r);
      if (!rd) return;
      if (rd.getFullYear() === todayCal.getFullYear() && rd.getMonth() === todayCal.getMonth()) {
        const day = rd.getDate();
        if (!events[day]) events[day] = t.info || t.inkSoft;
      }
    });
  }
  // First-run: a fresh household with no data at all → a welcoming get-started view
  // instead of a wall of zeros.
  const isFirstRun = ASSETS.length === 0 && ROUTINES.length === 0 && TODOS.length === 0 && CONTACTS.length === 0;
  if (isFirstRun && nav) {
    return <FirstRunDashboard t={t} heroId={heroId} nav={nav}/>;
  }

  return (
    <>
      <HifiHero t={t} heroId={heroId} overdueN={overdue.length} soonN={soon.length} nav={nav}/>
      <div className="hh-dash-mid" style={{
        display: 'grid', gridTemplateColumns: '1.3fr 1fr 1fr', gap: 14,
        padding: '18px 18px 24px',
        alignItems: 'start',
      }}>
        <UpcomingPanel t={t} nav={nav}/>
        <OverviewPanel t={t}
          overdueN={overdue.length} soonN={soon.length}
          todoN={todos.length} assetN={ASSETS.length} contactN={CONTACTS.length}
          nav={nav}/>
        <CalendarPanel t={t} events={events} nav={nav}/>
      </div>
    </>
  );
};

// Standalone dashboard (with its own static sidebar) — used by Hi-Fi Dashboard.html.
const HifiDashboard = ({ theme: t, heroId }) => {
  const overdue = ROUTINES.filter(r => r.status === 'overdue');
  const todos   = TODOS.filter(td => td.status !== 'Done');
  return (
    <div style={{
      width: 1280, height: 1180,
      background: t.paper,
      fontFamily: FONT_BODY,
      color: t.ink,
      display: 'flex',
      fontSize: 14,
    }}>
      <HifiSidebar t={t} overdueN={overdue.length} todoN={todos.length}/>
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0, overflow: 'hidden' }}>
        <HifiDashboardBody theme={t} heroId={heroId}/>
      </div>
    </div>
  );
};

window.HifiDashboard = HifiDashboard;
window.HifiDashboardBody = HifiDashboardBody;
