/* Office Sidekick — Tool components */

const { useState: useStateT, useEffect: useEffectT, useRef: useRefT, useMemo: useMemoT, useCallback: useCallbackT } = React;

/* ============================================================
   POMODORO — uses global state
   ============================================================ */
function PomodoroTool() {
  const app = window.Office.useApp();
  const { pomo, setPomo, startPomo, pausePomo, resetPomo, skipPomo } = app;
  const CIRC = 2 * Math.PI * 92;
  const pct = pomo.totalMs ? pomo.remainingMs / pomo.totalMs : 1;
  const dashOffset = CIRC * (1 - pct);

  const setMode = (mode) => {
    pausePomo();
    setPomo(p => {
      const dur = (mode === "focus" ? p.focus : mode === "short" ? p.short : p.long) * 60000;
      return { ...p, mode, totalMs: dur, remainingMs: dur };
    });
  };

  const setDuration = (key, val) => {
    setPomo(p => {
      const v = Math.max(1, +val || 1);
      const next = { ...p, [key]: v };
      if (!p.running) {
        const dur = (next.mode === "focus" ? next.focus : next.mode === "short" ? next.short : next.long) * 60000;
        next.totalMs = dur; next.remainingMs = dur;
      }
      return next;
    });
  };

  return (
    <div style={{ display: "grid", gridTemplateColumns: "1fr", gap: 18 }}>
      <style>{`
        @media (min-width: 920px) {
          .pomo-grid { grid-template-columns: 1fr 320px !important; }
        }
        .ring-bg { fill: none; stroke: rgba(255,255,255,0.08); stroke-width: 8; }
        [data-theme="light"] .ring-bg { stroke: rgba(11,16,32,0.10); }
        .ring-fg { fill: none; stroke: url(#ringGrad); stroke-width: 8; stroke-linecap: round;
          transition: stroke-dashoffset 0.4s linear;
          filter: drop-shadow(0 0 14px rgba(96,165,250,0.45));
        }
      `}</style>
      <div className="pomo-grid" style={{ display: "grid", gridTemplateColumns: "1fr", gap: 18 }}>
        <div className="card" style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: "32px 24px" }}>
          <div className="chips" style={{ marginBottom: 18 }}>
            {[["focus", "Focus"], ["short", "Short break"], ["long", "Long break"]].map(([k, label]) => (
              <button key={k} className={"chip " + (pomo.mode === k ? "active" : "")} onClick={() => setMode(k)}>{label}</button>
            ))}
          </div>
          <div style={{ position: "relative", width: 280, height: 280 }}>
            <svg viewBox="0 0 200 200" style={{ width: "100%", height: "100%", transform: "rotate(-90deg)" }}>
              <defs>
                <linearGradient id="ringGrad" x1="0" y1="0" x2="1" y2="1">
                  <stop offset="0%" stopColor="#60a5fa"/>
                  <stop offset="100%" stopColor="#fcd34d"/>
                </linearGradient>
              </defs>
              <circle cx="100" cy="100" r="92" className="ring-bg" />
              <circle cx="100" cy="100" r="92" className="ring-fg" strokeDasharray={CIRC.toFixed(2)} strokeDashoffset={dashOffset} />
            </svg>
            <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", fontFamily: "var(--font-mono)" }}>
              <span style={{ fontSize: 11, letterSpacing: "0.30em", textTransform: "uppercase", color: "var(--vps-text-muted)" }}>
                {pomo.mode === "focus" ? "Focus" : pomo.mode === "short" ? "Short Break" : "Long Break"}
              </span>
              <span style={{ fontSize: 56, fontWeight: 700, letterSpacing: "0.02em" }}>{window.Office.fmtMs(pomo.remainingMs)}</span>
              <span style={{ fontSize: 12, color: "var(--vps-blue-300)", marginTop: 6 }}>
                {pomo.mode === "focus" ? `Session ${pomo.sessionIdx} of ${pomo.cycles}` : "Break · then session " + Math.min(pomo.sessionIdx, pomo.cycles)}
              </span>
            </div>
          </div>
          <div style={{ display: "flex", gap: 6, marginTop: 14 }}>
            {Array.from({ length: pomo.cycles }).map((_, i) => {
              const idx = i + 1;
              const cls = idx < pomo.sessionIdx ? "done" : (idx === pomo.sessionIdx && pomo.mode === "focus" ? "active" : "");
              return <span key={i} style={{
                width: 10, height: 10, borderRadius: "50%",
                background: cls === "done" ? "var(--vps-amber-300)" : cls === "active" ? "var(--vps-blue-400)" : "var(--vps-border)",
                boxShadow: cls === "done" ? "0 0 8px var(--vps-amber-300)" : cls === "active" ? "0 0 8px var(--vps-blue-400)" : "none"
              }}></span>;
            })}
          </div>
          <div className="row" style={{ marginTop: 22, justifyContent: "center" }}>
            {!pomo.running ? (
              <button className="btn btn-primary" onClick={() => startPomo()}>▶ Start</button>
            ) : (
              <button className="btn btn-danger" onClick={pausePomo}>⏸ Pause</button>
            )}
            <button className="btn btn-ghost" onClick={resetPomo}>Reset</button>
            <button className="btn btn-ghost" onClick={skipPomo}>Skip →</button>
          </div>
        </div>

        <div className="card">
          <h3 style={{ margin: "0 0 14px", fontSize: 11, letterSpacing: "0.30em", textTransform: "uppercase", color: "var(--vps-blue-300)" }}>
            Durations
          </h3>
          {[["focus", "Focus block"], ["short", "Short break"], ["long", "Long break"]].map(([k, label]) => (
            <div className="field" key={k}>
              <label className="label">{label} (min)</label>
              <input className="input" type="number" min="1" value={pomo[k]} onChange={e => setDuration(k, e.target.value)} />
            </div>
          ))}
          <div className="field">
            <label className="label">Sessions before long break</label>
            <input className="input" type="number" min="2" max="8" value={pomo.cycles} onChange={e => setDuration("cycles", e.target.value)} />
          </div>
          <div className="field">
            <label className="label">Bell</label>
            <div className="chips">
              <button className={"chip " + (pomo.bell ? "active" : "")} onClick={() => setPomo(p => ({ ...p, bell: true }))}>On</button>
              <button className={"chip " + (!pomo.bell ? "active" : "")} onClick={() => setPomo(p => ({ ...p, bell: false }))}>Off</button>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   FOCUS MODE — full-screen distraction-free
   ============================================================ */
function FocusModeTool() {
  const app = window.Office.useApp();
  const { pomo, startPomo, pausePomo, resetPomo } = app;
  const pct = pomo.totalMs ? (1 - pomo.remainingMs / pomo.totalMs) * 100 : 0;

  return (
    <div className="card" style={{ padding: 0, overflow: "hidden", borderRadius: "var(--r-xl)", minHeight: 540, position: "relative" }}>
      <div style={{ position: "absolute", inset: 0,
        background: "radial-gradient(circle at 30% 20%, rgba(37,99,235,0.18), transparent 55%), radial-gradient(circle at 80% 80%, rgba(245,158,11,0.10), transparent 55%)",
        pointerEvents: "none"
      }}></div>

      <div style={{ position: "relative", padding: "60px 28px", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 22 }}>
        <div style={{ fontSize: 11, letterSpacing: "0.30em", textTransform: "uppercase", color: "var(--vps-amber-300)", display: "flex", alignItems: "center", gap: 8 }}>
          <span style={{ width: 6, height: 6, borderRadius: "50%", background: "var(--vps-amber-300)", boxShadow: "0 0 10px var(--vps-amber-300)" }}></span>
          <span>{pomo.running ? "In session · stay sharp" : "Ready when you are"}</span>
        </div>

        <div style={{
          fontFamily: "var(--font-mono)",
          fontSize: "clamp(96px, 14vw, 180px)",
          fontWeight: 700,
          letterSpacing: "-0.04em",
          lineHeight: 0.95,
          background: "linear-gradient(135deg, var(--vps-text-primary) 25%, var(--vps-blue-200) 70%, var(--vps-amber-200) 100%)",
          WebkitBackgroundClip: "text", backgroundClip: "text", color: "transparent",
        }}>
          {window.Office.fmtMs(pomo.remainingMs)}
        </div>

        <div style={{ width: "min(640px, 100%)", padding: "0 20px" }}>
          <div style={{ height: 6, borderRadius: 999, background: "rgba(255,255,255,0.06)", overflow: "hidden" }}>
            <div style={{ height: "100%", background: "linear-gradient(90deg, var(--vps-blue-400), var(--vps-amber-300))", borderRadius: 999, width: pct + "%", transition: "width 0.4s linear" }}></div>
          </div>
          <div className="mono" style={{ display: "flex", justifyContent: "space-between", marginTop: 8, fontSize: 12, color: "var(--vps-text-muted)" }}>
            <span>{pomo.mode === "focus" ? `Session ${pomo.sessionIdx} of ${pomo.cycles}` : pomo.mode === "short" ? "Short break" : "Long break"}</span>
            <span>{Math.round(pct)}%</span>
          </div>
        </div>

        <div className="row" style={{ marginTop: 14 }}>
          {!pomo.running ? (
            <button className="btn btn-amber" onClick={() => startPomo("focus")}>▶ Begin focus block</button>
          ) : (
            <button className="btn btn-danger" onClick={pausePomo}>⏸ Pause</button>
          )}
          <button className="btn btn-ghost" onClick={resetPomo}>Reset</button>
        </div>

        <div className="sub" style={{ maxWidth: 460, textAlign: "center", marginTop: 22, fontSize: 13, color: "var(--vps-text-body)", lineHeight: 1.6 }}>
          Close Slack. Close email. Put your phone face-down. Pick the smallest thing that moves your day forward.
          When the bell rings, take a breath and step away from the screen.
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   SCRATCHPAD
   ============================================================ */
function ScratchpadTool() {
  const app = window.Office.useApp();
  const { scratch, setScratch } = app;
  const [copied, setCopied] = useStateT(false);
  const words = useMemoT(() => scratch.trim() ? scratch.trim().split(/\s+/).length : 0, [scratch]);
  const chars = scratch.length;

  const onCopy = () => {
    navigator.clipboard.writeText(scratch).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 1500);
    });
  };
  const onClear = () => { if (confirm("Clear scratchpad?")) setScratch(""); };

  return (
    <div className="card">
      <div className="row" style={{ marginBottom: 12 }}>
        <span className="label" style={{ margin: 0 }}>Quick notes · session-only</span>
        <div className="spacer"></div>
        <span className="mono" style={{ fontSize: 11, color: "var(--vps-text-muted)" }}>
          {words} word{words === 1 ? "" : "s"} · {chars} char{chars === 1 ? "" : "s"}
        </span>
        <button className={"copy-btn " + (copied ? "copied" : "")} onClick={onCopy}>{copied ? "Copied" : "Copy all"}</button>
        <button className="copy-btn" onClick={onClear} style={{ background: "transparent", color: "var(--vps-text-muted)", borderColor: "var(--vps-border)" }}>Clear</button>
      </div>
      <textarea
        className="textarea"
        style={{ minHeight: 480, fontSize: 14, lineHeight: 1.7 }}
        placeholder="Jot anything. Half-thoughts, todos, quotes from a meeting. It disappears when you close the tab — that's the point."
        value={scratch}
        onChange={e => setScratch(e.target.value)}
        autoFocus
      ></textarea>
      <p className="sub" style={{ marginTop: 12, fontSize: 12, color: "var(--vps-text-muted)" }}>
        Heads up: no autosave. If that worries you, hit <b>Copy all</b> and paste somewhere safer.
      </p>
    </div>
  );
}

/* ============================================================
   WORLD CLOCK
   ============================================================ */
const CITIES = [
  { name: "Honolulu", country: "USA", tz: "Pacific/Honolulu" },
  { name: "Los Angeles", country: "USA", tz: "America/Los_Angeles" },
  { name: "San Francisco", country: "USA", tz: "America/Los_Angeles" },
  { name: "Seattle", country: "USA", tz: "America/Los_Angeles" },
  { name: "Vancouver", country: "Canada", tz: "America/Vancouver" },
  { name: "Denver", country: "USA", tz: "America/Denver" },
  { name: "Mexico City", country: "Mexico", tz: "America/Mexico_City" },
  { name: "Chicago", country: "USA", tz: "America/Chicago" },
  { name: "Toronto", country: "Canada", tz: "America/Toronto" },
  { name: "New York", country: "USA", tz: "America/New_York" },
  { name: "Boston", country: "USA", tz: "America/New_York" },
  { name: "São Paulo", country: "Brazil", tz: "America/Sao_Paulo" },
  { name: "London", country: "UK", tz: "Europe/London" },
  { name: "Dublin", country: "Ireland", tz: "Europe/Dublin" },
  { name: "Paris", country: "France", tz: "Europe/Paris" },
  { name: "Berlin", country: "Germany", tz: "Europe/Berlin" },
  { name: "Amsterdam", country: "Netherlands", tz: "Europe/Amsterdam" },
  { name: "Stockholm", country: "Sweden", tz: "Europe/Stockholm" },
  { name: "Athens", country: "Greece", tz: "Europe/Athens" },
  { name: "Istanbul", country: "Turkey", tz: "Europe/Istanbul" },
  { name: "Moscow", country: "Russia", tz: "Europe/Moscow" },
  { name: "Dubai", country: "UAE", tz: "Asia/Dubai" },
  { name: "Mumbai", country: "India", tz: "Asia/Kolkata" },
  { name: "Bangkok", country: "Thailand", tz: "Asia/Bangkok" },
  { name: "Singapore", country: "Singapore", tz: "Asia/Singapore" },
  { name: "Hong Kong", country: "China", tz: "Asia/Hong_Kong" },
  { name: "Shanghai", country: "China", tz: "Asia/Shanghai" },
  { name: "Seoul", country: "S. Korea", tz: "Asia/Seoul" },
  { name: "Tokyo", country: "Japan", tz: "Asia/Tokyo" },
  { name: "Sydney", country: "Australia", tz: "Australia/Sydney" },
  { name: "Auckland", country: "New Zealand", tz: "Pacific/Auckland" },
];

function offTz(tz, now) {
  const dtf = new Intl.DateTimeFormat("en-US", { timeZone: tz, hour12: false, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit" });
  const parts = dtf.formatToParts(now).reduce((a, p) => (a[p.type] = p.value, a), {});
  const asUTC = Date.UTC(+parts.year, +parts.month - 1, +parts.day, +parts.hour % 24, +parts.minute, +parts.second);
  return Math.round((asUTC - now.getTime()) / 60000);
}
function fmtTime(tz, now) {
  return new Intl.DateTimeFormat([], { timeZone: tz, hour: "2-digit", minute: "2-digit", hour12: false }).format(now);
}
function fmtDay(tz, now) {
  return new Intl.DateTimeFormat([], { timeZone: tz, weekday: "short", month: "short", day: "numeric" }).format(now);
}
function fmtOffset(mins) {
  const sign = mins >= 0 ? "+" : "−";
  const abs = Math.abs(mins);
  return `UTC${sign}${String(Math.floor(abs / 60)).padStart(2, "0")}:${String(abs % 60).padStart(2, "0")}`;
}

function WorldClockTool() {
  const app = window.Office.useApp();
  const localTz = useMemoT(() => Intl.DateTimeFormat().resolvedOptions().timeZone, []);
  const [cities, setCities] = useStateT([
    { name: "Your local time", tz: localTz, primary: true, country: "" },
    { name: "London", country: "UK", tz: "Europe/London" },
    { name: "New York", country: "USA", tz: "America/New_York" },
    { name: "Tokyo", country: "Japan", tz: "Asia/Tokyo" },
  ]);
  const [q, setQ] = useStateT("");
  const now = app.now;

  const matches = useMemoT(() => {
    const ql = q.trim().toLowerCase();
    if (!ql) return [];
    return CITIES.filter(c =>
      (c.name.toLowerCase().includes(ql) || c.country.toLowerCase().includes(ql) || c.tz.toLowerCase().includes(ql)) &&
      !cities.some(sc => sc.name === c.name && sc.tz === c.tz)
    ).slice(0, 8);
  }, [q, cities]);

  const localOff = offTz(localTz, now);
  const nowMin = now.getUTCHours() * 60 + now.getUTCMinutes();

  return (
    <div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(220px, 1fr))", gap: 12 }}>
        {cities.map((city, idx) => {
          const off = offTz(city.tz, now);
          const diff = (off - localOff) / 60;
          return (
            <div key={city.name + idx} className="card" style={{
              padding: "16px 18px",
              borderColor: city.primary ? "rgba(96,165,250,0.45)" : undefined,
              background: city.primary ? "linear-gradient(135deg, rgba(37,99,235,0.18), rgba(37,99,235,0.04))" : undefined,
            }}>
              <div className="row" style={{ marginBottom: 10 }}>
                <span style={{ fontSize: 11, letterSpacing: "0.20em", textTransform: "uppercase", color: city.primary ? "var(--vps-blue-300)" : "var(--vps-text-muted)", fontWeight: 600 }}>{city.name}</span>
                <div className="spacer"></div>
                {!city.primary && (
                  <button className="copy-btn" style={{ background: "transparent", color: "var(--vps-text-muted)", borderColor: "var(--vps-border)", padding: "2px 8px" }}
                    onClick={() => setCities(cs => cs.filter((_, i) => i !== idx))}>✕</button>
                )}
              </div>
              <div style={{ fontFamily: "var(--font-mono)", fontSize: 32, fontWeight: 700, letterSpacing: "0.02em" }}>{fmtTime(city.tz, now)}</div>
              <div className="mono" style={{ fontSize: 12, color: "var(--vps-text-body)", marginTop: 6 }}>
                {fmtDay(city.tz, now)} · {city.country || city.tz.replace(/_/g, " ")} · {fmtOffset(off)}
              </div>
              {!city.primary && (
                <div className="mono" style={{ fontSize: 11, color: "var(--vps-amber-300)", marginTop: 3 }}>
                  {diff === 0 ? "Same as local" : (diff > 0 ? `+${Math.abs(diff)}h ahead` : `−${Math.abs(diff)}h behind`)}
                </div>
              )}
            </div>
          );
        })}
      </div>

      <div className="card" style={{ marginTop: 18 }}>
        <label className="label">Add a city</label>
        <input className="input" placeholder="Search: tokyo, paris, sydney…" value={q} onChange={e => setQ(e.target.value)} />
        {matches.length > 0 && (
          <div style={{ marginTop: 10, border: "1px solid var(--vps-border)", borderRadius: 10, overflow: "hidden", maxHeight: 260, overflowY: "auto" }}>
            {matches.map(c => (
              <button key={c.name + c.tz} style={{
                width: "100%", textAlign: "left", padding: "9px 12px",
                background: "transparent", color: "var(--vps-text-secondary)",
                fontFamily: "var(--font-mono)", fontSize: 12.5,
                borderBottom: "1px solid var(--vps-border)",
              }} onClick={() => { setCities(cs => [...cs, c]); setQ(""); }}>
                {c.name}<span style={{ color: "var(--vps-text-muted)", marginLeft: 8 }}>{c.country} · {c.tz}</span>
              </button>
            ))}
          </div>
        )}
      </div>

      <div className="card" style={{ marginTop: 18 }}>
        <span className="label">Working hours overlap · 9–17 in each TZ</span>
        <div style={{ display: "flex", flexDirection: "column", gap: 6, marginTop: 10 }}>
          {cities.map((city, i) => {
            const off = offTz(city.tz, now);
            const startUtc = ((9 * 60) - off + 1440) % 1440;
            const endUtc = ((17 * 60) - off + 1440) % 1440;
            const nowPct = (nowMin / 1440) * 100;
            const renderFill = startUtc < endUtc
              ? <div style={{ position: "absolute", top: 0, bottom: 0, left: (startUtc / 1440 * 100) + "%", right: (100 - endUtc / 1440 * 100) + "%", background: "linear-gradient(90deg, rgba(16,185,129,0.30), rgba(96,165,250,0.30))" }}></div>
              : <>
                  <div style={{ position: "absolute", top: 0, bottom: 0, left: (startUtc / 1440 * 100) + "%", right: 0, background: "linear-gradient(90deg, rgba(16,185,129,0.30), rgba(96,165,250,0.30))" }}></div>
                  <div style={{ position: "absolute", top: 0, bottom: 0, left: 0, right: (100 - endUtc / 1440 * 100) + "%", background: "linear-gradient(90deg, rgba(16,185,129,0.30), rgba(96,165,250,0.30))" }}></div>
                </>;
            return (
              <div key={i} style={{ display: "grid", gridTemplateColumns: "120px 1fr 70px", gap: 12, alignItems: "center", fontFamily: "var(--font-mono)", fontSize: 12 }}>
                <span style={{ color: "var(--vps-text-secondary)", letterSpacing: "0.05em" }}>{city.name}</span>
                <div style={{ position: "relative", height: 18, borderRadius: 999, background: "rgba(255,255,255,0.04)", border: "1px solid var(--vps-border)", overflow: "hidden" }}>
                  {renderFill}
                  <div style={{ position: "absolute", top: -3, bottom: -3, width: 2, left: nowPct + "%", background: "var(--vps-amber-300)", boxShadow: "0 0 8px var(--vps-amber-300)" }}></div>
                </div>
                <span style={{ color: "var(--vps-blue-300)", textAlign: "right" }}>{fmtTime(city.tz, now)}</span>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   MEETING COST
   ============================================================ */
const CURR = { USD: "$", CAD: "$", EUR: "€", GBP: "£", JPY: "¥", AUD: "$" };

function MeetingCostTool() {
  const [state, setState] = useStateT({
    attendees: 6, hourly: 85, currency: "USD", burden: 1.4,
    running: false, startedAt: null, accumulatedMs: 0,
  });
  const stateRef = useRefT(state);
  stateRef.current = state;
  const [tick, setTick] = useStateT(0);

  useEffectT(() => {
    if (!state.running) return;
    const id = setInterval(() => setTick(t => t + 1), 200);
    return () => clearInterval(id);
  }, [state.running]);

  const elapsedMs = state.accumulatedMs + (state.running && state.startedAt ? Date.now() - state.startedAt : 0);
  const fmt = (n) => {
    const sym = CURR[state.currency] || "";
    const dec = state.currency === "JPY" ? 0 : 2;
    return sym + n.toLocaleString(undefined, { minimumFractionDigits: dec, maximumFractionDigits: dec });
  };
  const cost = (elapsedMs / 3600000) * state.attendees * state.hourly * state.burden;
  const perMin = (state.attendees * state.hourly * state.burden) / 60;
  const fmtTime = (ms) => {
    const total = Math.floor(ms / 1000);
    return [Math.floor(total / 3600), Math.floor((total % 3600) / 60), total % 60].map(n => String(n).padStart(2, "0")).join(":");
  };

  const start = () => setState(s => ({ ...s, running: true, startedAt: Date.now() }));
  const stop = () => setState(s => ({ ...s, running: false, accumulatedMs: s.accumulatedMs + (s.startedAt ? Date.now() - s.startedAt : 0), startedAt: null }));
  const reset = () => setState(s => ({ ...s, running: false, startedAt: null, accumulatedMs: 0 }));

  return (
    <div style={{ display: "grid", gridTemplateColumns: "360px 1fr", gap: 18 }}>
      <style>{`@media (max-width: 920px){ .mc-grid { grid-template-columns: 1fr !important; } }`}</style>
      <div className="mc-grid" style={{ display: "contents" }}>
        <div className="card">
          <h3 style={{ margin: "0 0 14px", fontSize: 11, letterSpacing: "0.30em", textTransform: "uppercase", color: "var(--vps-blue-300)" }}>1 · Setup</h3>
          <div className="field">
            <label className="label">Attendees</label>
            <input className="input" type="number" min="1" max="100" value={state.attendees} onChange={e => setState(s => ({ ...s, attendees: Math.max(1, +e.target.value || 1) }))} />
          </div>
          <div className="field">
            <label className="label">Avg hourly rate</label>
            <input className="input" type="number" min="0" step="5" value={state.hourly} onChange={e => setState(s => ({ ...s, hourly: Math.max(0, +e.target.value || 0) }))} />
          </div>
          <div className="field">
            <label className="label">Currency</label>
            <select className="select" value={state.currency} onChange={e => setState(s => ({ ...s, currency: e.target.value }))}>
              {Object.keys(CURR).map(c => <option key={c} value={c}>{c} ({CURR[c]})</option>)}
            </select>
          </div>
          <div className="field">
            <label className="label">Burden multiplier</label>
            <div className="chips">
              {[[1.0, "×1.0 raw"], [1.4, "×1.4 loaded"], [1.8, "×1.8 +overhead"]].map(([v, label]) => (
                <button key={v} className={"chip " + (state.burden === v ? "active" : "")} onClick={() => setState(s => ({ ...s, burden: v }))}>{label}</button>
              ))}
            </div>
            <p className="sub" style={{ fontSize: 11.5, marginTop: 6 }}>Loaded = +benefits & taxes. Overhead = +office, software, ops.</p>
          </div>
        </div>

        <div className="card" style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", minHeight: 380, padding: 30 }}>
          <span className="label" style={{ margin: 0 }}>{state.running ? "Burning · live" : (elapsedMs > 0 ? "Paused" : "Standby")}</span>
          <div style={{
            fontFamily: "var(--font-mono)", fontWeight: 700,
            fontSize: "clamp(56px, 11vw, 110px)", lineHeight: 1, margin: "14px 0",
            background: "linear-gradient(135deg, var(--vps-text-primary) 25%, var(--vps-blue-200) 60%, var(--vps-amber-200) 100%)",
            WebkitBackgroundClip: "text", backgroundClip: "text", color: "transparent",
            letterSpacing: "-0.02em",
            animation: state.running ? "countpulse 1s ease-in-out infinite alternate" : "none",
          }}>{fmt(cost)}</div>
          <style>{`@keyframes countpulse { from { filter: brightness(1); } to { filter: brightness(1.18); } }`}</style>
          <div className="mono sub">{state.running ? `${state.attendees} attendees @ ${fmt(state.hourly)}/hr × ${state.burden}×` : (elapsedMs > 0 ? "Resume to keep counting" : "Press start to begin")}</div>
          <div className="row" style={{ marginTop: 22, justifyContent: "center" }}>
            {!state.running ? <button className="btn btn-primary" onClick={start}>▶ Start meeting</button> : <button className="btn btn-danger" onClick={stop}>■ Stop</button>}
            <button className="btn btn-ghost" onClick={reset}>Reset</button>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10, marginTop: 22, width: "100%" }}>
            {[["Elapsed", fmtTime(elapsedMs)], ["Per minute", fmt(perMin)], ["Per attendee", fmt(state.attendees ? cost / state.attendees : 0)]].map(([l, v]) => (
              <div key={l} style={{ padding: "10px 12px", border: "1px solid var(--vps-border)", borderRadius: 10, background: "rgba(0,0,0,0.16)", textAlign: "center" }}>
                <div style={{ fontSize: 10, letterSpacing: "0.20em", textTransform: "uppercase", color: "var(--vps-text-muted)" }}>{l}</div>
                <div className="mono" style={{ fontSize: 14, marginTop: 4 }}>{v}</div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   MIRROR
   ============================================================ */
function MirrorTool() {
  const videoRef = useRefT(null);
  const [stream, setStream] = useStateT(null);
  const [error, setError] = useStateT(null);
  const [flipped, setFlipped] = useStateT(true);

  const start = async () => {
    try {
      const s = await navigator.mediaDevices.getUserMedia({ video: true, audio: false });
      setStream(s);
      if (videoRef.current) videoRef.current.srcObject = s;
      setError(null);
    } catch (e) {
      setError(e.message || "Couldn't access the camera");
    }
  };
  const stop = () => {
    stream?.getTracks().forEach(t => t.stop());
    setStream(null);
  };
  useEffectT(() => () => { stream?.getTracks().forEach(t => t.stop()); }, []);

  return (
    <div className="card" style={{ overflow: "hidden", padding: 0 }}>
      <div style={{ position: "relative", aspectRatio: "16 / 9", background: "#000", display: "grid", placeItems: "center" }}>
        {stream ? (
          <video ref={videoRef} autoPlay playsInline style={{ width: "100%", height: "100%", objectFit: "cover", transform: flipped ? "scaleX(-1)" : "none" }} />
        ) : (
          <div style={{ textAlign: "center", padding: 40 }}>
            <div style={{ fontSize: 48, marginBottom: 12 }}>🪞</div>
            <h3 style={{ margin: "0 0 6px", fontSize: 18 }}>Camera off</h3>
            <p className="sub" style={{ maxWidth: 320, margin: "0 auto", fontSize: 13 }}>
              Click below to check your hair, lighting, and that you're not eating a sandwich on camera.
            </p>
            {error && <div style={{ marginTop: 14, padding: "10px 14px", background: "rgba(239,68,68,0.10)", border: "1px solid rgba(239,68,68,0.40)", borderRadius: 8, color: "#fecaca", fontSize: 12, maxWidth: 380, margin: "14px auto 0" }}>{error}</div>}
          </div>
        )}
      </div>
      <div className="row" style={{ padding: 18, gap: 10 }}>
        {!stream
          ? <button className="btn btn-primary" onClick={start}>📷 Turn on camera</button>
          : <button className="btn btn-danger" onClick={stop}>■ Turn off</button>}
        {stream && <button className="btn btn-ghost" onClick={() => setFlipped(f => !f)}>{flipped ? "Unflip" : "Mirror"} view</button>}
        <div className="spacer"></div>
        <span className="sub mono" style={{ fontSize: 11 }}>Camera stream stays in your browser — never uploaded</span>
      </div>
    </div>
  );
}

/* ============================================================
   PASSWORD GENERATOR
   ============================================================ */
function PasswordTool() {
  const [opts, setOpts] = useStateT({ length: 20, lower: true, upper: true, digits: true, symbols: true });
  const [password, setPassword] = useStateT("");
  const [copied, setCopied] = useStateT(false);

  const generate = useCallbackT(() => {
    let chars = "";
    if (opts.lower) chars += "abcdefghijklmnopqrstuvwxyz";
    if (opts.upper) chars += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    if (opts.digits) chars += "0123456789";
    if (opts.symbols) chars += "!@#$%^&*()-_=+[]{};:,.<>?/~";
    if (!chars) { setPassword(""); return; }
    const arr = new Uint32Array(opts.length);
    crypto.getRandomValues(arr);
    setPassword(Array.from(arr, n => chars[n % chars.length]).join(""));
  }, [opts]);

  useEffectT(() => { generate(); }, [generate]);

  const onCopy = () => navigator.clipboard.writeText(password).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1500); });

  // strength
  const entropy = useMemoT(() => {
    let alphabet = 0;
    if (opts.lower) alphabet += 26;
    if (opts.upper) alphabet += 26;
    if (opts.digits) alphabet += 10;
    if (opts.symbols) alphabet += 28;
    return alphabet ? Math.round(opts.length * Math.log2(alphabet)) : 0;
  }, [opts]);
  const strengthLabel = entropy > 100 ? "Excellent" : entropy > 70 ? "Strong" : entropy > 40 ? "OK" : "Weak";
  const strengthColor = entropy > 100 ? "var(--vps-green-400)" : entropy > 70 ? "var(--vps-blue-400)" : entropy > 40 ? "var(--vps-amber-400)" : "var(--vps-red-400)";

  return (
    <div className="card">
      <div className="row" style={{ marginBottom: 14 }}>
        <span className="label" style={{ margin: 0 }}>Generated password · uses browser crypto</span>
        <div className="spacer"></div>
        <span className="mono sub" style={{ fontSize: 11, color: strengthColor, fontWeight: 600 }}>{strengthLabel} · {entropy} bits</span>
      </div>
      <div style={{
        display: "flex", alignItems: "center", gap: 10,
        background: "rgba(0,0,0,0.30)", border: "1px solid var(--vps-border)",
        borderRadius: 10, padding: "14px 16px", marginBottom: 16,
      }}>
        <span className="mono" style={{ flex: 1, fontSize: 18, letterSpacing: "0.02em", wordBreak: "break-all" }}>{password || <span className="muted">Set at least one character group</span>}</span>
        <button className={"copy-btn " + (copied ? "copied" : "")} onClick={onCopy} disabled={!password}>{copied ? "Copied" : "Copy"}</button>
        <button className="copy-btn" onClick={generate}>↺</button>
      </div>

      <div className="field">
        <label className="label">Length: {opts.length}</label>
        <input type="range" min="8" max="64" value={opts.length} onChange={e => setOpts(o => ({ ...o, length: +e.target.value }))} className="input" style={{ padding: 0, background: "transparent", border: 0 }} />
      </div>

      <div className="chips">
        {[["lower", "a–z"], ["upper", "A–Z"], ["digits", "0–9"], ["symbols", "!@#$"]].map(([k, label]) => (
          <button key={k} className={"chip " + (opts[k] ? "active" : "")} onClick={() => setOpts(o => ({ ...o, [k]: !o[k] }))}>{label}</button>
        ))}
      </div>
    </div>
  );
}

/* ============================================================
   MARKDOWN PREVIEW
   ============================================================ */
function mdToHtml(src) {
  if (!src) return "";
  // Lightweight markdown — headings, bold/italic/code, links, lists, blockquote, hr, paragraphs
  let html = src
    .replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  // Code blocks
  html = html.replace(/```([\s\S]*?)```/g, (_, code) => `<pre><code>${code.trim()}</code></pre>`);
  // Headings
  html = html.replace(/^###### (.*)$/gm, "<h6>$1</h6>");
  html = html.replace(/^##### (.*)$/gm, "<h5>$1</h5>");
  html = html.replace(/^#### (.*)$/gm, "<h4>$1</h4>");
  html = html.replace(/^### (.*)$/gm, "<h3>$1</h3>");
  html = html.replace(/^## (.*)$/gm, "<h2>$1</h2>");
  html = html.replace(/^# (.*)$/gm, "<h1>$1</h1>");
  // HR
  html = html.replace(/^---$/gm, "<hr/>");
  // Blockquote
  html = html.replace(/^&gt; (.*)$/gm, "<blockquote>$1</blockquote>");
  // Bold + italic + inline code
  html = html.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
  html = html.replace(/\*([^*]+)\*/g, "<em>$1</em>");
  html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
  // Links (sanitize URL — block javascript:/data:/vbscript: and escape attribute quotes)
  html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_m, text, url) => {
    const trimmed = (url || "").trim();
    const safe = /^(javascript|data|vbscript):/i.test(trimmed)
      ? "#"
      : trimmed.replace(/"/g, "&quot;").replace(/'/g, "&#39;");
    return `<a href="${safe}" rel="noreferrer noopener" target="_blank">${text}</a>`;
  });
  // Lists (simple)
  html = html.replace(/(^|\n)((?:- .+\n?)+)/g, (m, p, lst) => p + "<ul>" + lst.trim().split("\n").map(li => "<li>" + li.replace(/^- /, "") + "</li>").join("") + "</ul>");
  html = html.replace(/(^|\n)((?:\d+\. .+\n?)+)/g, (m, p, lst) => p + "<ol>" + lst.trim().split("\n").map(li => "<li>" + li.replace(/^\d+\. /, "") + "</li>").join("") + "</ol>");
  // Paragraphs (simple - split on blank lines that aren't already a block)
  html = html.split(/\n{2,}/).map(b => {
    if (/^<(h\d|ul|ol|pre|blockquote|hr)/.test(b.trim())) return b;
    return "<p>" + b.replace(/\n/g, "<br/>") + "</p>";
  }).join("\n");
  return html;
}

function MarkdownTool() {
  const [src, setSrc] = useStateT(`# Hello Markdown

A **markdown** preview that works in your browser. Type on the left, see the rendered version on the right.

## Things it supports
- Headings, lists, links
- *Italic*, **bold**, \`inline code\`
- Code blocks and blockquotes

> "The best time to plant a tree was 20 years ago. The second best time is now."

\`\`\`
function hello() {
  return 42;
}
\`\`\`

[VibeProSoft](https://example.com)
`);
  const html = useMemoT(() => mdToHtml(src), [src]);
  const [copied, setCopied] = useStateT(false);

  return (
    <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
      <style>{`
        @media (max-width: 880px) { .md-grid { grid-template-columns: 1fr !important; } }
        .md-preview h1 { font-size: 24px; margin: 0 0 10px; }
        .md-preview h2 { font-size: 19px; margin: 14px 0 8px; }
        .md-preview h3 { font-size: 16px; margin: 12px 0 6px; }
        .md-preview p  { line-height: 1.6; margin: 0 0 10px; color: var(--vps-text-secondary); }
        .md-preview ul, .md-preview ol { padding-left: 22px; margin: 0 0 10px; line-height: 1.6; }
        .md-preview blockquote { border-left: 3px solid var(--vps-blue-400); padding: 4px 14px; margin: 10px 0; color: var(--vps-text-body); background: rgba(37,99,235,0.06); border-radius: 0 6px 6px 0; }
        .md-preview pre { background: rgba(0,0,0,0.30); border: 1px solid var(--vps-border); border-radius: 8px; padding: 12px 14px; overflow-x: auto; font-family: var(--font-mono); font-size: 12.5px; }
        [data-theme="light"] .md-preview pre { background: rgba(255,255,255,0.6); }
        .md-preview code { background: rgba(37,99,235,0.10); padding: 2px 6px; border-radius: 4px; font-family: var(--font-mono); font-size: 12.5px; }
        .md-preview pre code { background: transparent; padding: 0; }
        .md-preview a { color: var(--vps-blue-300); text-decoration: underline; }
        [data-theme="light"] .md-preview a { color: var(--vps-blue-700); }
        .md-preview hr { border: 0; border-top: 1px solid var(--vps-border); margin: 14px 0; }
      `}</style>
      <div className="md-grid" style={{ display: "contents" }}>
        <div className="card">
          <div className="row" style={{ marginBottom: 10 }}>
            <span className="label" style={{ margin: 0 }}>Source</span>
            <div className="spacer"></div>
            <button className={"copy-btn " + (copied ? "copied" : "")} onClick={() => navigator.clipboard.writeText(src).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1200); })}>{copied ? "Copied" : "Copy md"}</button>
          </div>
          <textarea className="textarea" style={{ minHeight: 460 }} value={src} onChange={e => setSrc(e.target.value)} spellCheck="false"></textarea>
        </div>
        <div className="card">
          <div className="row" style={{ marginBottom: 10 }}>
            <span className="label" style={{ margin: 0 }}>Preview</span>
          </div>
          <div className="md-preview" style={{ minHeight: 460, padding: "8px 4px" }} dangerouslySetInnerHTML={{ __html: html }}></div>
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   UNIT CONVERTER
   ============================================================ */
const UNITS = {
  length: { base: "m", units: { mm: 0.001, cm: 0.01, m: 1, km: 1000, in: 0.0254, ft: 0.3048, yd: 0.9144, mi: 1609.344 } },
  weight: { base: "kg", units: { mg: 1e-6, g: 0.001, kg: 1, t: 1000, oz: 0.0283495, lb: 0.453592, st: 6.35029 } },
  volume: { base: "L", units: { ml: 0.001, L: 1, "fl oz": 0.0295735, cup: 0.236588, pt: 0.473176, qt: 0.946353, gal: 3.78541 } },
  temperature: { base: "C", units: { C: "C", F: "F", K: "K" } },
};

function convertTemp(value, from, to) {
  let c;
  if (from === "C") c = value;
  else if (from === "F") c = (value - 32) * 5/9;
  else c = value - 273.15;
  if (to === "C") return c;
  if (to === "F") return c * 9/5 + 32;
  return c + 273.15;
}

function UnitTool() {
  const [cat, setCat] = useStateT("length");
  const [from, setFrom] = useStateT("m");
  const [to, setTo] = useStateT("ft");
  const [val, setVal] = useStateT(1);

  useEffectT(() => {
    const keys = Object.keys(UNITS[cat].units);
    if (!keys.includes(from)) setFrom(keys[0]);
    if (!keys.includes(to)) setTo(keys[1] || keys[0]);
  }, [cat]);

  const result = useMemoT(() => {
    if (cat === "temperature") return convertTemp(+val || 0, from, to);
    const u = UNITS[cat].units;
    return (+val || 0) * u[from] / u[to];
  }, [cat, from, to, val]);

  return (
    <div className="card">
      <div className="chips" style={{ marginBottom: 18 }}>
        {Object.keys(UNITS).map(c => (
          <button key={c} className={"chip " + (cat === c ? "active" : "")} onClick={() => setCat(c)}>{c}</button>
        ))}
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "1fr auto 1fr", gap: 12, alignItems: "end" }}>
        <div>
          <label className="label">From</label>
          <input className="input" type="number" value={val} onChange={e => setVal(e.target.value)} style={{ fontFamily: "var(--font-mono)", fontSize: 18 }} />
          <select className="select" value={from} onChange={e => setFrom(e.target.value)} style={{ marginTop: 10 }}>
            {Object.keys(UNITS[cat].units).map(u => <option key={u} value={u}>{u}</option>)}
          </select>
        </div>
        <div style={{ paddingBottom: 18, fontSize: 24, color: "var(--vps-blue-400)" }}>→</div>
        <div>
          <label className="label">To</label>
          <input className="input" readOnly value={Number(result.toFixed(6))} style={{ fontFamily: "var(--font-mono)", fontSize: 18, background: "rgba(96,165,250,0.10)" }} />
          <select className="select" value={to} onChange={e => setTo(e.target.value)} style={{ marginTop: 10 }}>
            {Object.keys(UNITS[cat].units).map(u => <option key={u} value={u}>{u}</option>)}
          </select>
        </div>
      </div>
      <div className="sub mono" style={{ marginTop: 16, fontSize: 12, color: "var(--vps-text-muted)" }}>
        {val || 0} {from} = <b style={{ color: "var(--vps-text-primary)" }}>{Number(result.toFixed(6))} {to}</b>
      </div>
    </div>
  );
}

/* ============================================================
   PROMPT WIZARD
   ============================================================ */
const PW_MODELS = [
  { id: "claude",  name: "Claude (Anthropic)",        style: "xml",      tip: "Claude responds best to XML-style structure and explicit thinking instructions." },
  { id: "chatgpt", name: "ChatGPT / GPT-4o (OpenAI)", style: "markdown", tip: "ChatGPT works well with markdown structure and clear role instructions." },
  { id: "gemini",  name: "Gemini (Google)",            style: "markdown", tip: "Gemini favors concise instructions and clear examples." },
  { id: "generic", name: "Model-agnostic",             style: "markdown", tip: "Plain markdown structure. Works across most models." },
];
const PW_TEMPLATES = [
  { id: "code-review", name: "Code review", desc: "Have the model review code for bugs, style, and improvements.",
    placeholders: [
      { key: "language", label: "Language", type: "input", default: "TypeScript" },
      { key: "code", label: "Code to review", type: "textarea", default: "" },
      { key: "focus", label: "Focus areas", type: "input", default: "correctness, performance, readability" },
    ],
    body: `You are an experienced {{language}} engineer doing a code review.

Review the code below for: {{focus}}.

For each issue, give: (1) a one-line summary, (2) the specific line/block, (3) a concrete fix, (4) why it matters.
End with a short overall assessment.

\`\`\`{{language}}
{{code}}
\`\`\`` },
  { id: "email-reply", name: "Email reply", desc: "Draft a reply in a specific tone.",
    placeholders: [
      { key: "tone", label: "Tone", type: "input", default: "professional and friendly" },
      { key: "original", label: "Email you received", type: "textarea", default: "" },
      { key: "intent", label: "What you want to say back", type: "textarea", default: "" },
    ],
    body: `Draft a reply to the email below.

Tone: {{tone}}. Length: appropriate to the original.

Email I received:
"""
{{original}}
"""

Gist of what I want to say:
"""
{{intent}}
"""

Now write the reply.` },
  { id: "meeting", name: "Meeting summary", desc: "Turn raw notes into a clean summary with actions.",
    placeholders: [
      { key: "meeting", label: "Meeting name / context", type: "input", default: "" },
      { key: "notes", label: "Raw notes or transcript", type: "textarea", default: "" },
    ],
    body: `Summarize the meeting below in three sections:

1. **Decisions** — what was actually decided
2. **Action items** — bullets with owner + deadline
3. **Open questions** — anything unresolved

Be terse. No filler. If a section has no items, write "None."

Meeting: {{meeting}}
Notes:
"""
{{notes}}
"""` },
  { id: "rewrite", name: "Rewrite text", desc: "Edit a piece of writing for a goal.",
    placeholders: [
      { key: "goal", label: "Goal", type: "input", default: "more concise and direct" },
      { key: "audience", label: "Audience", type: "input", default: "" },
      { key: "text", label: "Text to rewrite", type: "textarea", default: "" },
    ],
    body: `Rewrite the text below to: {{goal}}.

Audience: {{audience}}. Preserve meaning. Don't invent facts. Show only the rewritten version.

Original:
"""
{{text}}
"""` },
  { id: "brainstorm", name: "Brainstorm ideas", desc: "Generate a diverse set of ideas.",
    placeholders: [
      { key: "topic", label: "Topic / problem", type: "textarea", default: "" },
      { key: "count", label: "How many ideas", type: "input", default: "10" },
    ],
    body: `Generate {{count}} distinct ideas for: {{topic}}.

For each: a 3–5 word name, one-line description, one reason it might work, one reason it might not. Order safest → most ambitious.` },
];

function PromptWizardTool() {
  const [modelId, setModelId] = useStateT(PW_MODELS[0].id);
  const [tplId, setTplId] = useStateT(PW_TEMPLATES[0].id);
  const tpl = PW_TEMPLATES.find(t => t.id === tplId);
  const model = PW_MODELS.find(m => m.id === modelId);
  const [vals, setVals] = useStateT(() => Object.fromEntries(tpl.placeholders.map(p => [p.key, p.default || ""])));
  const [copied, setCopied] = useStateT(false);

  useEffectT(() => {
    setVals(Object.fromEntries(tpl.placeholders.map(p => [p.key, p.default || ""])));
  }, [tplId]);

  const output = useMemoT(() => {
    let body = tpl.body;
    Object.entries(vals).forEach(([k, v]) => {
      body = body.replace(new RegExp(`{{\\s*${k}\\s*}}`, "g"), v || `[${k}]`);
    });
    return model.style === "xml"
      ? `<task>\n${body}\n</task>\n\n<instructions>\nThink step by step. Be concise but complete.\n</instructions>`
      : `# Task\n\n${body}\n\n---\n_Think step by step. Be concise but complete._`;
  }, [tpl, vals, model]);

  return (
    <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
      <style>{`@media (max-width: 980px){ .pw-grid { grid-template-columns: 1fr !important; } }`}</style>
      <div className="pw-grid" style={{ display: "contents" }}>
        <div className="card">
          <div className="field">
            <label className="label">Target model</label>
            <select className="select" value={modelId} onChange={e => setModelId(e.target.value)}>
              {PW_MODELS.map(m => <option key={m.id} value={m.id}>{m.name}</option>)}
            </select>
          </div>
          <div className="field">
            <label className="label">Template</label>
            <select className="select" value={tplId} onChange={e => setTplId(e.target.value)}>
              {PW_TEMPLATES.map(t => <option key={t.id} value={t.id}>{t.name}</option>)}
            </select>
          </div>
          <p className="sub" style={{ fontSize: 12.5, marginBottom: 18 }}>{tpl.desc}</p>

          <div style={{ borderTop: "1px solid var(--vps-border)", paddingTop: 18 }}>
            <span className="label">Fill the blanks</span>
            {tpl.placeholders.map(p => (
              <div key={p.key} className="field" style={{ marginTop: 12 }}>
                <label className="label" style={{ fontSize: 10, letterSpacing: "0.10em" }}>{p.label}</label>
                {p.type === "textarea" ? (
                  <textarea className="textarea" style={{ minHeight: 100 }} value={vals[p.key] || ""} onChange={e => setVals(v => ({ ...v, [p.key]: e.target.value }))}></textarea>
                ) : (
                  <input className="input" value={vals[p.key] || ""} onChange={e => setVals(v => ({ ...v, [p.key]: e.target.value }))} />
                )}
              </div>
            ))}
          </div>
        </div>
        <div className="card">
          <div className="row" style={{ marginBottom: 10 }}>
            <span className="label" style={{ margin: 0 }}>Your prompt</span>
            <div className="spacer"></div>
            <button className={"copy-btn " + (copied ? "copied" : "")} onClick={() => navigator.clipboard.writeText(output).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1200); })}>{copied ? "Copied" : "Copy"}</button>
          </div>
          <textarea className="textarea" readOnly value={output} style={{ minHeight: 380, whiteSpace: "pre-wrap" }}></textarea>
          <div style={{ marginTop: 12, padding: "10px 12px", background: "rgba(37,99,235,0.06)", border: "1px solid rgba(96,165,250,0.20)", borderRadius: 8, fontSize: 12, color: "var(--vps-blue-300)", lineHeight: 1.5 }}>
            Tip for {model.name}: {model.tip}
          </div>
          <p className="sub" style={{ marginTop: 10, fontSize: 11.5 }}>
            No API calls — copy this prompt and paste into your AI of choice.
          </p>
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   REGISTER
   ============================================================ */
window.ToolComponents = {
  "pomodoro":     PomodoroTool,
  "focus-mode":   FocusModeTool,
  "scratchpad":   ScratchpadTool,
  "world-clock":  WorldClockTool,
  "meeting-cost": MeetingCostTool,
  "mirror":       MirrorTool,
  "password":     PasswordTool,
  "markdown":     MarkdownTool,
  "unit":         UnitTool,
  "prompt":       PromptWizardTool,
};

// more-tools.jsx will call window.OfficeMountApp() after registering additional tools.
