// ─────────────────────────────────────────────────────────────────────────────
// HonestSlim — Weight Loss Monte Carlo Simulator
// ─────────────────────────────────────────────────────────────────────────────

const { useState, useCallback, useEffect } = React;
const {
  ComposedChart, Area, Line, XAxis, YAxis, CartesianGrid,
  Tooltip, ResponsiveContainer, ReferenceLine
} = Recharts;


// ── Theme definitions ─────────────────────────────────────────────────────────

const THEMES = {
  light: {
    bg:               "#ffffff",
    panelBg:          "#f8fafc",
    cardBg:           "#ffffff",
    chartBg:          "#f8fafc",
    border:           "#e2e8f0",
    textPrimary:      "#0f172a",
    textSecondary:    "#475569",
    textMuted:        "#94a3b8",
    inputBg:          "#f1f5f9",
    inputBorder:      "#e2e8f0",
    inputColor:       "#059669",
    primary:          "#059669",
    primaryDark:      "#065f46",
    badgeBg:          "#d1fae5",
    badgeText:        "#059669",
    activeBg:         "#d1fae5",
    activeText:       "#059669",
    activeBorder:     "#059669",
    inactiveBorder:   "#e2e8f0",
    inactiveText:     "#94a3b8",
    buttonBg:         "linear-gradient(135deg,#065f46,#059669)",
    buttonGlow:       "#05966933",
    buttonDisBg:      "#f1f5f9",
    buttonDisTx:      "#94a3b8",
    sliderTrack:      "#e2e8f0",
    sliderFill:       "linear-gradient(90deg,#065f46,#059669)",
    fanLine10:        "#3b82f6",
    fanLine25:        "#10b981",
    fanLine50:        "#059669",
    fanLine75:        "#f59e0b",
    fanLine90:        "#ef4444",
    samplePath:       "rgba(100,116,139,0.28)",
    sampleHighlight:  "rgba(71,85,105,0.75)",
    gridLine:         "#e2e8f0",
    axisStroke:       "#e2e8f0",
    axisTick:         "#94a3b8",
    tooltipBg:        "#ffffff",
    tooltipBorder:    "#e2e8f0",
    insightBg:        "#f8fafc",
    insightBorder:    "#e2e8f0",
    insightAccent:    "#059669",
    insightStrong:    "#0f172a",
    insightBody:      "#475569",
    insightHint:      "#94a3b8",
    disclaimer:       "#94a3b8",
    errorText:        "#dc2626",
    warningText:      "#b45309",
    warningBg:        "#fffbeb",
    toggleBg:         "#f1f5f9",
    toggleBorder:     "#e2e8f0",
    toggleText:       "#475569",
    hintText:         "#94a3b8",
    labelText:        "#475569",
    sectionLabel:     "#94a3b8",
  },
  dark: {
    bg:               "#0F172A",
    panelBg:          "#1E293B",
    cardBg:           "#1E293B",
    chartBg:          "#0F172A",
    border:           "#334155",
    textPrimary:      "#f1f5f9",
    textSecondary:    "#94a3b8",
    textMuted:        "#94a3b8",
    inputBg:          "#0F172A",
    inputBorder:      "#334155",
    inputColor:       "#38BDF8",
    primary:          "#38BDF8",
    primaryDark:      "#0369a1",
    badgeBg:          "#0c4a6e",
    badgeText:        "#38BDF8",
    activeBg:         "#0c4a6e",
    activeText:       "#38BDF8",
    activeBorder:     "#38BDF8",
    inactiveBorder:   "#334155",
    inactiveText:     "#475569",
    buttonBg:         "linear-gradient(135deg,#0369a1,#38BDF8)",
    buttonGlow:       "#38BDF833",
    buttonDisBg:      "#1e293b",
    buttonDisTx:      "#475569",
    sliderTrack:      "#334155",
    sliderFill:       "linear-gradient(90deg,#0369a1,#38BDF8)",
    fanLine10:        "#60a5fa",
    fanLine25:        "#34d399",
    fanLine50:        "#38bdf8",
    fanLine75:        "#fbbf24",
    fanLine90:        "#f87171",
    samplePath:       "rgba(148,163,184,0.22)",
    sampleHighlight:  "rgba(203,213,225,0.65)",
    gridLine:         "#1e293b",
    axisStroke:       "#334155",
    axisTick:         "#475569",
    tooltipBg:        "#1E293B",
    tooltipBorder:    "#334155",
    insightBg:        "#0F172A",
    insightBorder:    "#334155",
    insightAccent:    "#38BDF8",
    insightStrong:    "#f1f5f9",
    insightBody:      "#94a3b8",
    insightHint:      "#475569",
    disclaimer:       "#475569",
    errorText:        "#f87171",
    warningText:      "#fbbf24",
    warningBg:        "rgba(251,191,36,0.08)",
    toggleBg:         "#1E293B",
    toggleBorder:     "#334155",
    toggleText:       "#94a3b8",
    hintText:         "#94a3b8",
    labelText:        "#94a3b8",
    sectionLabel:     "#475569",
  }
};


// ── Monte Carlo Engine ────────────────────────────────────────────────────────

function mifflinBMR(weight, height, age, sex) {
  const base = 10 * weight + 6.25 * height - 5 * age;
  return sex === "male" ? base + 5 : base - 161;
}

function sampleNormal(mean, sd) {
  let u = 0, v = 0;
  while (u === 0) u = Math.random();
  while (v === 0) v = Math.random();
  return mean + sd * Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
}

function runSimulation(params) {
  const { startWeight, goalWeight, dailyDeficit, adherence, sex, height, age } = params;
  const SIMS      = 5000;
  const MAX_WEEKS = 104;
  const allPaths  = [];

  for (let s = 0; s < SIMS; s++) {
    let weight     = startWeight;
    const path     = [weight];
    const startBMR = mifflinBMR(startWeight, height, age, sex);

    for (let w = 1; w <= MAX_WEEKS; w++) {
      if (weight <= goalWeight) { path.push(goalWeight); continue; }

      const actualAdherence = Math.min(1, Math.max(0.05,
        sampleNormal(adherence / 100, 0.08)
      ));
      const weeklyDeficit    = dailyDeficit * 7 * actualAdherence;
      const rawLoss          = weeklyDeficit / 7700;
      const noise            = sampleNormal(0, 0.45);
      const currentBMR       = mifflinBMR(weight, height, age, sex);
      const adaptationFactor = 0.75 + 0.25 * (currentBMR / startBMR);

      weight = Math.max(goalWeight, weight - rawLoss * adaptationFactor + noise);
      path.push(parseFloat(weight.toFixed(2)));
    }
    allPaths.push(path);
  }

  const chartData = [];

  for (let w = 0; w <= MAX_WEEKS; w++) {
    const vals = allPaths
      .map(p => p[Math.min(w, p.length - 1)])
      .sort((a, b) => a - b);

    const p = (pct) => vals[Math.floor(pct * (vals.length - 1))];

    chartData.push({
      week: w,
      p10: parseFloat(p(0.1).toFixed(2)),
      p25: parseFloat(p(0.25).toFixed(2)),
      p50: parseFloat(p(0.5).toFixed(2)),
      p75: parseFloat(p(0.75).toFixed(2)),
      p90: parseFloat(p(0.9).toFixed(2)),
    });

    // Stop once the slowest cohort arrives — each band descends naturally
    if (w > 0 && p(0.9) <= goalWeight + 0.05) break;
  }

  const keys     = ["p10","p25","p50","p75","p90"];
  const findWeek = (ki) => {
    for (let w = 0; w < chartData.length; w++) {
      if (chartData[w][keys[ki]] <= goalWeight + 0.1) return w;
    }
    return MAX_WEEKS;
  };

  const sampleIndices = [];
  while (sampleIndices.length < 30) {
    const idx = Math.floor(Math.random() * SIMS);
    if (!sampleIndices.includes(idx)) sampleIndices.push(idx);
  }

  sampleIndices.forEach((simIdx, i) => {
    const path = allPaths[simIdx];
    chartData.forEach((point, w) => {
      point[`s${i}`] = path[Math.min(w, path.length - 1)];
    });
  });

  const medianFinal  = chartData[chartData.length - 1].p50;
  const highlightIdx = sampleIndices.reduce((best, simIdx, i) => {
    const fw   = allPaths[simIdx][Math.min(chartData.length - 1, allPaths[simIdx].length - 1)];
    const prev = allPaths[sampleIndices[best]][Math.min(chartData.length - 1, allPaths[sampleIndices[best]].length - 1)];
    return Math.abs(fw - medianFinal) < Math.abs(prev - medianFinal) ? i : best;
  }, 0);

  return {
    chartData,
    sampleCount: sampleIndices.length,
    highlightIdx,
    weeks: {
      best:   findWeek(0),
      good:   findWeek(1),
      median: findWeek(2),
      slow:   findWeek(3),
      worst:  findWeek(4),
    }
  };
}


// ── Healthy Weight Range ──────────────────────────────────────────────────────

function healthyWeightRange(height, sex) {
  const h      = height / 100;
  const low    = parseFloat((18.5 * h * h).toFixed(1));
  const high   = parseFloat((24.9 * h * h).toFixed(1));
  const ideal  = parseFloat(((sex === "male" ? 23 : 21.5) * h * h).toFixed(1));
  return { low, high, ideal };
}


// ── Unit conversion ───────────────────────────────────────────────────────────

const kgToLbs  = kg  => Math.round(kg  * 2.20462);
const lbsToKg  = lbs => Math.round(lbs / 2.20462 * 10) / 10;
const cmToIn   = cm  => Math.round(cm  / 2.54);
const inToCm   = in_ => Math.round(in_ * 2.54);
const inToFtIn = in_ => `${Math.floor(in_ / 12)}'${in_ % 12}"`;


// ── Helpers ───────────────────────────────────────────────────────────────────

function weeksToDate(weeks) {
  const d = new Date();
  d.setDate(d.getDate() + weeks * 7);
  return d.toLocaleDateString("en-GB", { day: "numeric", month: "short", year: "numeric" });
}


// ── Custom Tooltip ────────────────────────────────────────────────────────────

function CustomTooltip({ active, payload, label, theme: t, isImperial }) {
  if (!active || !payload || !payload.length || !t) return null;

  const rows = [
    ["p90", t.fanLine90, "Slow (90%)"],
    ["p75", t.fanLine75, "Below avg (75%)"],
    ["p50", t.fanLine50, "Most likely (50%)"],
    ["p25", t.fanLine25, "Above avg (25%)"],
    ["p10", t.fanLine10, "Best case (10%)"],
  ];

  const fmt = v => isImperial ? `${kgToLbs(v)} lbs` : `${v} kg`;

  return (
    <div style={{
      background: t.tooltipBg, border: `1px solid ${t.tooltipBorder}`,
      borderRadius: 8, padding: "10px 14px", fontSize: 13,
      fontFamily: "'DM Sans', sans-serif",
      boxShadow: "0 4px 16px rgba(0,0,0,0.12)"
    }}>
      <p style={{ color: t.textMuted, margin: "0 0 6px" }}>Week {label}</p>
      {rows.map(([key, color, lbl]) => {
        const entry = payload.find(p => p.dataKey === key);
        if (!entry) return null;
        return (
          <p key={key} style={{ color, margin: "2px 0" }}>
            {lbl}: <strong>{fmt(entry.value)}</strong>
          </p>
        );
      })}
    </div>
  );
}


// ── Slider ────────────────────────────────────────────────────────────────────

function Slider({ label, value, min, max, step = 1, unit = "", onChange, hint, warning, theme: t }) {
  const [inputVal, setInputVal] = useState(String(value));

  useEffect(() => { setInputVal(String(value)); }, [value]);

  const handleInputChange = (e) => {
    setInputVal(e.target.value);
    const n = Number(e.target.value);
    if (!isNaN(n) && n >= min && n <= max) onChange(n);
  };

  const handleBlur = () => {
    const n = Number(inputVal);
    if (isNaN(n)) {
      setInputVal(String(value));
    } else {
      const clamped = Math.max(min, Math.min(max, n));
      onChange(clamped);
      setInputVal(String(clamped));
    }
  };

  const pct       = ((value - min) / (max - min)) * 100;
  const unitLabel = unit.trim();

  return (
    <div style={{ marginBottom: 20 }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 6 }}>
        <span style={{ fontSize: 13, color: t.labelText, fontFamily: "'DM Sans', sans-serif" }}>
          {label}
        </span>
        <div style={{ display: "flex", alignItems: "center", gap: 4 }}>
          <input
            type="number"
            value={inputVal}
            min={min} max={max} step={step}
            onChange={handleInputChange}
            onBlur={handleBlur}
            style={{
              width: 62, padding: "3px 6px", borderRadius: 6,
              border: `1px solid ${t.inputBorder}`,
              background: t.inputBg,
              color: t.inputColor,
              fontSize: 13, fontWeight: 600,
              fontFamily: "monospace", textAlign: "right", outline: "none",
              WebkitAppearance: "none", MozAppearance: "textfield"
            }}
          />
          {unitLabel && (
            <span style={{ fontSize: 12, color: t.textMuted, minWidth: 20 }}>{unitLabel}</span>
          )}
        </div>
      </div>

      <div style={{ position: "relative", height: 6, background: t.sliderTrack, borderRadius: 3 }}>
        <div style={{
          position: "absolute", left: 0, width: `${pct}%`,
          height: "100%", background: t.sliderFill,
          borderRadius: 3, pointerEvents: "none"
        }} />
        <input
          type="range" min={min} max={max} step={step} value={value}
          onChange={e => onChange(Number(e.target.value))}
          style={{
            position: "absolute", top: -5, left: 0,
            width: "100%", height: 16,
            opacity: 0, cursor: "pointer", zIndex: 2
          }}
        />
      </div>

      {hint && (
        <p style={{ fontSize: 13, color: t.hintText, margin: "4px 0 0", fontFamily: "'DM Sans', sans-serif" }}>
          {hint}
        </p>
      )}
      {warning && (
        <div style={{
          marginTop: 8, padding: "8px 10px", borderRadius: 6,
          background: t.warningBg, border: `1px solid ${t.warningText}44`,
          fontSize: 12, color: t.warningText, lineHeight: 1.6,
          fontFamily: "'DM Sans', sans-serif"
        }}>
          ⚠ {warning}
        </div>
      )}
    </div>
  );
}


// ── Stat Card ─────────────────────────────────────────────────────────────────

function StatCard({ label, weeks, sublabel, theme: t, dim }) {
  const color = dim ? t.primary + "99" : t.primary;
  return (
    <div style={{
      background: t.cardBg,
      border: `1px solid ${t.primary}${dim ? "22" : "40"}`,
      borderRadius: 10, padding: "12px 14px", flex: 1, minWidth: 0
    }}>
      <div style={{
        fontSize: 11, color: t.textMuted, marginBottom: 4,
        fontFamily: "'DM Sans', sans-serif",
        textTransform: "uppercase", letterSpacing: "0.05em"
      }}>{label}</div>
      <div style={{ fontSize: 22, fontWeight: 700, color, fontFamily: "monospace", marginBottom: 2 }}>
        Wk {weeks}
      </div>
      <div style={{ fontSize: 13, color: t.textSecondary, fontFamily: "'DM Sans', sans-serif" }}>
        {weeksToDate(weeks)}
      </div>
      <div style={{ fontSize: 12, color: t.textMuted, marginTop: 3, fontFamily: "'DM Sans', sans-serif" }}>
        {sublabel}
      </div>
    </div>
  );
}


// ── Main App ──────────────────────────────────────────────────────────────────

function App() {
  const [isDark,      setIsDark]      = useState(false);
  const [isImperial,  setIsImperial]  = useState(false);
  const t = THEMES[isDark ? "dark" : "light"];

  const [params, setParams] = useState({
    startWeight:  90,
    goalWeight:   70,
    dailyDeficit: 500,
    adherence:    80,
    sex:          "female",
    height:       168,
    age:          35,
  });

  const [result,      setResult]      = useState(null);
  const [running,     setRunning]     = useState(false);
  const [copiedShare, setCopiedShare] = useState(false);
  const [sharingImage, setSharingImage] = useState(false);
  const [savedImage,   setSavedImage]   = useState(false);
  const resultsRef  = React.useRef(null);
  const shareRef    = React.useRef(null);
  const userRanOnce = React.useRef(false);

  const set = (key) => (val) => setParams(prev => ({ ...prev, [key]: val }));

  const setStartWeight = (val) => {
    const h      = healthyWeightRange(params.height, params.sex);
    const newGoal = Math.max(30, Math.min(Math.round(h.high), val - 1));
    setParams(prev => ({ ...prev, startWeight: val, goalWeight: newGoal }));
  };

  const run = useCallback(() => {
    if (params.goalWeight >= params.startWeight) return;
    setRunning(true);
    setTimeout(() => {
      setResult(runSimulation(params));
      setRunning(false);
      if (userRanOnce.current && window.innerWidth < 768 && resultsRef.current) {
        resultsRef.current.scrollIntoView({ behavior: "smooth", block: "start" });
      }
    }, 10);
  }, [params]);

  useEffect(() => { run(); }, []);

  const handleShareImage = async () => {
    if (!result || !shareRef.current || sharingImage) return;
    setSharingImage(true);
    try {
      const canvas = await html2canvas(shareRef.current, {
        scale: 2,
        backgroundColor: t.bg,
        logging: false,
        useCORS: true,
      });
      canvas.toBlob(async (blob) => {
        const file = new File([blob], 'honestslim-results.png', { type: 'image/png' });
        if (navigator.canShare?.({ files: [file] })) {
          await navigator.share({
            files: [file],
            text: 'My realistic weight loss timeline:',
            url: 'https://honestslim.com',
          });
        } else {
          const url = URL.createObjectURL(blob);
          const a = document.createElement('a');
          a.href = url;
          a.download = 'honestslim-results.png';
          document.body.appendChild(a);
          a.click();
          document.body.removeChild(a);
          URL.revokeObjectURL(url);
          setSavedImage(true);
          setTimeout(() => setSavedImage(false), 2500);
        }
        setSharingImage(false);
      }, 'image/png');
    } catch (e) {
      setSharingImage(false);
    }
  };

  const handleShare = () => {
    if (!result) return;
    const lossLine = isImperial
      ? `${kgToLbs(params.startWeight) - kgToLbs(params.goalWeight)} lbs`
      : `${totalLoss} kg`;
    const text = [
      `I ran HonestSlim's Monte Carlo weight loss simulator.`,
      `Goal: lose ${lossLine} · ${params.dailyDeficit} kcal/day · ${params.adherence}% adherence`,
      ``,
      `Best case (10%):    week ${result.weeks.best} · ${weeksToDate(result.weeks.best)}`,
      `Most likely (50%):  week ${result.weeks.median} · ${weeksToDate(result.weeks.median)}`,
      `Realistic (80%):    week ${result.weeks.slow} · ${weeksToDate(result.weeks.slow)}`,
      ``,
      `Try it free: https://honestslim.com`,
    ].join('\n');
    navigator.clipboard.writeText(text).then(() => {
      setCopiedShare(true);
      setTimeout(() => setCopiedShare(false), 2000);
    });
  };

  const totalLoss          = params.startWeight - params.goalWeight;
  const healthy            = healthyWeightRange(params.height, params.sex);
  const expectedWeeklyLoss = parseFloat((params.dailyDeficit * 7 / 7700).toFixed(2));
  const goalBMI            = parseFloat((params.goalWeight / Math.pow(params.height / 100, 2)).toFixed(1));

  // Imperial display values — params always stored in kg/cm
  const dispStartWeight = isImperial ? kgToLbs(params.startWeight)  : params.startWeight;
  const dispGoalWeight  = isImperial ? kgToLbs(params.goalWeight)   : params.goalWeight;
  const dispHeight      = isImperial ? cmToIn(params.height)        : params.height;
  const wUnit           = isImperial ? " lbs" : " kg";
  const hUnit           = isImperial ? " in"  : " cm";
  const wMin            = isImperial ? 88  : 40;
  const wMax            = isImperial ? 551 : 250;
  const hMin            = isImperial ? 55  : 140;
  const hMax            = isImperial ? 87  : 220;

  const goalHint = isImperial
    ? <React.Fragment>Healthy range for {dispHeight} in ({inToFtIn(dispHeight)}) ({params.sex}):<br/>{kgToLbs(healthy.low)}–{kgToLbs(healthy.high)} lbs · Ideal ≈ {kgToLbs(healthy.ideal)} lbs</React.Fragment>
    : <React.Fragment>Healthy range for {params.height} cm ({params.sex}):<br/>{healthy.low}–{healthy.high} kg · Ideal ≈ {healthy.ideal} kg</React.Fragment>;

  const rateWarning = expectedWeeklyLoss > 1.1
    ? `At this deficit you could lose up to ${isImperial ? (expectedWeeklyLoss * 2.20462).toFixed(1) + ' lbs/week' : expectedWeeklyLoss.toFixed(1) + ' kg/week'} at full adherence — above the generally recommended limit of ${isImperial ? '2.2–2.4 lbs' : '1.0–1.1 kg'}/week. Rapid weight loss increases the risk of muscle loss, nutritional deficiencies, and gallstones. Only attempt an aggressive deficit under medical supervision.`
    : null;
  const underweightWarning = params.goalWeight < healthy.low
    ? `This goal weight gives a BMI of ${goalBMI} — below the healthy threshold of 18.5. A BMI under 18.5 is clinically underweight and is associated with bone density loss, hormonal disruption, and immune suppression. Please consult a doctor before setting this as your target.`
    : null;
  const deficitHint = isImperial
    ? "500 kcal/day ≈ ~1.1 lbs/week under ideal conditions"
    : "500 kcal/day ≈ ~0.5 kg/week under ideal conditions";
  const totalLossDisplay = isImperial ? `${kgToLbs(totalLoss)} lbs` : `${totalLoss} kg`;

  return (
    <div style={{
      background: t.bg, minHeight: "100vh",
      color: t.textPrimary, fontFamily: "'DM Sans', sans-serif",
      padding: "0 0 60px"
    }}>
      <style>{`
        body { background: ${t.bg}; }
        input[type=number]::-webkit-inner-spin-button,
        input[type=number]::-webkit-outer-spin-button { opacity: 0.4; }
        @media (max-width: 767px) {
          .hs-layout       { flex-direction: column !important; }
          .hs-sidebar      { width: 100% !important; border-right: none !important; border-bottom: 1px solid ${t.border} !important; }
          .hs-badge        { display: none !important; }
          input[type=range] { height: 44px !important; top: -19px !important; }
          .hs-run-btn      { padding-top: 15px !important; padding-bottom: 15px !important; }
          .hs-sex-toggle button { padding-top: 12px !important; padding-bottom: 12px !important; font-size: 15px !important; }
          .hs-sidebar span             { font-size: 15px !important; }
          .hs-sidebar p                { font-size: 14px !important; }
          .hs-sidebar input[type=number] { font-size: 15px !important; width: 70px !important; }
          .hs-mode-toggle  { padding-top: 10px !important; padding-bottom: 10px !important; }
          .hs-seo          { padding-top: 32px !important; }
        }
      `}</style>

      <link
        href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap"
        rel="stylesheet"
      />

      {/* ── Header ── */}
      <div style={{ borderBottom: `1px solid ${t.border}`, padding: "16px 24px 14px" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <div style={{
            width: 8, height: 8, borderRadius: "50%",
            background: t.primary,
            boxShadow: `0 0 8px ${t.primary}`
          }} />
          <span style={{ fontSize: 20, fontWeight: 700, letterSpacing: "-0.02em", color: t.textPrimary }}>
            HonestSlim
          </span>
          <span style={{
            fontSize: 11, background: t.badgeBg, color: t.badgeText,
            padding: "2px 8px", borderRadius: 20
          }} className="hs-badge">Monte Carlo Weight Loss Simulator</span>

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

          {/* Mode toggle */}
          <button
            onClick={() => setIsDark(d => !d)}
            className="hs-mode-toggle"
            style={{
              padding: "5px 14px", borderRadius: 20, cursor: "pointer",
              border: `1px solid ${t.toggleBorder}`,
              background: t.toggleBg,
              color: t.toggleText,
              fontSize: 12, fontFamily: "'DM Sans', sans-serif",
              fontWeight: 500, transition: "all 0.2s",
              display: "flex", alignItems: "center", gap: 6
            }}
          >
            <span style={{
              display: "inline-block", width: 10, height: 10, borderRadius: "50%",
              background: isDark ? "#38BDF8" : "#059669",
              boxShadow: isDark ? "0 0 6px #38BDF8" : "0 0 6px #059669"
            }} />
            {isDark ? "Light mode" : "Dark mode"}
          </button>
        </div>
        <p style={{ fontSize: 14, color: t.textMuted, margin: "6px 0 0" }}>
          Honest weight loss forecasting — a range of realistic outcomes, not a fake straight line.
        </p>
      </div>

      {/* ── Two-column layout ── */}
      <div style={{ display: "flex", flexWrap: "wrap" }} className="hs-layout">

        {/* ── Left: inputs ── */}
        <div className="hs-sidebar" style={{
          width: 284, flexShrink: 0,
          padding: "22px 20px",
          borderRight: `1px solid ${t.border}`,
          background: t.panelBg
        }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 18 }}>
            <p style={{ fontSize: 11, color: t.sectionLabel, textTransform: "uppercase", letterSpacing: "0.07em", margin: 0 }}>Your details</p>
            <div style={{ display: "flex", gap: 2 }}>
              {[["kg", false], ["lbs", true]].map(([lbl, imp]) => (
                <button key={lbl} onClick={() => setIsImperial(imp)} style={{
                  padding: "3px 9px", borderRadius: 4, cursor: "pointer",
                  border: `1px solid ${isImperial === imp ? t.activeBorder : t.inactiveBorder}`,
                  background: isImperial === imp ? t.activeBg : "transparent",
                  color: isImperial === imp ? t.activeText : t.inactiveText,
                  fontSize: 11, fontFamily: "'DM Sans', sans-serif", fontWeight: 500,
                  transition: "all 0.15s"
                }}>{lbl}</button>
              ))}
            </div>
          </div>

          {/* Sex toggle */}
          <div style={{ marginBottom: 20 }}>
            <p style={{ fontSize: 13, color: t.labelText, marginBottom: 8, fontFamily: "'DM Sans', sans-serif" }}>Sex</p>
            <div style={{ display: "flex", gap: 8 }} className="hs-sex-toggle">
              {["female", "male"].map(s => (
                <button
                  key={s}
                  onClick={() => set("sex")(s)}
                  style={{
                    flex: 1, padding: "8px 0", borderRadius: 6, cursor: "pointer",
                    border: `1px solid ${params.sex === s ? t.activeBorder : t.inactiveBorder}`,
                    background: params.sex === s ? t.activeBg : "transparent",
                    color: params.sex === s ? t.activeText : t.inactiveText,
                    fontSize: 13, fontFamily: "'DM Sans', sans-serif",
                    fontWeight: params.sex === s ? 600 : 400,
                    transition: "all 0.15s"
                  }}
                >
                  {s.charAt(0).toUpperCase() + s.slice(1)}
                </button>
              ))}
            </div>
          </div>

          <Slider label="Age"            value={params.age}     min={18}  max={80}   unit=" yrs"  onChange={set("age")}   theme={t} />
          <Slider label="Height"
            value={dispHeight} min={hMin} max={hMax} unit={hUnit}
            hint={isImperial ? `${inToFtIn(dispHeight)}` : undefined}
            onChange={v => set("height")(isImperial ? inToCm(v) : v)}
            theme={t}
          />
          <Slider label="Current weight"
            value={dispStartWeight} min={wMin} max={wMax} unit={wUnit}
            onChange={v => setStartWeight(isImperial ? lbsToKg(v) : v)}
            theme={t}
          />
          <Slider label="Goal weight"
            value={dispGoalWeight}
            min={wMin}
            max={isImperial ? kgToLbs(params.startWeight) - 1 : params.startWeight - 1}
            unit={wUnit}
            onChange={v => set("goalWeight")(isImperial ? lbsToKg(v) : v)}
            theme={t} hint={goalHint} warning={underweightWarning}
          />
          <Slider label="Daily deficit"  value={params.dailyDeficit} min={100} max={1500} step={50} unit=" kcal" onChange={set("dailyDeficit")} theme={t} hint={deficitHint} warning={rateWarning} />
          <Slider label="Adherence"      value={params.adherence}    min={40}  max={100}             unit="%"     onChange={set("adherence")}    theme={t} hint="How consistently you'll hit your target each week" />

          {/* Run button */}
          <button
            onClick={() => { userRanOnce.current = true; run(); }}
            disabled={running || params.goalWeight >= params.startWeight}
            className="hs-run-btn"
            style={{
              width: "100%", padding: "12px 0", borderRadius: 8, border: "none",
              background: running ? t.buttonDisBg : t.buttonBg,
              color: running ? t.buttonDisTx : "#ffffff",
              fontSize: 14, fontWeight: 600,
              cursor: running ? "not-allowed" : "pointer",
              fontFamily: "'DM Sans', sans-serif",
              boxShadow: running ? "none" : `0 0 24px ${t.buttonGlow}`,
              transition: "all 0.2s"
            }}
          >
            {running ? "Simulating…" : "▶  Run 5,000 Simulations"}
          </button>

          {params.goalWeight >= params.startWeight && (
            <p style={{ fontSize: 11, color: t.errorText, marginTop: 8, textAlign: "center" }}>
              Goal weight must be less than current weight
            </p>
          )}
        </div>

        {/* ── Right: chart + results ── */}
        <div ref={resultsRef} style={{ flex: 1, minWidth: 0, padding: "22px 20px", background: t.bg }}>

          {result ? (
            <>
              {/* Shareable content — captured by html2canvas */}
              <div ref={shareRef} style={{ background: t.bg, paddingBottom: 2 }}>

              {/* Stat cards */}
              <div style={{ display: "flex", gap: 8, marginBottom: 20, flexWrap: "wrap" }}>
                <StatCard label="Best case (10%)"  weeks={result.weeks.best}   sublabel="Top 10% of outcomes"      theme={t} />
                <StatCard label="Most likely (50%)" weeks={result.weeks.median} sublabel="Median — half reach here" theme={t} />
                <StatCard label="Realistic (80%)"  weeks={result.weeks.slow}   sublabel="8 in 10 people reach goal" theme={t} dim />
              </div>

              {/* Fan chart */}
              <div style={{
                background: t.chartBg, borderRadius: 12,
                border: `1px solid ${t.border}`, padding: "16px 8px 8px"
              }}>
                <p style={{
                  fontSize: 11, color: t.sectionLabel,
                  textTransform: "uppercase", letterSpacing: "0.07em",
                  margin: "0 0 12px 12px"
                }}>
                  Weight over time — 5,000 simulations
                  <span style={{ color: t.textMuted, marginLeft: 8, textTransform: "none", letterSpacing: 0 }}>
                    (faint lines = individual journeys)
                  </span>
                </p>

                <ResponsiveContainer width="100%" height={280}>
                  <ComposedChart
                    data={result.chartData}
                    margin={{ top: 4, right: 16, bottom: 28, left: -8 }}
                  >
                    <CartesianGrid strokeDasharray="3 3" stroke={t.gridLine} vertical={false} />
                    <XAxis
                      dataKey="week"
                      stroke={t.axisStroke}
                      tick={{ fill: t.axisTick, fontSize: 13 }}
                      label={{ value: "Weeks from today", position: "insideBottom", offset: -16, fill: t.axisTick, fontSize: 13 }}
                    />
                    <YAxis
                      stroke={t.axisStroke}
                      tick={{ fill: t.axisTick, fontSize: 13 }}
                      domain={["auto", "auto"]}
                      tickFormatter={v => isImperial ? `${kgToLbs(v)}lbs` : `${v}kg`}
                    />
                    <Tooltip content={<CustomTooltip theme={t} isImperial={isImperial} />} />
                    <ReferenceLine
                      y={params.goalWeight}
                      stroke={t.primary} strokeDasharray="5 4" strokeOpacity={0.55}
                      label={{ value: isImperial ? `Goal ${kgToLbs(params.goalWeight)}lbs` : `Goal ${params.goalWeight}kg`, fill: t.primary, fontSize: 11, position: "insideTopRight" }}
                    />

                    {/* Individual simulation paths — drawn first, sit beneath percentile lines */}
                    {Array.from({ length: result.sampleCount }, (_, i) => (
                      <Line
                        key={`s${i}`}
                        dataKey={`s${i}`}
                        dot={false} activeDot={false} isAnimationActive={false}
                        stroke={i === result.highlightIdx ? t.sampleHighlight : t.samplePath}
                        strokeWidth={i === result.highlightIdx ? 1.5 : 0.7}
                        strokeDasharray={i === result.highlightIdx ? "4 3" : undefined}
                        legendType="none"
                      />
                    ))}

                    {/* Percentile lines — distinct colours, rendered on top */}
                    <Line type="monotone" dataKey="p90" dot={false} activeDot={false} isAnimationActive={false} stroke={t.fanLine90} strokeWidth={1.5} legendType="none" />
                    <Line type="monotone" dataKey="p75" dot={false} activeDot={false} isAnimationActive={false} stroke={t.fanLine75} strokeWidth={1.5} legendType="none" />
                    <Line type="monotone" dataKey="p50" dot={false} activeDot={false} isAnimationActive={false} stroke={t.fanLine50} strokeWidth={2.5} legendType="none" />
                    <Line type="monotone" dataKey="p25" dot={false} activeDot={false} isAnimationActive={false} stroke={t.fanLine25} strokeWidth={1.5} legendType="none" />
                    <Line type="monotone" dataKey="p10" dot={false} activeDot={false} isAnimationActive={false} stroke={t.fanLine10} strokeWidth={1.5} strokeDasharray="4 3" legendType="none" />
                  </ComposedChart>
                </ResponsiveContainer>
              </div>

              {/* Legend */}
              <div style={{ display: "flex", gap: 16, marginTop: 10, flexWrap: "wrap", padding: "0 4px" }}>
                {[
                  [t.fanLine10, "Best 10%"],
                  [t.fanLine25, "Above avg 25%"],
                  [t.fanLine50, "Median"],
                  [t.fanLine75, "Below avg 75%"],
                  [t.fanLine90, "Slow 90%"],
                ].map(([color, lbl]) => (
                  <div key={lbl} style={{ display: "flex", alignItems: "center", gap: 5 }}>
                    <div style={{ width: 20, height: 2.5, background: color, borderRadius: 2 }} />
                    <span style={{ fontSize: 13, color: t.textMuted }}>{lbl}</span>
                  </div>
                ))}
              </div>

              {/* Insight box */}
              <div style={{
                marginTop: 16, background: t.insightBg,
                border: `1px solid ${t.insightBorder}`, borderRadius: 10, padding: "14px 16px"
              }}>
                <p style={{ fontSize: 13, color: t.insightBody, margin: 0, lineHeight: 1.8 }}>
                  <span style={{ color: t.insightAccent, fontWeight: 600 }}>What this means: </span>
                  Losing <strong style={{ color: t.insightStrong }}>{totalLossDisplay}</strong> at a{" "}
                  <strong style={{ color: t.insightStrong }}>{params.dailyDeficit} kcal/day</strong> deficit
                  with <strong style={{ color: t.insightStrong }}>{params.adherence}% adherence</strong> —
                  half of simulated people reach their goal by{" "}
                  <strong style={{ color: t.insightStrong }}>week {result.weeks.median}</strong>, and
                  80% get there by{" "}
                  <strong style={{ color: t.insightStrong }}>week {result.weeks.slow}</strong>.{" "}
                  <span style={{ color: t.insightHint }}>
                    Try lowering adherence to 60% to see how much uncertainty grows.
                  </span>
                </p>
              </div>

              </div>{/* end shareRef */}

              {/* Share buttons */}
              <div style={{ marginTop: 16, display: "flex", justifyContent: "center", gap: 10, flexWrap: "wrap" }}>
                <button
                  onClick={handleShareImage}
                  disabled={sharingImage}
                  style={{
                    padding: "8px 20px", borderRadius: 8,
                    cursor: sharingImage ? "not-allowed" : "pointer",
                    border: `1px solid ${savedImage ? t.primary : t.border}`,
                    background: savedImage ? t.activeBg : "transparent",
                    color: savedImage ? t.activeText : sharingImage ? t.textMuted : t.textSecondary,
                    fontSize: 13, fontFamily: "'DM Sans', sans-serif", fontWeight: 500,
                    transition: "all 0.2s",
                  }}
                >
                  {sharingImage ? "Generating…" : savedImage ? "Saved!" : "Share chart"}
                </button>
                <button
                  onClick={handleShare}
                  style={{
                    padding: "8px 20px", borderRadius: 8, cursor: "pointer",
                    border: `1px solid ${copiedShare ? t.primary : t.border}`,
                    background: copiedShare ? t.activeBg : "transparent",
                    color: copiedShare ? t.activeText : t.textMuted,
                    fontSize: 13, fontFamily: "'DM Sans', sans-serif", fontWeight: 500,
                    transition: "all 0.2s",
                  }}
                >
                  {copiedShare ? "Copied!" : "Copy text"}
                </button>
              </div>

              <div style={{ marginTop: 10, display: "flex", justifyContent: "center" }}>
                <a
                  href="https://buymeacoffee.com/Wolfy82"
                  target="_blank"
                  rel="noopener noreferrer"
                  style={{
                    fontSize: 12, color: t.textMuted, textDecoration: "none",
                    padding: "5px 12px", borderRadius: 6, border: `1px solid ${t.border}`,
                    fontFamily: "'DM Sans', sans-serif", display: "flex", alignItems: "center", gap: 5,
                  }}
                >☕ Buy me a coffee</a>
              </div>

              <p style={{ fontSize: 13, color: t.disclaimer, marginTop: 10, textAlign: "center", lineHeight: 1.6 }}>
                Statistical estimates only. Not medical advice.
                Consult a healthcare professional before starting any weight loss programme.
              </p>
            </>
          ) : (
            <div style={{
              height: 300, display: "flex",
              alignItems: "center", justifyContent: "center",
              color: t.textMuted, fontSize: 14
            }}>
              {params.goalWeight >= params.startWeight
                ? "Goal weight must be less than current weight"
                : "Click Run to simulate"}
            </div>
          )}
        </div>
      </div>

      {/* ── SEO content ── */}
      <div className="hs-seo" style={{ maxWidth: 760, margin: "0 auto", padding: "56px 24px 0" }}>

        <h1 style={{
          fontSize: 26, fontWeight: 700, color: t.textPrimary,
          letterSpacing: "-0.02em", marginBottom: 10, lineHeight: 1.3
        }}>
          Realistic Weight Loss Timeline Calculator
        </h1>
        <p style={{ fontSize: 15, color: t.textSecondary, marginBottom: 44, lineHeight: 1.75 }}>
          Most weight loss calculators give you one date and pretend it's certain. HonestSlim shows the honest spread — a fan of 5,000 simulated journeys so you can see best case, most likely, and realistic worst case all at once.
        </p>

        {/* How it works */}
        <h2 style={{
          fontSize: 19, fontWeight: 700, color: t.textPrimary,
          marginBottom: 18, letterSpacing: "-0.01em"
        }}>How it works</h2>

        <p style={{ fontSize: 15, color: t.textSecondary, lineHeight: 1.85, marginBottom: 18 }}>
          Every weight loss calculator you've ever used gives you one date: <em>"At a 500 kcal/day deficit you'll reach your goal in 14 weeks."</em> That date is almost always wrong — not because the maths is bad, but because it assumes perfect consistency every day, a metabolism that never adapts, and a scale that moves in a straight line. None of those things are true.
        </p>

        <p style={{ fontSize: 15, color: t.textSecondary, lineHeight: 1.85, marginBottom: 18 }}>
          HonestSlim uses a <strong style={{ color: t.textPrimary }}>Monte Carlo simulation</strong> — the same technique used by financial analysts and climate modellers — to treat weight loss as the noisy, variable process it actually is. It runs 5,000 independent simulated journeys, each with its own random week-to-week adherence swings, water retention noise, and metabolic slowdown. The result is a fan chart: a spread of outcomes from best case to realistic worst case.
        </p>

        <p style={{ fontSize: 15, color: t.textSecondary, lineHeight: 1.85, marginBottom: 18 }}>
          The <strong style={{ color: t.textPrimary }}>fan chart</strong> shows five percentile lines. The median (50%) is your most likely timeline — half of all simulated journeys reach goal faster, half take longer. The best-case (10%) line shows how fast things can go with a consistent run. The slow (90%) line is the realistic outer limit: only 1 in 10 journeys takes longer than this. The 30 faint individual lines cutting through the fan are actual simulated journeys — you can see real plateaus, rebounds and zigzags, not a theoretical smooth curve.
        </p>

        <p style={{ fontSize: 15, color: t.textSecondary, lineHeight: 1.85, marginBottom: 18 }}>
          The <strong style={{ color: t.textPrimary }}>Adherence slider</strong> is the most important input on the page. Set it to 95% and the fan stays narrow — you'll be close to your plan nearly every week. Drop it to 60% and watch the fan widen dramatically. No other weight loss calculator asks this question. It's also the most honest one: most people aren't perfectly consistent, and this slider lets you model that upfront rather than discovering it six months later.
        </p>

        <p style={{ fontSize: 15, color: t.textSecondary, lineHeight: 1.85, marginBottom: 48 }}>
          The calorie model uses the <strong style={{ color: t.textPrimary }}>Mifflin-St Jeor equation</strong> — the most widely validated BMR formula in clinical use — recalculated every single week as your weight changes. This is what causes the fan to visibly flatten and slow in the second half: metabolic adaptation is real, and the model captures it.{" "}
          <span style={{ fontSize: 13, color: t.textMuted }}>
            Source: Mifflin MD, St Jeor ST, et al. (1990). "A new predictive equation for resting energy expenditure in healthy individuals." <em>American Journal of Clinical Nutrition.</em>
          </span>
        </p>

        {/* FAQ */}
        <h2 style={{
          fontSize: 19, fontWeight: 700, color: t.textPrimary,
          marginBottom: 28, letterSpacing: "-0.01em"
        }}>Frequently asked questions</h2>

        {[
          {
            q: "How long will it realistically take me to lose weight?",
            a: "It depends on your starting weight, goal, calorie deficit, and how consistently you follow your plan. HonestSlim's honest answer is always a range, not a single date — because real weight loss is variable. The median (50%) line on the fan chart is your most likely timeline: half of all simulated journeys finish faster, half take longer. For a typical scenario of 10 kg to lose at a 500 kcal/day deficit with 80% adherence, most simulated outcomes land between 16 and 28 weeks. Anyone giving you a single exact date is ignoring the randomness that's built into every real diet."
          },
          {
            q: "Why does this calculator show a range instead of a single date?",
            a: "Because weight loss is not deterministic. Your adherence varies week to week. Water retention goes up and down by 1–2 kg depending on sodium, hydration, hormones, and gut contents. Your metabolism slows as you lose weight. A single-date calculator ignores all of this — it treats you as a machine. HonestSlim runs 5,000 simulated journeys and shows you the actual probability distribution. The width of the fan is information, not noise: a wide fan means your result is genuinely uncertain given your inputs. A narrow fan means your plan is robust and your timeline is predictable."
          },
          {
            q: "What does the fan chart actually show?",
            a: "The fan chart shows five percentile lines from 5,000 simulated weight loss journeys. The best-case (10%) line shows the trajectory of the fastest 10% of outcomes. The slow (90%) line is the realistic outer limit — only 1 in 10 simulated journeys takes longer than this. The median (50%) line divides the distribution in half. The 30 faint lines running through the fan are individual simulated journeys, not statistical abstractions — each one has its own random plateaus and rebounds. The highlighted dashed line is the single journey whose final weight came closest to the median: a representative 'typical' path."
          },
          {
            q: "Why does lowering adherence change my results so much?",
            a: "Adherence variation compounds over time. One missed week barely matters. But consistent partial adherence — hitting 70% of your plan instead of 90% — meaningfully reduces your average weekly deficit and dramatically widens the variance of outcomes. At 60% adherence, some simulated weeks produce almost no progress while others are good, creating long plateaus separated by bursts of progress. This matches what most real dieters actually experience. The adherence slider lets you be honest with yourself before you start, not six months in when you're wondering why the original date came and went."
          },
          {
            q: "How accurate is this weight loss probability calculator?",
            a: "It is a statistical model, not a medical prediction. Its accuracy depends on how closely your real-world behaviour matches your inputs. The Mifflin-St Jeor equation has roughly ±10% individual variance — real metabolisms differ. The model does not account for activity level (fold this into your deficit estimate), muscle gain from resistance training, medical conditions, or medications. Use the fan chart to understand the realistic range of outcomes and to see how adherence affects your timeline — not to predict an exact week. For personalised guidance, consult a registered dietitian or your doctor."
          }
        ].map(({ q, a }) => (
          <div key={q} style={{
            marginBottom: 0, padding: "20px 0",
            borderTop: `1px solid ${t.border}`
          }}>
            <h3 style={{
              fontSize: 15, fontWeight: 600, color: t.textPrimary,
              marginBottom: 10, lineHeight: 1.45
            }}>{q}</h3>
            <p style={{ fontSize: 15, color: t.textSecondary, lineHeight: 1.85, margin: 0 }}>{a}</p>
          </div>
        ))}

        <div style={{ height: 1, background: t.border, marginTop: 0 }} />

      </div>

      {/* ── Footer ── */}
      <div style={{
        maxWidth: 760, margin: "0 auto", padding: "24px 24px 40px",
        display: "flex", justifyContent: "space-between", alignItems: "center",
        flexWrap: "wrap", gap: 12
      }}>
        <span style={{ fontSize: 13, color: t.textMuted }}>© {new Date().getFullYear()} HonestSlim - All rights reserved</span>
        <div style={{ display: "flex", gap: 20 }}>
          {[
            { label: "About", href: "about" },
            { label: "How It Works", href: "algorithm" },
            { label: "Privacy Policy", href: "privacy" },
          ].map(({ label, href }) => (
            <a key={href} href={href} style={{
              fontSize: 13, color: t.textMuted, textDecoration: "none"
            }}
              onMouseOver={e => e.target.style.color = t.primary}
              onMouseOut={e => e.target.style.color = t.textMuted}
            >{label}</a>
          ))}
        </div>
      </div>

    </div>
  );
}

// ── Mount ─────────────────────────────────────────────────────────────────────
ReactDOM.render(<App />, document.getElementById("root"));
