/* web.jsx — desktop shell + Today tab */

function useIsNarrow(breakpoint = 760) {
  const [isNarrow, setIsNarrow] = React.useState(() => window.innerWidth <= breakpoint);
  React.useEffect(() => {
    const update = () => setIsNarrow(window.innerWidth <= breakpoint);
    window.addEventListener('resize', update);
    return () => window.removeEventListener('resize', update);
  }, [breakpoint]);
  return isNarrow;
}

function WebShell({ activeTab = 'today', children, onTabChange, cycleLength = 3, fullPage = false }) {
  const isNarrow = useIsNarrow(860);
  const tabs = ['today', 'log', 'history', 'insights', 'settings'];
  const padded = String(cycleLength).padStart(2, '0');
  return (
    <div style={{
      width: fullPage ? '100%' : 1280,
      minHeight: fullPage ? '100vh' : 800,
      height: fullPage && !isNarrow ? '100vh' : 'auto',
      minWidth: undefined,
      background: 'var(--paper)', color: 'var(--ink)',
      fontFamily: "'Geist', system-ui, sans-serif",
      position: 'relative', overflow: fullPage && isNarrow ? 'auto' : 'hidden',
      boxShadow: fullPage ? 'none' : '0 30px 80px var(--hair-strong), 0 0 0 1px var(--hair-faint)',
      borderRadius: fullPage ? 0 : 14,
    }}>
      {/* paper grid */}
      <div style={{
        position: 'absolute', inset: 0,
        backgroundImage: 'radial-gradient(circle at 1px 1px, var(--grid) 1px, transparent 0)',
        backgroundSize: '20px 20px',
        pointerEvents: 'none',
      }} />

      {/* top nav */}
      <header style={{
        position: 'relative', zIndex: 2,
        display: 'flex', alignItems: isNarrow ? 'flex-start' : 'center', justifyContent: 'space-between',
        gap: isNarrow ? 14 : 20,
        flexDirection: isNarrow ? 'column' : 'row',
        padding: isNarrow ? '16px 18px' : '20px 40px',
        borderBottom: '1px solid var(--hair-faint)',
      }}>
        <div style={{ display: 'flex', alignItems: isNarrow ? 'flex-start' : 'center', gap: isNarrow ? 12 : 24, flexDirection: isNarrow ? 'column' : 'row', width: isNarrow ? '100%' : 'auto' }}>
          <Wordmark size={30} />
          <nav style={{ display: 'flex', gap: isNarrow ? 14 : 22, flexWrap: 'wrap' }}>
            {tabs.map((t) => {
              const active = t === activeTab;
              return (
                <a key={t} href="#" onClick={(e) => { e.preventDefault(); onTabChange && onTabChange(t); }}
                  style={{
                    fontSize: 13.5, color: active ? 'var(--ink)' : 'var(--ink-3)',
                    textDecoration: 'none', fontWeight: active ? 500 : 400,
                    textTransform: 'capitalize', paddingBottom: 4,
                    borderBottom: active ? '1.5px solid var(--ink)' : '1.5px solid transparent',
                    cursor: 'pointer',
                  }}>
                  {t}
                </a>
              );
            })}
          </nav>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 14, width: isNarrow ? '100%' : 'auto', justifyContent: isNarrow ? 'space-between' : 'flex-start' }}>
          <span className="mono" style={{ fontSize: 11.5, color: 'var(--ink-3)', letterSpacing: '0.06em' }}>
            CYCLE · DAY {padded} / {padded}
          </span>
          <div style={{
            width: 36, height: 36, borderRadius: 99,
            background: 'var(--paper-2)', border: '1px solid var(--hair-strong)',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontFamily: "'Instrument Serif', serif", fontSize: 16, color: 'var(--ink-2)',
          }}>A</div>
        </div>
      </header>

      <div style={{ position: 'relative', zIndex: 2, height: fullPage && !isNarrow ? 'calc(100% - 73px)' : 'auto', minHeight: isNarrow ? 0 : undefined }}>
        {children}
      </div>
    </div>
  );
}

// ── TODAY tab ────────────────────────────────────────────────────

function WebToday({ scenario, showMarkers, markerStyle, connector, tdee = TDEE, windowDays = 3, onUpdateDailyIntake, unitSystem = 'metric' }) {
  const isNarrow = useIsNarrow();
  const reading = getBalanceReading(scenario.days, windowDays, tdee);
  const [isEditingToday, setIsEditingToday] = React.useState(false);
  const [todayDraft, setTodayDraft] = React.useState('');
  const days = reading.days;
  const today = days[days.length - 1];
  const zone = reading.state;
  const avg = reading.averageBalance;
  const accent = ZONES[zone].hex;
  const todayZone = classify(today.balance, tdee);
  const accentTone = ZONES[todayZone].hex;
  const pre = 'storage';
  const post = zone;
  const projectedPerWeek = unitSystem === 'imperial' ? reading.projectedKgPerWeek * 2.2046226218 : reading.projectedKgPerWeek;
  const projectionUnit = unitSystem === 'imperial' ? 'lb / week' : 'kg / week';
  const kgProjection = Math.abs(projectedPerWeek).toFixed(2);
  const kgSign = projectedPerWeek > 0 ? '+' : projectedPerWeek < 0 ? '-' : '';
  const dayCount = days.length;
  const statusBody = todayStatusBody(reading, unitSystem);
  const confidenceLine = todayConfidenceLine(reading);
  const todayRows = scenario.dailyTotalOnly
    ? [{ time: 'daily', name: 'Manual daily total', kcal: Number.isFinite(today.intake) ? today.intake : null }]
    : TODAY_MEALS;
  const todayRight = scenario.dailyTotalOnly
    ? `manual total · ${Number.isFinite(today.intake) ? `${today.intake} kcal` : 'N/A'}`
    : `${TODAY_MEALS.length} meals · ${today.intake} kcal`;
  const actionLabel = scenario.dailyTotalOnly ? 'edit daily total' : 'log a meal';
  const rangeLabel = `${displayDate(days[0].date)} — ${displayDate(days[dayCount - 1].date)}`;

  React.useEffect(() => {
    setTodayDraft(Number.isFinite(today.intake) ? String(today.intake) : '');
  }, [today.dateKey || today.date, today.intake]);

  const saveTodayDraft = () => {
    if (!scenario.dailyTotalOnly || !onUpdateDailyIntake || !Number.isFinite(Number(todayDraft))) return;
    onUpdateDailyIntake(today.dateKey || today.date, Number(todayDraft));
    setIsEditingToday(false);
  };

  return (
    <div style={{
      display: 'grid', gridTemplateColumns: isNarrow ? '1fr' : '1fr 480px',
      gap: isNarrow ? 24 : 0,
      padding: isNarrow ? '18px' : '20px 40px 28px', height: isNarrow ? 'auto' : '100%', boxSizing: 'border-box',
    }}>
      {/* LEFT */}
      <div style={{ display: 'flex', flexDirection: 'column', paddingRight: isNarrow ? 0 : 36, minWidth: 0 }}>
        <SectionEyebrow left={`${dayCount}-day reading`} right={rangeLabel} />
        <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative' }}>
          <div style={{ position: 'relative' }}>
            <FluidOrb days={days} size={isNarrow ? Math.min(330, window.innerWidth - 54) : 460} showMarkers={showMarkers} markerStyle={markerStyle} connector={connector} tdee={tdee} />
            {!isNarrow && <ZoneRail />}
          </div>
        </div>
        <div style={{ marginTop: 12 }}>
          <CicoDayStrip days={days} tdee={tdee} />
        </div>
      </div>

      {/* RIGHT */}
      <aside style={{ display: 'flex', flexDirection: 'column', gap: 18, borderLeft: isNarrow ? 'none' : '1px solid var(--hair)', borderTop: isNarrow ? '1px solid var(--hair)' : 'none', paddingLeft: isNarrow ? 0 : 36, paddingTop: isNarrow ? 18 : 0, minWidth: 0 }}>
        <div>
          <div style={{
            fontFamily: "'Instrument Serif', serif",
            fontSize: isNarrow ? 46 : 58, lineHeight: 0.98, color: 'var(--ink)',
          }}>
            <div style={{ color: 'var(--ink-3)', fontSize: 22, marginBottom: 6, letterSpacing: '0.02em' }}>{pre}</div>
            <span style={{ color: accent, fontStyle: 'italic' }}>{post}.</span>
          </div>
          <div style={{ marginTop: 14, fontSize: 14, color: 'var(--ink-2)', lineHeight: 1.5, maxWidth: 380 }}>
            {statusBody}
          </div>
          <div className="mono" style={{
            marginTop: 10, fontSize: 11, lineHeight: 1.45, color: 'var(--ink-3)',
            letterSpacing: '0.04em', textTransform: 'uppercase',
          }}>
            {confidenceLine}
          </div>
        </div>

        <div style={{
          background: 'var(--paper-2)', border: '1px solid var(--hair)',
          borderRadius: 14, padding: '14px 4px',
          display: 'grid', gridTemplateColumns: isNarrow ? '1fr' : '1fr 1px 1fr 1px 1fr', alignItems: 'center',
        }}>
          <WebStat label={`${dayCount}-day avg`} value={(avg > 0 ? '+' : '') + Math.round(avg)} unit="kcal/day" toneColor={accent} />
          {!isNarrow && <VDivW />}
          <WebStat label="projected" value={`${kgSign}${kgProjection}`} unit={projectionUnit} />
          {!isNarrow && <VDivW />}
          <WebStat label="TDEE" value={tdee} unit="kcal" />
        </div>

        <div>
          <SectionHead title="today" right={todayRight} />
          <div style={{
            background: 'var(--paper-2)', border: '1px solid var(--hair)',
            borderRadius: 14, padding: '4px 0', marginTop: 8,
            maxHeight: 232, overflow: 'auto',
          }}>
            {todayRows.map((m, i) => (
              <div key={i} style={{
                display: 'grid', gridTemplateColumns: '56px 1fr 64px',
                alignItems: 'center', padding: '11px 16px',
                borderTop: i === 0 ? 'none' : '1px solid var(--hair-faint)',
              }}>
                <span className="mono" style={{ fontSize: 11.5, color: 'var(--ink-3)', letterSpacing: '0.04em' }}>{m.time}</span>
                <span style={{ fontSize: 13.5, color: 'var(--ink)' }}>{m.name}</span>
                <span className="mono num-tab" style={{ fontSize: 13, color: 'var(--ink-2)', textAlign: 'right' }}>{Number.isFinite(m.kcal) ? `${m.kcal} kcal` : 'N/A'}</span>
              </div>
            ))}
          </div>
          {scenario.dailyTotalOnly && isEditingToday ? (
            <div style={{
              marginTop: 10,
              background: 'var(--paper-2)', border: '1px solid var(--hair)',
              borderRadius: 14, padding: 14,
              display: 'grid', gridTemplateColumns: isNarrow ? '1fr' : '1fr auto auto', gap: 10,
              alignItems: 'end',
            }}>
              <label style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
                <span className="mono" style={{ fontSize: 10.5, color: 'var(--ink-3)', letterSpacing: '0.08em', textTransform: 'uppercase' }}>today total</span>
                <input
                  inputMode="numeric"
                  autoFocus
                  value={todayDraft}
                  onChange={(e) => setTodayDraft(e.target.value)}
                  onKeyDown={(e) => {
                    if (e.key === 'Enter') saveTodayDraft();
                    if (e.key === 'Escape') setIsEditingToday(false);
                  }}
                  style={{
                    width: '100%',
                    minWidth: 0,
                    background: 'transparent',
                    border: 'none',
                    outline: 'none',
                    color: 'var(--ink)',
                    fontFamily: "'JetBrains Mono', monospace",
                    fontSize: 28,
                    fontVariantNumeric: 'tabular-nums',
                  }}
                />
              </label>
              <button onClick={saveTodayDraft} style={todayEditorButtonStyle(false)}>save</button>
              <button onClick={() => setIsEditingToday(false)} style={todayEditorButtonStyle(true)}>cancel</button>
            </div>
          ) : (
            <button style={primaryBtn()} onClick={() => {
              if (!scenario.dailyTotalOnly || !onUpdateDailyIntake) return;
              setTodayDraft(Number.isFinite(today.intake) ? String(today.intake) : '');
              setIsEditingToday(true);
            }}>
              <span style={{ fontFamily: "'Instrument Serif', serif", fontSize: 22, marginRight: 8, lineHeight: 1 }}>+</span>
              <span style={{ fontSize: 13.5, fontWeight: 500 }}>{actionLabel}</span>
              <span style={{ flex: 1 }} />
              <kbd style={kbdStyle()}>⌘ K</kbd>
            </button>
          )}
        </div>
      </aside>
    </div>
  );
}

// ── Shared bits used across tabs ───────────────────────────────

function todayStatusBody(reading, unitSystem = 'metric') {
  const n = reading.days.length;
  const avg = Math.round(reading.averageBalance);
  const absAvg = Math.abs(avg);
  const projected = unitSystem === 'imperial' ? reading.projectedKgPerWeek * 2.2046226218 : reading.projectedKgPerWeek;
  const projectionUnit = unitSystem === 'imperial' ? 'lb' : 'kg';
  const projectedAmount = Math.abs(projected).toFixed(2);
  const threshold = Math.round(reading.threshold);

  if (reading.state === 'gaining') {
    return `Over the last ${n} days, intake averaged ${absAvg} kcal/day above maintenance. At this pace, storage is filling by about +${projectedAmount} ${projectionUnit} per week.`;
  }
  if (reading.state === 'losing') {
    return `Over the last ${n} days, intake averaged ${absAvg} kcal/day below maintenance. At this pace, storage is draining by about -${projectedAmount} ${projectionUnit} per week.`;
  }
  return `Over the last ${n} days, intake stayed inside the +/-${threshold} kcal maintenance band. Storage is holding at this pace.`;
}

function todayConfidenceLine(reading) {
  const threshold = Math.round(reading.threshold);
  if (reading.assumed) {
    return `${reading.confidence} estimate · assumes missing days matched today · keeping band +/-${threshold} kcal`;
  }
  return `${reading.confidence} confidence · keeping band +/-${threshold} kcal`;
}

function ZoneRail() {
  const rows = [
    { label: 'gaining', top: 52,  color: 'var(--rust)' },
    { label: 'keeping', top: 215, color: 'var(--amber)' },
    { label: 'losing',  top: 378, color: 'var(--moss)' },
  ];
  return (
    <div style={{ position: 'absolute', top: 0, right: -120, height: '100%', width: 110 }}>
      {rows.map((r) => (
        <div key={r.label} style={{ position: 'absolute', top: r.top, left: 0, display: 'flex', alignItems: 'center', gap: 8 }}>
          <span style={{ width: 22, height: 1, background: r.color, opacity: 0.7 }} />
          <span style={{ fontFamily: "'Instrument Serif', serif", fontStyle: 'italic', fontSize: 17, color: 'var(--ink)' }}>{r.label}</span>
        </div>
      ))}
    </div>
  );
}

function WebStat({ label, value, unit, toneColor }) {
  return (
    <div style={{ padding: '2px 6px', textAlign: 'center' }}>
      <div className="mono" style={{ fontSize: 10, color: 'var(--ink-3)', letterSpacing: '0.08em', textTransform: 'uppercase' }}>{label}</div>
      <div className="mono num-tab" style={{ fontSize: 24, fontWeight: 500, color: toneColor || 'var(--ink)', marginTop: 4, lineHeight: 1 }}>{value}</div>
      <div className="mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 3 }}>{unit}</div>
    </div>
  );
}

function VDivW() {
  return <div style={{ width: 1, height: 40, background: 'var(--hair)', justifySelf: 'center' }} />;
}

function SectionHead({ title, right }) {
  return (
    <div style={{ display: 'flex', alignItems: 'baseline', gap: 10 }}>
      <span className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', letterSpacing: '0.10em', textTransform: 'uppercase' }}>{title}</span>
      <span style={{ flex: 1, height: 1, background: 'var(--hair)' }} />
      {right && <span className="mono num-tab" style={{ fontSize: 11.5, color: 'var(--ink-3)', letterSpacing: '0.04em' }}>{right}</span>}
    </div>
  );
}

function SectionEyebrow({ left, right }) {
  return (
    <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 4 }}>
      <span className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', letterSpacing: '0.10em', textTransform: 'uppercase' }}>{left}</span>
      <span style={{ flex: 1, height: 1, background: 'var(--hair)' }} />
      {right && <span className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', letterSpacing: '0.06em' }}>{right}</span>}
    </div>
  );
}

function primaryBtn(extra = {}) {
  return {
    marginTop: 10, width: '100%',
    background: 'var(--ink)', color: 'var(--paper-2)',
    border: 'none', borderRadius: 12, padding: '12px 16px',
    display: 'flex', alignItems: 'center', cursor: 'pointer',
    fontFamily: "'Geist', system-ui, sans-serif",
    boxShadow: '0 1px 0 var(--btn-shine) inset, 0 6px 16px var(--shadow-cta)',
    ...extra,
  };
}

function todayEditorButtonStyle(secondary = false) {
  return {
    border: secondary ? '1px solid var(--hair)' : 'none',
    background: secondary ? 'var(--paper-2)' : 'var(--ink)',
    color: secondary ? 'var(--ink)' : 'var(--paper-2)',
    borderRadius: 10,
    padding: '11px 13px',
    fontFamily: "'Geist', system-ui, sans-serif",
    fontSize: 13,
    fontWeight: 600,
    cursor: 'pointer',
  };
}

function kbdStyle() {
  return {
    fontFamily: "'JetBrains Mono', monospace", fontSize: 10, letterSpacing: '0.04em',
    color: 'var(--btn-kbd-fg)', border: '1px solid var(--btn-kbd-bd)',
    borderRadius: 4, padding: '2px 6px',
  };
}

Object.assign(window, {
  WebShell, WebToday, todayStatusBody, todayConfidenceLine, ZoneRail, WebStat, VDivW, SectionHead, SectionEyebrow, primaryBtn, todayEditorButtonStyle, kbdStyle,
});
