// Shared components for TitratePlan

const DOW_NAMES = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const MONTH_NAMES = ["January","February","March","April","May","June","July","August","September","October","November","December"];
const SITE_ZONES = ["Abdomen L", "Thigh R", "Upper arm L", "Abdomen R", "Thigh L", "Upper arm R"];
const DEFAULT_SITES = [...SITE_ZONES];
window.SITE_ZONES = SITE_ZONES;
window.DEFAULT_SITES = DEFAULT_SITES;

function fmtDose(mg) {
  return mg < 1 ? `${mg} mg` : `${mg} mg`;
}
function fmtDate(d, opts = {}) {
  return d.toLocaleDateString(undefined, { month: "short", day: "numeric", year: opts.year ? "numeric" : undefined });
}
function isoDate(d) {
  return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,"0")}-${String(d.getDate()).padStart(2,"0")}`;
}
function sameDay(a, b) {
  return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
}

// ----- Topbar -----
function Topbar({ theme, setTheme }) {
  return (
    <header className="topbar">
      <div className="brand">
        <span className="brand-mark"></span>
        <span>TitratePlan</span>
        <span className="badge-beta">Beta</span>
      </div>
      <nav>
        <a href="#how">How it works</a>
        <a href="#providers">Find a provider</a>
        <a href="#faq">FAQ</a>
        <button
          className="theme-toggle"
          onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
          aria-label="Toggle theme"
          title="Toggle theme">
          {theme === "dark" ? "☀" : "☾"}
        </button>
      </nav>
    </header>
  );
}

// ----- AdSlot -----
function AdSlot({ size = "300x250", label }) {
  const cls = size === "300x250" ? "ad-300x250" : size === "leaderboard" ? "ad-leaderboard" : size === "skyscraper" ? "ad-skyscraper" : "";
  return (
    <div className={`ad-slot ${cls}`} aria-label="Ad placeholder">
      <div style={{ marginTop: 14 }}>{label || `${size} ad unit`}</div>
      <div style={{ fontSize: 9, color: "var(--muted-2)", marginTop: 6 }}>Programmatic / Direct</div>
    </div>
  );
}

// ----- Affiliate panel -----
function AffiliatePanel() {
  const providers = [
    { initials: "Rx", name: "ClearPath Health", meta: "Online GLP-1 evaluation • from $0", url: "#" },
    { initials: "Hk", name: "Hypoke Telehealth", meta: "Compounded options • board-certified", url: "#" },
    { initials: "Wm", name: "Westmark Clinic", meta: "Insurance-friendly • RN support", url: "#" },
  ];
  return (
    <section className="affiliate" id="providers">
      <div className="affiliate-head">
        <h4>Telehealth providers</h4>
        <span className="tag">Sponsored</span>
      </div>
      {providers.map(p => (
        <a key={p.name} className="provider-row" href={p.url} rel="sponsored nofollow">
          <div className="provider-logo">{p.initials}</div>
          <div className="provider-info">
            <div className="name">{p.name}</div>
            <div className="meta">{p.meta}</div>
          </div>
          <span className="arrow">→</span>
        </a>
      ))}
      <div style={{ padding: "10px 18px", fontSize: 11, color: "var(--muted)", borderTop: "1px solid var(--line)" }}>
        Listings are paid placements. We may earn a commission on sign-ups.
      </div>
    </section>
  );
}

// ----- Custom plan acknowledgment modal -----
function CustomConfirmModal({ onCancel, onAccept }) {
  return (
    <div className="modal-scrim" onClick={onCancel}>
      <div className="modal-card" onClick={e => e.stopPropagation()}>
        <div className="label-mono" style={{ color: "var(--accent-ink)", marginBottom: 10 }}>Heads up</div>
        <h3 style={{ fontFamily: "var(--serif)", fontWeight: 400, fontSize: 24, margin: "0 0 14px" }}>
          You're about to use a non-standard schedule
        </h3>
        <p style={{ color: "var(--ink-2)", lineHeight: 1.5, margin: "0 0 12px" }}>
          This allows you to alter the plan in a manner that is no longer in accordance with the manufacturer's guidelines — whether by changing how long you stay at a step or by changing the dose itself.
        </p>
        <p style={{ color: "var(--ink-2)", lineHeight: 1.5, margin: "0 0 20px" }}>
          Any changes to your actual schedule or dose should always be discussed with and decided by your prescriber. TitratePlan does not provide medical advice — it simply renders the calendar you tell it to.
        </p>
        <div className="row" style={{ gap: 10, justifyContent: "flex-end" }}>
          <button className="btn btn-ghost" onClick={onCancel}>Cancel</button>
          <button className="btn btn-primary" onClick={onAccept}>I understand — continue</button>
        </div>
      </div>
    </div>
  );
}

// ----- Setup form -----
function SetupForm({ value, onChange, onSubmit, onReset, hasActivePlan, onRequestCustomize }) {
  const v = value;
  const set = (patch) => onChange({ ...v, ...patch });
  const meds = Object.values(window.GLP1_SCHEDULES);
  const sched = window.GLP1_SCHEDULES[v.scheduleId];
  const baseSteps = sched ? sched.steps : [];
  const overrides = v.customStepOverrides || baseSteps.map(s => ({ weeks: s.weeks, doseMg: s.doseMg }));
  const isCustomized = !!v.customStepOverrides;

  const requestEdit = (idx, patch) => {
    const next = overrides.map((o, i) => i === idx ? { ...o, ...patch } : o);
    onRequestCustomize && onRequestCustomize(next);
  };
  const requestAddStep = () => {
    const last = overrides[overrides.length - 1] || { weeks: 4, doseMg: 0.25 };
    const next = [...overrides, { weeks: 4, doseMg: last.doseMg }];
    onRequestCustomize && onRequestCustomize(next);
  };
  const requestRemoveStep = (idx) => {
    if (overrides.length <= 1) return;
    const next = overrides.filter((_, i) => i !== idx);
    onRequestCustomize && onRequestCustomize(next);
  };

  return (
    <div className="card card-pad">
      <div className="step-rail">
        <div className="step-pill"><div className="fill" style={{ transform: v.scheduleId ? "scaleX(1)" : "scaleX(0.2)" }} /></div>
        <div className="step-pill"><div className="fill" style={{ transform: v.startDate ? "scaleX(1)" : "scaleX(0)" }} /></div>
        <div className="step-pill"><div className="fill" style={{ transform: v.startDate ? "scaleX(1)" : "scaleX(0)" }} /></div>
      </div>

      <h3 style={{ marginBottom: 6 }}>Build your schedule</h3>
      <p className="muted small" style={{ marginBottom: 18 }}>Three steps. Editable anytime.</p>

      <div className="stack-md">
        <div>
          <div className="label-mono" style={{ marginBottom: 8 }}>1 · Medication & plan</div>
          <div className="med-grid">
            {meds.map(m => (
              <button
                key={m.id}
                type="button"
                className={`med-tile ${v.scheduleId === m.id ? "selected" : ""}`}
                onClick={() => set({ scheduleId: m.id })}>
                <div className="gen">{m.generic}</div>
                <div className="ind">{m.indication}</div>
                <div className="br">{m.brandRef}</div>
              </button>
            ))}
          </div>
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "1fr", gap: 12 }}>
          <div className="field">
            <label>2 · First injection date</label>
            <input
              type="date"
              className="input"
              value={v.startDate}
              onChange={e => set({ startDate: e.target.value })}
            />
            <span className="small muted" style={{ marginTop: 4 }}>
              {v.startDate ? `Doses recur every ${DOW_NAMES[(new Date(v.startDate + "T00:00:00")).getDay()]}day.` : "Pick a date — the day of the week becomes your weekly cadence."}
            </span>
          </div>
        </div>

        <div className="field">
          <label>Maintenance weeks to schedule</label>
          <select className="select" value={v.maintenanceWeeks} onChange={e => set({ maintenanceWeeks: parseInt(e.target.value, 10) })}>
            <option value={0}>None — titration only</option>
            <option value={12}>12 weeks (3 months)</option>
            <option value={26}>26 weeks (6 months)</option>
            <option value={52}>52 weeks (1 year)</option>
            <option value={104}>104 weeks (2 years)</option>
          </select>
          <p className="small muted" style={{ margin: "6px 0 0" }}>
            Your full titration ladder is always scheduled. This sets how many additional weeks of your maintenance dose to include in the calendar.
          </p>
        </div>

        <label className="check">
          <input type="checkbox" checked={v.includeReminders} onChange={e => set({ includeReminders: e.target.checked })} />
          Include 1-hour calendar reminders before each dose
        </label>

        <hr className="card-divider" style={{ margin: "4px 0" }} />
        <div>
          <label className="check" style={{ marginBottom: 8 }}>
            <input type="checkbox" checked={v.rotateSites} onChange={e => set({ rotateSites: e.target.checked })} />
            Rotate injection sites with my schedule
          </label>
          <p className="small muted" style={{ margin: "0 0 10px 24px" }}>
            Cycles through selected zones to help avoid lipohypertrophy. Each dose is tagged with a site in the calendar and .ics export.
          </p>
          {v.rotateSites && (() => {
            const enabled = v.enabledSites && v.enabledSites.length > 0 ? v.enabledSites : DEFAULT_SITES;
            const startSite = enabled.includes(v.startSite) ? v.startSite : enabled[0];
            const toggleSite = (z) => {
              const isOn = enabled.includes(z);
              if (isOn) {
                if (enabled.length <= 1) return; // never disable all
                const next = enabled.filter(x => x !== z);
                set({ enabledSites: next, startSite: next.includes(startSite) ? startSite : next[0] });
              } else {
                set({ enabledSites: [...enabled, z].sort((a, b) => SITE_ZONES.indexOf(a) - SITE_ZONES.indexOf(b)) });
              }
            };
            return (
              <div style={{ marginLeft: 24 }}>
                <div className="row-between" style={{ marginBottom: 6 }}>
                  <div className="label-mono">Sites in rotation · tap to start, × to exclude</div>
                </div>
                <div className="body-map">
                  {SITE_ZONES.map((z) => {
                    const isEnabled = enabled.includes(z);
                    const isStart = isEnabled && z === startSite;
                    return (
                      <div
                        key={z}
                        className={`body-zone ${isStart ? "next" : ""} ${!isEnabled ? "disabled" : ""}`}
                        onClick={() => isEnabled && set({ startSite: z })}
                        role="button"
                        tabIndex={isEnabled ? 0 : -1}>
                        <button
                          type="button"
                          className="body-zone-toggle"
                          onClick={(e) => { e.stopPropagation(); toggleSite(z); }}
                          aria-label={isEnabled ? `Exclude ${z}` : `Include ${z}`}
                          title={isEnabled ? `Exclude ${z}` : `Include ${z}`}>
                          {isEnabled ? "×" : "+"}
                        </button>
                        <span className="lab">{isStart ? "Start" : isEnabled ? "Included" : "Excluded"}</span>
                        {z}
                      </div>
                    );
                  })}
                </div>
                <p className="small muted" style={{ marginTop: 6 }}>
                  Rotation alternates between abdomen, thigh, and upper arm — the three zones listed in the prescribing information for semaglutide and tirzepatide.
                </p>
              </div>
            );
          })()}
        </div>

        <details className="customize-block">
          <summary className="customize-summary">
            <span className="customize-summary-label">Non-standard customization</span>
            <span className="customize-summary-status">
              {isCustomized ? "Customized — your prescriber's plan" : "Optional"}
            </span>
          </summary>
          <p className="small muted" style={{ margin: "10px 0 8px" }}>
            By default, TitratePlan uses the manufacturer's standard cadence and pen-strength doses. If your prescriber is using a different schedule — holding longer at a step, or compounded medication at non-standard doses — enter their schedule below.
          </p>
          <p className="small muted" style={{ margin: "0 0 12px", fontStyle: "italic" }}>
            Branded pens (Ozempic, Wegovy, Mounjaro, Zepbound) ship at fixed strengths; non-standard mg values typically apply to compounded medication only.
          </p>
          <div className="custom-step-grid">
            <div className="custom-step-row custom-step-head">
              <div className="cs-label-head">Step</div>
              <div className="cs-dose-head">Dose (mg)</div>
              <div className="cs-weeks-head">Weeks</div>
              <div></div>
            </div>
            {overrides.map((o, i) => {
              const base = baseSteps[i];
              const baseDose = base ? base.doseMg : null;
              const baseWeeks = base ? base.weeks : null;
              const weeksChanged = baseWeeks !== null && baseWeeks !== o.weeks;
              const doseChanged = baseDose !== null && baseDose !== o.doseMg;
              const isExtra = !base;
              return (
                <div key={i} className="custom-step-row">
                  <div className="cs-label">
                    <span className="cs-step-label">{i === overrides.length - 1 ? "Maintenance" : `Step ${i + 1}`}</span>
                    <span className="small muted">
                      {isExtra ? "added step" : `default ${baseDose} mg · ${baseWeeks} wk`}
                    </span>
                  </div>
                  <div className="cs-dose">
                    <input
                      type="number"
                      min="0.05"
                      step="0.05"
                      className={`input cs-input ${doseChanged || isExtra ? "is-modified" : ""}`}
                      value={o.doseMg}
                      onChange={e => {
                        const n = parseFloat(e.target.value);
                        if (Number.isFinite(n) && n > 0) requestEdit(i, { doseMg: n });
                      }}
                    />
                  </div>
                  <div className="cs-weeks">
                    <input
                      type="number"
                      min="1"
                      max="52"
                      className={`input cs-input ${weeksChanged || isExtra ? "is-modified" : ""}`}
                      value={o.weeks}
                      onChange={e => {
                        const n = parseInt(e.target.value, 10);
                        if (Number.isFinite(n) && n > 0) requestEdit(i, { weeks: n });
                      }}
                    />
                  </div>
                  <button
                    type="button"
                    className="cs-remove"
                    title="Remove this step"
                    disabled={overrides.length <= 1}
                    onClick={() => requestRemoveStep(i)}>
                    ×
                  </button>
                </div>
              );
            })}
          </div>
          <div className="row" style={{ gap: 10, marginTop: 10 }}>
            <button type="button" className="btn btn-ghost small" onClick={requestAddStep}>
              + Add step
            </button>
            {isCustomized && (
              <button
                type="button"
                className="btn btn-ghost small"
                onClick={() => set({ customStepOverrides: null })}>
                Reset to manufacturer defaults
              </button>
            )}
          </div>
        </details>

        <hr className="card-divider card-divider-dashed" style={{ margin: "8px 0" }} />
        <div className="row-between" style={{ gap: 12, flexWrap: "wrap" }}>
          <button className="btn btn-primary" onClick={onSubmit} disabled={!v.scheduleId || !v.startDate}>
            Generate schedule →
          </button>
          <button
            type="button"
            className="btn btn-ghost"
            onClick={onReset}
            disabled={!hasActivePlan}
            title={hasActivePlan ? "Clear the active plan and start over" : "No active plan to clear"}>
            ↺ Start over
          </button>
        </div>

        <p className="small muted" style={{ marginTop: 4 }}>
          Educational tool. <strong style={{ color: "var(--ink-2)" }}>Always confirm dose with your prescriber.</strong>
        </p>
      </div>
    </div>
  );
}

// ----- Titration ladder visualization -----
function TitrationLadder({ schedule, events, currentEvent }) {
  if (!schedule) return null;
  const steps = schedule.steps;
  const totalWeeks = steps.reduce((sum, s) => sum + Math.min(s.weeks, 16), 0);
  const maxDose = Math.max(...steps.map(s => s.doseMg));
  const minDose = Math.min(...steps.map(s => s.doseMg));
  // Floor sized to comfortably hold "Month N" + a dose label (matches the original 0.25 mg bar height).
  const FLOOR_PCT = 16;
  const TOP_PCT = 100;
  // When step count is high, give the chart a minimum horizontal width so labels don't crush together.
  const minChartWidth = steps.length > 7 ? steps.length * 88 : 0;

  let weekCursor = 0;
  return (
    <div className="ladder">
      <div className="row-between" style={{ marginBottom: 16, gap: 24 }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div className="label-mono">Titration ladder</div>
          <h3 style={{ fontFamily: "var(--serif)", fontSize: 22, fontWeight: 400, marginTop: 4, lineHeight: 1.2 }}>
            {schedule.generic} · {schedule.indication}
          </h3>
        </div>
      </div>

      <div className={`ladder-scroll ${minChartWidth ? "is-wide" : ""}`}>
      <div className="ladder-grid" style={minChartWidth ? { minWidth: `${minChartWidth}px` } : null}>
        {steps.map((step, idx) => {
          const stepWeeks = Math.min(step.weeks, 16);
          const left = (weekCursor / totalWeeks) * 100;
          const width = (stepWeeks / totalWeeks) * 100;
          // Anchor smallest dose at the readable floor; scale larger doses proportionally above it.
          const span = maxDose - minDose;
          const norm = span > 0 ? (step.doseMg - minDose) / span : 0;
          const heightPct = FLOOR_PCT + norm * (TOP_PCT - FLOOR_PCT);
          weekCursor += stepWeeks;
          const isCurrent = currentEvent && currentEvent.stepIdx === idx;
          return (
            <div
              key={idx}
              className={`ladder-step ${step.isMaintenance ? "maint" : ""} ${isCurrent ? "current" : ""} ${idx === steps.length - 1 ? "is-last" : ""}`}
              style={{
                left: `${left}%`,
                width: `${width}%`,
                height: `${heightPct}%`,
              }}>
              <div className="step-cap"></div>
              <div className="step-label">{step.label}</div>
              <div className="step-dose">{fmtDose(step.doseMg)}</div>
            </div>
          );
        })}

        {currentEvent && (() => {
          const eventsLen = events.length;
          const idx = events.indexOf(currentEvent);
          const pct = (idx / Math.max(eventsLen - 1, 1)) * Math.min(100, (events.length / (totalWeeks)) * 100);
          // Place the today flag at the current event's week position
          let weekPos = 0, c = 0;
          for (let i = 0; i < steps.length; i++) {
            if (i < currentEvent.stepIdx) c += Math.min(steps[i].weeks, 16);
            else break;
          }
          // figure out weeks-into-step
          const eventsBeforeStep = events.findIndex(e => e.stepIdx === currentEvent.stepIdx);
          const weekInStep = Math.max(0, idx - eventsBeforeStep);
          const totalPct = ((c + weekInStep + 0.5) / totalWeeks) * 100;
          return (
            <div className="ladder-today-line" style={{ left: `${totalPct}%` }}>
              <div className="ladder-today-flag">Today</div>
            </div>
          );
        })()}

        <div className="ladder-axis"></div>
        {(() => {
          // x-axis ticks for each step boundary
          let cursor = 0;
          const ticks = [];
          ticks.push({ left: 0, lab: "Wk 1" });
          for (let i = 0; i < steps.length; i++) {
            cursor += Math.min(steps[i].weeks, 16);
            if (cursor < totalWeeks) ticks.push({ left: (cursor / totalWeeks) * 100, lab: `Wk ${cursor + 1}` });
          }
          ticks.push({ left: 100, lab: `Wk ${totalWeeks}+` });
          return ticks.map((t, i) => (
            <div key={i} className="ladder-x-tick" style={{ left: `${t.left}%` }}>{t.lab}</div>
          ));
        })()}
      </div>
      </div>
    </div>
  );
}

// ----- Status strip -----
function StatusStrip({ schedule, events, todayEvent, nextEscalation, weeksIn }) {
  if (!schedule) return null;
  const dose = todayEvent ? fmtDose(todayEvent.doseMg) : "—";
  const stepLabel = todayEvent ? todayEvent.stepLabel : "Not started";
  return (
    <div className="status-strip">
      <div className="status-cell dose-today">
        <span className="label-mono">Current dose</span>
        <div className="v">{dose.replace(" mg", "")}<span className="unit">mg / week</span></div>
        <span className="small" style={{ color: "var(--accent-ink)" }}>{stepLabel}{todayEvent && todayEvent.isMaintenance ? " · Maintenance" : ""}</span>
      </div>
      <div className="status-cell">
        <span className="label-mono">Weeks in</span>
        <div className="v">{weeksIn}<span className="unit">/ {events.length}</span></div>
        <span className="small muted">of projected plan</span>
      </div>
      <div className="status-cell">
        <span className="label-mono">Next escalation</span>
        <div className="v">{nextEscalation ? fmtDose(nextEscalation.doseMg).replace(" mg", "") : "—"}{nextEscalation && <span className="unit">mg</span>}</div>
        <span className="small muted">{nextEscalation ? fmtDate(nextEscalation.date) : "Maintenance reached"}</span>
      </div>
      <div className="status-cell">
        <span className="label-mono">Total doses</span>
        <div className="v">{events.length}<span className="unit">planned</span></div>
        <span className="small muted">{schedule.cadence === "weekly" ? "Weekly cadence" : "Daily cadence"}</span>
      </div>
    </div>
  );
}

// ----- Calendar (month view) -----
function MonthCalendar({ events, anchorDate, setAnchorDate }) {
  const eventByISO = React.useMemo(() => {
    const m = {};
    events.forEach(e => { m[isoDate(e.date)] = e; });
    return m;
  }, [events]);

  const year = anchorDate.getFullYear();
  const month = anchorDate.getMonth();
  const firstOfMonth = new Date(year, month, 1);
  const startGrid = new Date(firstOfMonth);
  startGrid.setDate(startGrid.getDate() - startGrid.getDay());
  const cells = [];
  for (let i = 0; i < 42; i++) {
    const d = new Date(startGrid);
    d.setDate(startGrid.getDate() + i);
    cells.push(d);
  }
  const today = new Date();

  return (
    <div className="cal">
      <div className="cal-head">
        <h3>{MONTH_NAMES[month]} <span style={{ color: "var(--muted)", fontFamily: "var(--mono)", fontSize: 16 }}>{year}</span></h3>
        <div className="nav">
          <button onClick={() => setAnchorDate(new Date(year, month - 1, 1))} aria-label="Previous month">‹</button>
          <button onClick={() => setAnchorDate(new Date())} aria-label="Today" style={{ width: "auto", padding: "0 10px", fontFamily: "var(--mono)", fontSize: 11 }}>Today</button>
          <button onClick={() => setAnchorDate(new Date(year, month + 1, 1))} aria-label="Next month">›</button>
        </div>
      </div>
      <div className="cal-grid">
        {DOW_NAMES.map(d => (<div key={d} className="cal-dow">{d}</div>))}
        {cells.map((d, i) => {
          const ev = eventByISO[isoDate(d)];
          const otherMonth = d.getMonth() !== month;
          const isToday = sameDay(d, today);
          return (
            <div key={i} className={`cal-cell ${otherMonth ? "other-month" : ""} ${isToday ? "today" : ""}`}>
              <div className="day-num">{d.getDate()}</div>
              {ev && (
                <div className={`cal-dose ${ev.isMaintenance ? "maint" : ""} ${ev.isFirstOfStep && ev.stepIdx > 0 ? "escalation" : ""}`}>
                  <span className="dose">{fmtDose(ev.doseMg).replace(" mg", " mg")}</span>
                  {ev.isFirstOfStep && ev.stepIdx > 0 && <span className="lab">↑ {ev.stepLabel}</span>}
                  {ev.site && !(ev.isFirstOfStep && ev.stepIdx > 0) && <span className="lab">{ev.site}</span>}
                </div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ----- Calendar export panel -----
function ExportPanel({ schedule, events, includeReminders, onChangeReminders }) {
  const handleExport = () => {
    const ics = window.buildICS({
      events,
      productName: schedule.generic,
      scheduleLabel: `${schedule.generic} (${schedule.indication})`,
      reminderMinutes: includeReminders ? 60 : 0,
    });
    const fname = `titrateplan-${schedule.id}-${isoDate(events[0].date)}.ics`;
    window.downloadICS(ics, fname);
  };
  return (
    <div className="card card-pad">
      <div className="row-between" style={{ marginBottom: 14 }}>
        <h3 style={{ fontFamily: "var(--serif)", fontSize: 20, fontWeight: 400 }}>Add to your calendar</h3>
      </div>
      <p className="small muted" style={{ marginBottom: 14 }}>
        Download a single .ics file with every projected dose. Imports into Apple Calendar, Google Calendar, Outlook, Fantastical, and most reminder apps.
      </p>
      <div className="cal-export-btns">
        <button className="cal-export-btn" onClick={handleExport}>
          <span className="ico">G</span>
          <span><div className="lab">Google Calendar</div><div className="sub">Import .ics manually</div></span>
        </button>
        <button className="cal-export-btn" onClick={handleExport}>
          <span className="ico">A</span>
          <span><div className="lab">Apple Calendar</div><div className="sub">Open with Calendar.app</div></span>
        </button>
        <button className="cal-export-btn" onClick={handleExport}>
          <span className="ico">O</span>
          <span><div className="lab">Outlook / Other</div><div className="sub">Universal .ics download</div></span>
        </button>
      </div>

      <hr className="card-divider" style={{ margin: "16px 0" }} />
      <label className="check">
        <input type="checkbox" checked={includeReminders} onChange={e => onChangeReminders(e.target.checked)} />
        Include 1-hour reminders
      </label>
    </div>
  );
}

// ----- Today / next-up card -----
function TodayCard({ todayEvent, nextEvent, schedule }) {
  if (!schedule) return null;
  return (
    <div className="card card-pad">
      <div className="label-mono" style={{ marginBottom: 6 }}>Today</div>
      {todayEvent ? (
        <>
          <div style={{ fontFamily: "var(--serif)", fontSize: 36, lineHeight: 1, letterSpacing: "-0.02em" }}>
            {fmtDose(todayEvent.doseMg)}
          </div>
          <div className="small muted" style={{ marginTop: 4 }}>{schedule.generic} · {todayEvent.stepLabel}</div>
          {todayEvent.site && (
            <div style={{ marginTop: 10, padding: "8px 10px", background: "var(--accent-soft)", borderRadius: "var(--radius)", display: "inline-flex", alignItems: "center", gap: 8 }}>
              <span className="label-mono" style={{ color: "var(--accent-ink)" }}>Site</span>
              <span style={{ fontFamily: "var(--mono)", fontSize: 13, color: "var(--accent-ink)" }}>{todayEvent.site}</span>
            </div>
          )}
          {todayEvent.isFirstOfStep && todayEvent.stepIdx > 0 && (
            <div className="alert" style={{ marginTop: 12 }}>
              <span className="dot"></span>
              <div><strong>Dose escalation today.</strong> First injection at this strength — watch for new side effects.</div>
            </div>
          )}
          {todayEvent.isFirstOfStep && todayEvent.stepIdx === 0 && (
            <div className="alert" style={{ marginTop: 12 }}>
              <span className="dot"></span>
              <div><strong>First injection.</strong> Starting dose — your body is just beginning to adjust.</div>
            </div>
          )}
        </>
      ) : (
        <>
          <div style={{ fontFamily: "var(--serif)", fontSize: 28, lineHeight: 1.1 }}>No injection today</div>
          {nextEvent && <div className="small muted" style={{ marginTop: 6 }}>Next: {fmtDate(nextEvent.date)} · {fmtDose(nextEvent.doseMg)}</div>}
        </>
      )}
    </div>
  );
}

// ----- Site rotation -----
function SiteRotation({ todayEvent, nextEvent, enabledSites }) {
  const zones = enabledSites && enabledSites.length ? enabledSites : SITE_ZONES;
  const nextSite = (nextEvent && nextEvent.site) || (todayEvent && todayEvent.site);
  const recentSite = todayEvent && todayEvent !== nextEvent ? todayEvent.site : null;
  return (
    <div className="card card-pad">
      <div className="label-mono" style={{ marginBottom: 10 }}>Injection site rotation</div>
      <div className="body-map">
        {zones.map((z) => (
          <div key={z} className={`body-zone ${z === nextSite ? "next" : z === recentSite ? "recent" : ""}`}>
            <span className="lab">{z === nextSite ? "Next" : z === recentSite ? "Last" : "Site"}</span>
            {z}
          </div>
        ))}
      </div>
      <p className="small muted" style={{ marginTop: 10 }}>Rotate sites to avoid lipohypertrophy. Stay 1+ inch from the navel.</p>
    </div>
  );
}

// ----- Dose log list (upcoming) -----
function DoseList({ events, schedule, limit }) {
  const today = new Date();
  const upcoming = events.filter(e => e.date >= new Date(today.getFullYear(), today.getMonth(), today.getDate())).slice(0, limit || 12);
  return (
    <div className="dose-list">
      {upcoming.map((ev, i) => {
        const prev = i > 0 ? upcoming[i - 1] : null;
        const isStepUpInList = ev.isFirstOfStep && ev.stepIdx > 0 && prev && prev.doseMg !== ev.doseMg;
        const isFirstDose = i === 0 && ev.weekNum === 1;
        const highlight = isStepUpInList || isFirstDose;
        return (
          <div key={i} className={`dose-row ${highlight ? "first-of-step" : ""} ${ev.isMaintenance ? "maint" : ""}`}>
            <div className="date">{fmtDate(ev.date)}</div>
            <div className="dose-mg">{fmtDose(ev.doseMg)}</div>
            <div className="step-name">
              Wk {ev.weekNum} · {ev.stepLabel}
              {isStepUpInList && <span className="row-tag row-tag-warn">↑ Step up</span>}
              {isFirstDose && <span className="row-tag row-tag-warn">● First dose</span>}
              {ev.isMaintenance && ev.isFirstOfStep && <span className="row-tag row-tag-maint">Maintenance</span>}
              {ev.site && <span className="pill pill-accent" style={{ marginLeft: 8 }}>◷ {ev.site}</span>}
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ----- Weekly horizontal bar chart (one bar per week) -----
function WeeklyBarChart({ schedule, events, currentEvent, isExample }) {
  if (!schedule || !events || !events.length) return null;
  const maxDose = Math.max(...events.map(e => e.doseMg));
  const currentIdx = currentEvent ? events.indexOf(currentEvent) : -1;
  const fmtRowDate = (d) => {
    const m = d.toLocaleDateString(undefined, { month: "short" });
    const day = d.getDate();
    const yy = String(d.getFullYear()).slice(-2);
    return `${m} ${day}, '${yy}`;
  };
  const rows = [];
  events.forEach((ev, i) => {
    if (ev.isFirstOfStep) rows.push({ kind: "header", ev, i });
    rows.push({ kind: "row", ev, i });
  });
  return (
    <div className={`wbar ${isExample ? "is-example" : ""}`}>
      <div className="row-between" style={{ marginBottom: 12, gap: 16 }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div className="label-mono">{isExample ? "Example · semaglutide 0.25 → 2.4 mg" : "Weekly view"}</div>
          <h3 style={{ fontFamily: "var(--serif)", fontSize: 18, fontWeight: 400, marginTop: 4, lineHeight: 1.2 }}>
            {isExample ? "What your plan will look like" : `${events.length} doses`}
          </h3>
        </div>
        {isExample && <span className="pill pill-muted">Example</span>}
      </div>
      <div className="wbar-scroll">
        {rows.map((r, k) => {
          if (r.kind === "header") {
            return (
              <div key={k} className="wbar-step-head">
                <div className="wbar-step-label">{r.ev.stepLabel}</div>
                {r.ev.note && <div className="wbar-step-note">{r.ev.note}</div>}
              </div>
            );
          }
          const ev = r.ev;
          const i = r.i;
          const widthPct = (ev.doseMg / maxDose) * 100;
          const isCurrent = i === currentIdx;
          const isMaint = !!ev.isMaintenance;
          return (
            <div key={k} className={`wbar-row ${isCurrent ? "is-current" : ""}`}>
              <div className="wbar-date">{fmtRowDate(ev.date)}</div>
              <div className="wbar-track">
                <div
                  className={`wbar-fill ${isMaint ? "maint" : ""} ${isCurrent ? "current" : ""}`}
                  style={{ width: `${Math.max(widthPct, 4)}%` }}
                />
              </div>
              <div className="wbar-dose">{fmtDose(ev.doseMg)}</div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

Object.assign(window, {
  Topbar, AdSlot, AffiliatePanel, SetupForm, TitrationLadder, WeeklyBarChart, CustomConfirmModal,
  StatusStrip, MonthCalendar, ExportPanel, TodayCard, SiteRotation, DoseList,
  fmtDose, fmtDate, isoDate, sameDay, DOW_NAMES, MONTH_NAMES,
});
