
// tweaks-panel.jsx
// Reusable Tweaks shell + form-control helpers.
//
// Owns the host protocol (listens for __activate_edit_mode / __deactivate_edit_mode,
// posts __edit_mode_available / __edit_mode_set_keys / __edit_mode_dismissed) so
// individual prototypes don't re-roll it. Ships a consistent set of controls so you
// don't hand-draw <input type="range">, segmented radios, steppers, etc.
//
// Usage (in an HTML file that loads React + Babel):
//
//   const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
//     "primaryColor": "#D97757",
//     "fontSize": 16,
//     "density": "regular",
//     "dark": false
//   }/*EDITMODE-END*/;
//
//   function App() {
//     const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
//     return (
//       <div style={{ fontSize: t.fontSize, color: t.primaryColor }}>
//         Hello
//         <TweaksPanel>
//           <TweakSection label="Typography" />
//           <TweakSlider label="Font size" value={t.fontSize} min={10} max={32} unit="px"
//                        onChange={(v) => setTweak('fontSize', v)} />
//           <TweakRadio  label="Density" value={t.density}
//                        options={['compact', 'regular', 'comfy']}
//                        onChange={(v) => setTweak('density', v)} />
//           <TweakSection label="Theme" />
//           <TweakColor  label="Primary" value={t.primaryColor}
//                        onChange={(v) => setTweak('primaryColor', v)} />
//           <TweakToggle label="Dark mode" value={t.dark}
//                        onChange={(v) => setTweak('dark', v)} />
//         </TweaksPanel>
//       </div>
//     );
//   }
//
// ─────────────────────────────────────────────────────────────────────────────

const __TWEAKS_STYLE = `
  .twk-panel{position:fixed;right:16px;bottom:16px;z-index:2147483646;width:280px;
    max-height:calc(100vh - 32px);display:flex;flex-direction:column;
    background:rgba(250,249,247,.78);color:#29261b;
    -webkit-backdrop-filter:blur(24px) saturate(160%);backdrop-filter:blur(24px) saturate(160%);
    border:.5px solid rgba(255,255,255,.6);border-radius:14px;
    box-shadow:0 1px 0 rgba(255,255,255,.5) inset,0 12px 40px rgba(0,0,0,.18);
    font:11.5px/1.4 ui-sans-serif,system-ui,-apple-system,sans-serif;overflow:hidden}
  .twk-hd{display:flex;align-items:center;justify-content:space-between;
    padding:10px 8px 10px 14px;cursor:move;user-select:none}
  .twk-hd b{font-size:12px;font-weight:600;letter-spacing:.01em}
  .twk-x{appearance:none;border:0;background:transparent;color:rgba(41,38,27,.55);
    width:22px;height:22px;border-radius:6px;cursor:default;font-size:13px;line-height:1}
  .twk-x:hover{background:rgba(0,0,0,.06);color:#29261b}
  .twk-body{padding:2px 14px 14px;display:flex;flex-direction:column;gap:10px;
    overflow-y:auto;overflow-x:hidden;min-height:0;
    scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.15) transparent}
  .twk-body::-webkit-scrollbar{width:8px}
  .twk-body::-webkit-scrollbar-track{background:transparent;margin:2px}
  .twk-body::-webkit-scrollbar-thumb{background:rgba(0,0,0,.15);border-radius:4px;
    border:2px solid transparent;background-clip:content-box}
  .twk-body::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,.25);
    border:2px solid transparent;background-clip:content-box}
  .twk-row{display:flex;flex-direction:column;gap:5px}
  .twk-row-h{flex-direction:row;align-items:center;justify-content:space-between;gap:10px}
  .twk-lbl{display:flex;justify-content:space-between;align-items:baseline;
    color:rgba(41,38,27,.72)}
  .twk-lbl>span:first-child{font-weight:500}
  .twk-val{color:rgba(41,38,27,.5);font-variant-numeric:tabular-nums}

  .twk-sect{font-size:10px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
    color:rgba(41,38,27,.45);padding:10px 0 0}
  .twk-sect:first-child{padding-top:0}

  .twk-field{appearance:none;width:100%;height:26px;padding:0 8px;
    border:.5px solid rgba(0,0,0,.1);border-radius:7px;
    background:rgba(255,255,255,.6);color:inherit;font:inherit;outline:none}
  .twk-field:focus{border-color:rgba(0,0,0,.25);background:rgba(255,255,255,.85)}
  select.twk-field{padding-right:22px;
    background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='rgba(0,0,0,.5)' d='M0 0h10L5 6z'/></svg>");
    background-repeat:no-repeat;background-position:right 8px center}

  .twk-slider{appearance:none;-webkit-appearance:none;width:100%;height:4px;margin:6px 0;
    border-radius:999px;background:rgba(0,0,0,.12);outline:none}
  .twk-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;
    width:14px;height:14px;border-radius:50%;background:#fff;
    border:.5px solid rgba(0,0,0,.12);box-shadow:0 1px 3px rgba(0,0,0,.2);cursor:default}
  .twk-slider::-moz-range-thumb{width:14px;height:14px;border-radius:50%;
    background:#fff;border:.5px solid rgba(0,0,0,.12);box-shadow:0 1px 3px rgba(0,0,0,.2);cursor:default}

  .twk-seg{position:relative;display:flex;padding:2px;border-radius:8px;
    background:rgba(0,0,0,.06);user-select:none}
  .twk-seg-thumb{position:absolute;top:2px;bottom:2px;border-radius:6px;
    background:rgba(255,255,255,.9);box-shadow:0 1px 2px rgba(0,0,0,.12);
    transition:left .15s cubic-bezier(.3,.7,.4,1),width .15s}
  .twk-seg.dragging .twk-seg-thumb{transition:none}
  .twk-seg button{appearance:none;position:relative;z-index:1;flex:1;border:0;
    background:transparent;color:inherit;font:inherit;font-weight:500;min-height:22px;
    border-radius:6px;cursor:default;padding:4px 6px;line-height:1.2;
    overflow-wrap:anywhere}

  .twk-toggle{position:relative;width:32px;height:18px;border:0;border-radius:999px;
    background:rgba(0,0,0,.15);transition:background .15s;cursor:default;padding:0}
  .twk-toggle[data-on="1"]{background:#34c759}
  .twk-toggle i{position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;
    background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.25);transition:transform .15s}
  .twk-toggle[data-on="1"] i{transform:translateX(14px)}

  .twk-num{display:flex;align-items:center;height:26px;padding:0 0 0 8px;
    border:.5px solid rgba(0,0,0,.1);border-radius:7px;background:rgba(255,255,255,.6)}
  .twk-num-lbl{font-weight:500;color:rgba(41,38,27,.6);cursor:ew-resize;
    user-select:none;padding-right:8px}
  .twk-num input{flex:1;min-width:0;height:100%;border:0;background:transparent;
    font:inherit;font-variant-numeric:tabular-nums;text-align:right;padding:0 8px 0 0;
    outline:none;color:inherit;-moz-appearance:textfield}
  .twk-num input::-webkit-inner-spin-button,.twk-num input::-webkit-outer-spin-button{
    -webkit-appearance:none;margin:0}
  .twk-num-unit{padding-right:8px;color:rgba(41,38,27,.45)}

  .twk-btn{appearance:none;height:26px;padding:0 12px;border:0;border-radius:7px;
    background:rgba(0,0,0,.78);color:#fff;font:inherit;font-weight:500;cursor:default}
  .twk-btn:hover{background:rgba(0,0,0,.88)}
  .twk-btn.secondary{background:rgba(0,0,0,.06);color:inherit}
  .twk-btn.secondary:hover{background:rgba(0,0,0,.1)}

  .twk-swatch{appearance:none;-webkit-appearance:none;width:56px;height:22px;
    border:.5px solid rgba(0,0,0,.1);border-radius:6px;padding:0;cursor:default;
    background:transparent;flex-shrink:0}
  .twk-swatch::-webkit-color-swatch-wrapper{padding:0}
  .twk-swatch::-webkit-color-swatch{border:0;border-radius:5.5px}
  .twk-swatch::-moz-color-swatch{border:0;border-radius:5.5px}
`;

// ── useTweaks ───────────────────────────────────────────────────────────────
// Single source of truth for tweak values. setTweak persists via the host
// (__edit_mode_set_keys → host rewrites the EDITMODE block on disk).
function useTweaks(defaults) {
  const [values, setValues] = React.useState(defaults);
  // Accepts either setTweak('key', value) or setTweak({ key: value, ... }) so a
  // useState-style call doesn't write a "[object Object]" key into the persisted
  // JSON block.
  const setTweak = React.useCallback((keyOrEdits, val) => {
    const edits = typeof keyOrEdits === 'object' && keyOrEdits !== null
      ? keyOrEdits : { [keyOrEdits]: val };
    setValues((prev) => ({ ...prev, ...edits }));
    window.parent.postMessage({ type: '__edit_mode_set_keys', edits }, '*');
  }, []);
  return [values, setTweak];
}

// ── TweaksPanel ─────────────────────────────────────────────────────────────
// Floating shell. Registers the protocol listener BEFORE announcing
// availability — if the announce ran first, the host's activate could land
// before our handler exists and the toolbar toggle would silently no-op.
// The close button posts __edit_mode_dismissed so the host's toolbar toggle
// flips off in lockstep; the host echoes __deactivate_edit_mode back which
// is what actually hides the panel.
function TweaksPanel({ title = 'Tweaks', children }) {
  const [open, setOpen] = React.useState(false);
  const dragRef = React.useRef(null);
  const offsetRef = React.useRef({ x: 16, y: 16 });
  const PAD = 16;

  const clampToViewport = React.useCallback(() => {
    const panel = dragRef.current;
    if (!panel) return;
    const w = panel.offsetWidth, h = panel.offsetHeight;
    const maxRight = Math.max(PAD, window.innerWidth - w - PAD);
    const maxBottom = Math.max(PAD, window.innerHeight - h - PAD);
    offsetRef.current = {
      x: Math.min(maxRight, Math.max(PAD, offsetRef.current.x)),
      y: Math.min(maxBottom, Math.max(PAD, offsetRef.current.y)),
    };
    panel.style.right = offsetRef.current.x + 'px';
    panel.style.bottom = offsetRef.current.y + 'px';
  }, []);

  React.useEffect(() => {
    if (!open) return;
    clampToViewport();
    if (typeof ResizeObserver === 'undefined') {
      window.addEventListener('resize', clampToViewport);
      return () => window.removeEventListener('resize', clampToViewport);
    }
    const ro = new ResizeObserver(clampToViewport);
    ro.observe(document.documentElement);
    return () => ro.disconnect();
  }, [open, clampToViewport]);

  React.useEffect(() => {
    const onMsg = (e) => {
      const t = e?.data?.type;
      if (t === '__activate_edit_mode') setOpen(true);
      else if (t === '__deactivate_edit_mode') setOpen(false);
    };
    window.addEventListener('message', onMsg);
    window.parent.postMessage({ type: '__edit_mode_available' }, '*');
    return () => window.removeEventListener('message', onMsg);
  }, []);

  const dismiss = () => {
    setOpen(false);
    window.parent.postMessage({ type: '__edit_mode_dismissed' }, '*');
  };

  const onDragStart = (e) => {
    const panel = dragRef.current;
    if (!panel) return;
    const r = panel.getBoundingClientRect();
    const sx = e.clientX, sy = e.clientY;
    const startRight = window.innerWidth - r.right;
    const startBottom = window.innerHeight - r.bottom;
    const move = (ev) => {
      offsetRef.current = {
        x: startRight - (ev.clientX - sx),
        y: startBottom - (ev.clientY - sy),
      };
      clampToViewport();
    };
    const up = () => {
      window.removeEventListener('mousemove', move);
      window.removeEventListener('mouseup', up);
    };
    window.addEventListener('mousemove', move);
    window.addEventListener('mouseup', up);
  };

  if (!open) return null;
  return (
    <>
      <style>{__TWEAKS_STYLE}</style>
      <div ref={dragRef} className="twk-panel"
           style={{ right: offsetRef.current.x, bottom: offsetRef.current.y }}>
        <div className="twk-hd" onMouseDown={onDragStart}>
          <b>{title}</b>
          <button className="twk-x" aria-label="Close tweaks"
                  onMouseDown={(e) => e.stopPropagation()}
                  onClick={dismiss}>✕</button>
        </div>
        <div className="twk-body">{children}</div>
      </div>
    </>
  );
}

// ── Layout helpers ──────────────────────────────────────────────────────────

function TweakSection({ label, children }) {
  return (
    <>
      <div className="twk-sect">{label}</div>
      {children}
    </>
  );
}

function TweakRow({ label, value, children, inline = false }) {
  return (
    <div className={inline ? 'twk-row twk-row-h' : 'twk-row'}>
      <div className="twk-lbl">
        <span>{label}</span>
        {value != null && <span className="twk-val">{value}</span>}
      </div>
      {children}
    </div>
  );
}

// ── Controls ────────────────────────────────────────────────────────────────

function TweakSlider({ label, value, min = 0, max = 100, step = 1, unit = '', onChange }) {
  return (
    <TweakRow label={label} value={`${value}${unit}`}>
      <input type="range" className="twk-slider" min={min} max={max} step={step}
             value={value} onChange={(e) => onChange(Number(e.target.value))} />
    </TweakRow>
  );
}

function TweakToggle({ label, value, onChange }) {
  return (
    <div className="twk-row twk-row-h">
      <div className="twk-lbl"><span>{label}</span></div>
      <button type="button" className="twk-toggle" data-on={value ? '1' : '0'}
              role="switch" aria-checked={!!value}
              onClick={() => onChange(!value)}><i /></button>
    </div>
  );
}

function TweakRadio({ label, value, options, onChange }) {
  const trackRef = React.useRef(null);
  const [dragging, setDragging] = React.useState(false);
  const opts = options.map((o) => (typeof o === 'object' ? o : { value: o, label: o }));
  const idx = Math.max(0, opts.findIndex((o) => o.value === value));
  const n = opts.length;

  // The active value is read by pointer-move handlers attached for the lifetime
  // of a drag — ref it so a stale closure doesn't fire onChange for every move.
  const valueRef = React.useRef(value);
  valueRef.current = value;

  const segAt = (clientX) => {
    const r = trackRef.current.getBoundingClientRect();
    const inner = r.width - 4;
    const i = Math.floor(((clientX - r.left - 2) / inner) * n);
    return opts[Math.max(0, Math.min(n - 1, i))].value;
  };

  const onPointerDown = (e) => {
    setDragging(true);
    const v0 = segAt(e.clientX);
    if (v0 !== valueRef.current) onChange(v0);
    const move = (ev) => {
      if (!trackRef.current) return;
      const v = segAt(ev.clientX);
      if (v !== valueRef.current) onChange(v);
    };
    const up = () => {
      setDragging(false);
      window.removeEventListener('pointermove', move);
      window.removeEventListener('pointerup', up);
    };
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', up);
  };

  return (
    <TweakRow label={label}>
      <div ref={trackRef} role="radiogroup" onPointerDown={onPointerDown}
           className={dragging ? 'twk-seg dragging' : 'twk-seg'}>
        <div className="twk-seg-thumb"
             style={{ left: `calc(2px + ${idx} * (100% - 4px) / ${n})`,
                      width: `calc((100% - 4px) / ${n})` }} />
        {opts.map((o) => (
          <button key={o.value} type="button" role="radio" aria-checked={o.value === value}>
            {o.label}
          </button>
        ))}
      </div>
    </TweakRow>
  );
}

function TweakSelect({ label, value, options, onChange }) {
  return (
    <TweakRow label={label}>
      <select className="twk-field" value={value} onChange={(e) => onChange(e.target.value)}>
        {options.map((o) => {
          const v = typeof o === 'object' ? o.value : o;
          const l = typeof o === 'object' ? o.label : o;
          return <option key={v} value={v}>{l}</option>;
        })}
      </select>
    </TweakRow>
  );
}

function TweakText({ label, value, placeholder, onChange }) {
  return (
    <TweakRow label={label}>
      <input className="twk-field" type="text" value={value} placeholder={placeholder}
             onChange={(e) => onChange(e.target.value)} />
    </TweakRow>
  );
}

function TweakNumber({ label, value, min, max, step = 1, unit = '', onChange }) {
  const clamp = (n) => {
    if (min != null && n < min) return min;
    if (max != null && n > max) return max;
    return n;
  };
  const startRef = React.useRef({ x: 0, val: 0 });
  const onScrubStart = (e) => {
    e.preventDefault();
    startRef.current = { x: e.clientX, val: value };
    const decimals = (String(step).split('.')[1] || '').length;
    const move = (ev) => {
      const dx = ev.clientX - startRef.current.x;
      const raw = startRef.current.val + dx * step;
      const snapped = Math.round(raw / step) * step;
      onChange(clamp(Number(snapped.toFixed(decimals))));
    };
    const up = () => {
      window.removeEventListener('pointermove', move);
      window.removeEventListener('pointerup', up);
    };
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', up);
  };
  return (
    <div className="twk-num">
      <span className="twk-num-lbl" onPointerDown={onScrubStart}>{label}</span>
      <input type="number" value={value} min={min} max={max} step={step}
             onChange={(e) => onChange(clamp(Number(e.target.value)))} />
      {unit && <span className="twk-num-unit">{unit}</span>}
    </div>
  );
}

function TweakColor({ label, value, onChange }) {
  return (
    <div className="twk-row twk-row-h">
      <div className="twk-lbl"><span>{label}</span></div>
      <input type="color" className="twk-swatch" value={value}
             onChange={(e) => onChange(e.target.value)} />
    </div>
  );
}

function TweakButton({ label, onClick, secondary = false }) {
  return (
    <button type="button" className={secondary ? 'twk-btn secondary' : 'twk-btn'}
            onClick={onClick}>{label}</button>
  );
}

Object.assign(window, {
  useTweaks, TweaksPanel, TweakSection, TweakRow,
  TweakSlider, TweakToggle, TweakRadio, TweakSelect,
  TweakText, TweakNumber, TweakColor, TweakButton,
});
/* ========== Shared utilities & primitives ========== */

const fmt$ = (n, digits = 0) => {
  if (!isFinite(n) || isNaN(n)) n = 0;
  return '$' + Math.round(n * Math.pow(10, digits)) / Math.pow(10, digits)
    .toLocaleString();
};
// Better $ formatter
const $ = (n, digits = 0) => {
  if (!isFinite(n) || isNaN(n)) n = 0;
  const opts = { minimumFractionDigits: digits, maximumFractionDigits: digits };
  return '$' + Math.abs(n).toLocaleString('en-US', opts) * (n < 0 ? -1 : 1) === 0
    ? '$' + (0).toLocaleString('en-US', opts)
    : (n < 0 ? '-$' : '$') + Math.abs(n).toLocaleString('en-US', opts);
};
const money = (n, digits = 0) => {
  if (!isFinite(n) || isNaN(n)) n = 0;
  const sign = n < 0 ? '-' : '';
  const opts = { minimumFractionDigits: digits, maximumFractionDigits: digits };
  return sign + '$' + Math.abs(n).toLocaleString('en-US', opts);
};
const pct = (n, digits = 2) => {
  if (!isFinite(n) || isNaN(n)) n = 0;
  return n.toFixed(digits) + '%';
};
const num = (n, digits = 0) => {
  if (!isFinite(n) || isNaN(n)) n = 0;
  return n.toLocaleString('en-US', { minimumFractionDigits: digits, maximumFractionDigits: digits });
};

// ----- Calculator state registry -----
window.__calcState = {};
window.__calcLoadState = {};
function useCalcState(calcId, initialState) {
  const loaded = window.__calcLoadState[calcId];
  const [s, setS] = useState(loaded || initialState);
  useEffect(() => {
    if (loaded) delete window.__calcLoadState[calcId];
  }, []);
  useEffect(() => { window.__calcState[calcId] = s; }, [s]);
  return [s, setS];
}

const SAVED_KEY = 'lot-saved-presentations';
function loadSaved() {
  try { return JSON.parse(localStorage.getItem(SAVED_KEY) || '[]'); } catch { return []; }
}
function persistSaved(arr) {
  try { localStorage.setItem(SAVED_KEY, JSON.stringify(arr)); } catch {}
  // Best-effort cloud sync (debounced inside firebase helper).
  const u = window.__firebase && window.__firebase.auth && window.__firebase.auth.currentUser;
  if (u) window.__firebase.savePresentationsCloud(u.uid, arr);
}

// ----- Branding settings (company logo, headshot, contact info) -----
const BRANDING_KEY = 'lot-branding-settings';
function loadBranding() {
  try { return JSON.parse(localStorage.getItem(BRANDING_KEY) || '{}'); } catch { return {}; }
}
function persistBranding(obj) {
  try { localStorage.setItem(BRANDING_KEY, JSON.stringify(obj)); }
  catch (e) { console.warn('Branding save failed (likely localStorage quota):', e); return false; }
  const u = window.__firebase && window.__firebase.auth && window.__firebase.auth.currentUser;
  if (u) window.__firebase.saveBrandingCloud(u.uid, obj);
  return true;
}
// Resize uploaded images client-side so headshots/logos fit in localStorage (~5MB cap).
function imageToDataUrl(file, maxDim, mime) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      const img = new Image();
      img.onload = () => {
        const scale = Math.min(1, maxDim / Math.max(img.width, img.height));
        const w = Math.max(1, Math.round(img.width * scale));
        const h = Math.max(1, Math.round(img.height * scale));
        const canvas = document.createElement('canvas');
        canvas.width = w; canvas.height = h;
        const ctx = canvas.getContext('2d');
        ctx.drawImage(img, 0, 0, w, h);
        try { resolve(canvas.toDataURL(mime || 'image/png', 0.9)); }
        catch (e) { reject(e); }
      };
      img.onerror = reject;
      img.src = reader.result;
    };
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}
window.loadBranding = loadBranding;
window.persistBranding = persistBranding;
window.imageToDataUrl = imageToDataUrl;

// ----- Mortgage math -----
function calcPI(principal, annualRate, years) {
  const r = (annualRate / 100) / 12;
  const n = years * 12;
  if (r === 0) return n > 0 ? principal / n : 0;
  if (n === 0) return 0;
  return principal * (r * Math.pow(1 + r, n)) / (Math.pow(1 + r, n) - 1);
}

function buildAmortization(principal, annualRate, years, extraMonthly = 0, extraYearly = 0, oneTime = 0, oneTimeMonth = 0) {
  const r = (annualRate / 100) / 12;
  const n = years * 12;
  const pi = calcPI(principal, annualRate, years);
  const rows = [];
  let balance = principal;
  let totalInterest = 0;
  for (let m = 1; m <= n && balance > 0.01; m++) {
    const interest = balance * r;
    let principalPay = pi - interest + extraMonthly;
    if (m === oneTimeMonth) principalPay += oneTime;
    if (m % 12 === 0) principalPay += extraYearly;
    if (principalPay > balance) principalPay = balance;
    balance -= principalPay;
    totalInterest += interest;
    rows.push({ month: m, payment: pi + extraMonthly, principal: principalPay, interest, balance, totalInterest });
  }
  return { rows, pi, totalInterest, payoffMonths: rows.length };
}

function buildArmAmortization(principal, initialRate, years, armInitial, armCap, armLifeCap, armIndex, armMargin) {
  const totalMonths = years * 12;
  const initMonths = armInitial * 12;
  const maxRate = initialRate + armLifeCap;
  const rows = [];
  let balance = principal;
  let totalInterest = 0;
  let curRate = initialRate;
  let pi = calcPI(principal, initialRate, years);

  for (let m = 1; m <= totalMonths && balance > 0.01; m++) {
    if (m <= initMonths) {
      // Initial fixed period — use initial rate
      curRate = initialRate;
    } else if ((m - initMonths - 1) % 12 === 0) {
      // Adjustment point: first month after initial period, then every 12 months
      const fullyIndexed = armIndex + armMargin;
      const adjCount = Math.floor((m - initMonths - 1) / 12) + 1;
      const adjRate = initialRate + Math.min(armCap * adjCount, armLifeCap);
      curRate = Math.min(adjRate, maxRate, Math.max(fullyIndexed, initialRate));
      curRate = Math.min(curRate, maxRate);
      // Recalculate payment for remaining term at new rate
      const remainYears = (totalMonths - m + 1) / 12;
      pi = calcPI(balance, curRate, remainYears);
    }

    const mRate = curRate / 100 / 12;
    const interest = balance * mRate;
    let principalPay = pi - interest;
    if (principalPay > balance) principalPay = balance;
    if (principalPay < 0) principalPay = 0;
    balance -= principalPay;
    totalInterest += interest;
    rows.push({ month: m, payment: pi, principal: principalPay, interest, balance, totalInterest, rate: curRate });
  }

  return { rows, pi: calcPI(principal, initialRate, years), totalInterest, payoffMonths: rows.length };
}

// ----- Primitives -----
const { useState, useEffect, useMemo, useRef, useCallback, Fragment } = React;

const Field = ({ label, hint, children, row }) => (
  <div className="field" style={row ? {marginBottom: 0} : {}}>
    {label && <div className="field-label"><span>{label}</span>{hint && <span className="field-hint">{hint}</span>}</div>}
    {children}
  </div>
);

const NumInput = ({ value, onChange, prefix, suffix, step = 1, min, max, placeholder }) => {
  const [local, setLocal] = useState(value);
  useEffect(() => { setLocal(value); }, [value]);
  return (
    <div className="input-wrap">
      {prefix && <span className="input-prefix">{prefix}</span>}
      <input
        type="text"
        inputMode="decimal"
        value={local === '' || local === null || local === undefined ? '' : local}
        placeholder={placeholder}
        onChange={(e) => {
          const v = e.target.value;
          setLocal(v);
          const n = parseFloat(v.replace(/,/g, ''));
          if (!isNaN(n)) onChange(n);
          else if (v === '') onChange(0);
        }}
        className={[prefix ? 'input-with-prefix' : '', suffix ? 'input-with-suffix' : ''].join(' ')}
      />
      {suffix && <span className="input-suffix">{suffix}</span>}
    </div>
  );
};

const Segmented = ({ value, onChange, options, full }) => (
  <div className={"seg" + (full ? " seg-full" : "")}>
    {options.map(o => (
      <button key={o.value} className={"seg-btn" + (value === o.value ? ' active' : '')}
        onClick={() => onChange(o.value)}>{o.label}</button>
    ))}
  </div>
);

const Toggle = ({ value, onChange, children }) => (
  <label className="toggle-row" style={{cursor:'pointer'}}>
    <input type="checkbox" checked={value} onChange={e => onChange(e.target.checked)} />
    {children}
  </label>
);

const KPI = ({ label, value, sub, accent, good, bad }) => (
  <div className={"kpi" + (accent ? ' accent' : '') + (good ? ' good' : '') + (bad ? ' bad' : '')}>
    <div className="kpi-label">{label}</div>
    <div className="kpi-value">{value}</div>
    {sub && <div className="kpi-sub">{sub}</div>}
  </div>
);

const Card = ({ title, sub, action, children, style, className }) => (
  <div className={'card' + (className ? ' ' + className : '')} style={style}>
    {(title || action) && (
      <div className="row-between" style={{marginBottom: sub ? 4 : 16}}>
        {title && <h3 className="card-title">{title}</h3>}
        {action}
      </div>
    )}
    {sub && <div className="card-sub">{sub}</div>}
    {children}
  </div>
);

const Tip = ({ children }) => (
  <div className="tip">
    <span className="tip-icon">
      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
        <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/>
        <circle cx="12" cy="12" r="4"/>
      </svg>
    </span>
    <div>{children}</div>
  </div>
);

// ----- Charts (custom SVG) -----
const ChartContainer = ({ height = 240, children, padding = {top: 16, right: 16, bottom: 28, left: 64}, onMove, onLeave, tooltip }) => {
  const ref = useRef(null);
  const svgRef = useRef(null);
  const [w, setW] = useState(600);
  useEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver(es => setW(es[0].contentRect.width));
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);
  const handleMove = (e) => {
    if (!onMove || !svgRef.current) return;
    const rect = svgRef.current.getBoundingClientRect();
    onMove(e.clientX - rect.left, e.clientY - rect.top);
  };
  return (
    <div ref={ref} className="chart-wrap" style={{width: '100%', height, position:'relative'}}>
      <svg ref={svgRef} width={w} height={height} className="chart"
        onMouseMove={handleMove} onMouseLeave={onLeave}>
        {typeof children === 'function' ? children({w, h: height, padding}) : children}
      </svg>
      {tooltip && (() => {
        // Flip tooltip below the point when it would clip the top of the chart.
        // Also nudge horizontally so the tooltip doesn't get cut off at left/right edges.
        const flipBelow = tooltip.y < 56;
        const tx = Math.max(60, Math.min(w - 60, tooltip.x));
        const ty = flipBelow ? tooltip.y + 14 : tooltip.y - 10;
        const yShift = flipBelow ? '0%' : '-100%';
        return (
          <div style={{position:'absolute', left: tx, top: ty, transform: `translate(-50%, ${yShift})`,
            background: 'var(--text)', color:'var(--bg-elev)', padding:'8px 12px', borderRadius: 8,
            fontSize: 12, fontWeight: 500, pointerEvents:'none', whiteSpace:'nowrap', boxShadow: 'var(--shadow-md)', zIndex: 5}}>
            {tooltip.content}
          </div>
        );
      })()}
    </div>
  );
};

// Line / area chart with multiple series + hover tooltip
const LineChart = ({ series, height = 260, yFormat = (v) => money(v), xFormat = (v) => v, smooth = true, area = true }) => {
  const [tt, setTT] = useState(null);
  const [dims, setDims] = useState(null);
  return (
    <ChartContainer height={height}
      onMove={(mx, my) => {
        if (!dims) return;
        const { padding, innerW, xMin, xMax, w, h, series: ss, ys } = dims;
        if (mx < padding.left || mx > w - padding.right) { setTT(null); return; }
        const xRatio = (mx - padding.left) / innerW;
        const xVal = xMin + xRatio * (xMax - xMin);
        // Snap to nearest x in first series
        let nearest = ss[0].data[0], best = Infinity;
        ss[0].data.forEach(p => { const d = Math.abs(p.x - xVal); if (d < best) { best = d; nearest = p; } });
        const items = ss.map(s => {
          const pt = s.data.find(p => p.x === nearest.x) || s.data[0];
          return { color: s.color, label: s.label, value: pt.y };
        });
        setTT({
          x: padding.left + ((nearest.x - xMin) / (xMax - xMin || 1)) * innerW,
          y: Math.min(...items.map(i => ys(i.value))),
          xLabel: xFormat(nearest.x),
          items,
        });
      }}
      onLeave={() => setTT(null)}
      tooltip={tt && {
        x: tt.x, y: tt.y,
        content: (
          <div>
            <div style={{fontSize: 11, opacity: 0.7, marginBottom: 4}}>{tt.xLabel}</div>
            {tt.items.map((it, i) => (
              <div key={i} style={{display:'flex', alignItems:'center', gap: 8}}>
                <span style={{width: 8, height: 8, borderRadius: 2, background: it.color}}></span>
                <span>{it.label}:</span>
                <span style={{fontWeight: 700, fontFamily:'JetBrains Mono'}}>{yFormat(it.value)}</span>
              </div>
            ))}
          </div>
        )
      }}>
      {({w, h, padding}) => {
        const innerW = w - padding.left - padding.right;
        const innerH = h - padding.top - padding.bottom;
        if (innerW <= 0 || !series.length) return null;

        const allPts = series.flatMap(s => s.data);
        const xMin = Math.min(...allPts.map(p => p.x));
        const xMax = Math.max(...allPts.map(p => p.x));
        const yMin = Math.min(0, ...allPts.map(p => p.y));
        const yMax = Math.max(...allPts.map(p => p.y), 1);

        const xs = (x) => padding.left + ((x - xMin) / (xMax - xMin || 1)) * innerW;
        const ys = (y) => padding.top + innerH - ((y - yMin) / (yMax - yMin || 1)) * innerH;

        // Stash dims for hover
        if (!dims || dims.w !== w) setTimeout(() => setDims({ padding, innerW, xMin, xMax, yMin, yMax, w, h, series, ys, xs }), 0);

        const yTicks = 4;
        const xTicks = Math.min(6, xMax - xMin);
        const pathFor = (data) => data.map((p, i) => `${i ? 'L' : 'M'} ${xs(p.x)} ${ys(p.y)}`).join(' ');

        return (
          <g>
            {Array.from({length: yTicks + 1}).map((_, i) => {
              const y = padding.top + (innerH * i) / yTicks;
              const v = yMax - ((yMax - yMin) * i) / yTicks;
              return (
                <g key={i}>
                  <line x1={padding.left} x2={w - padding.right} y1={y} y2={y} stroke="var(--border)" strokeDasharray="2,3" />
                  <text x={padding.left - 8} y={y + 4} fontSize="10.5" textAnchor="end" fill="var(--text-subtle)" fontFamily="JetBrains Mono">{yFormat(v)}</text>
                </g>
              );
            })}
            {Array.from({length: xTicks + 1}).map((_, i) => {
              const x = padding.left + (innerW * i) / xTicks;
              const v = xMin + ((xMax - xMin) * i) / xTicks;
              return (
                <text key={i} x={x} y={h - 8} fontSize="10.5" textAnchor="middle" fill="var(--text-subtle)" fontFamily="JetBrains Mono">{xFormat(Math.round(v))}</text>
              );
            })}
            {area && series.map((s, idx) => (
              <path key={'a'+idx} d={pathFor(s.data) + ` L ${xs(xMax)} ${ys(yMin)} L ${xs(xMin)} ${ys(yMin)} Z`}
                fill={s.color} opacity="0.12" />
            ))}
            {series.map((s, idx) => (
              <path key={'l'+idx} d={pathFor(s.data)} fill="none" stroke={s.color} strokeWidth={s.dashed ? 1.5 : 2.25} strokeLinejoin="round" strokeLinecap="round" {...(s.dashed ? {strokeDasharray: '6,4'} : {})} />
            ))}
            {tt && series.map((s, idx) => {
              const pt = s.data.find(p => xFormat(p.x) === tt.xLabel) || null;
              if (!pt) return null;
              return <circle key={'h'+idx} cx={xs(pt.x)} cy={ys(pt.y)} r="4.5" fill={s.color} stroke="var(--bg-elev)" strokeWidth="2" />;
            })}
            {tt && <line x1={tt.x} x2={tt.x} y1={padding.top} y2={padding.top + innerH} stroke="var(--text-subtle)" strokeDasharray="3,3" opacity="0.5" />}
          </g>
        );
      }}
    </ChartContainer>
  );
};

const BarChart = ({ data, height = 240, yFormat = (v) => money(v), color = 'var(--accent)', horizontal = false, showValues = true }) => {
  const [tt, setTT] = useState(null);
  const max = data.length ? Math.max(...data.map(d => d.value), 1) : 1;
  const maxLabel = yFormat(max);
  const dynLeft = Math.max(48, maxLabel.length * 7 + 12);
  return (
    <ChartContainer height={height} padding={{top: 16, right: 16, bottom: 28, left: dynLeft}}
      tooltip={tt}
      onLeave={() => setTT(null)}>
      {({w, h, padding}) => {
        const innerW = w - padding.left - padding.right;
        const innerH = h - padding.top - padding.bottom;
        if (!data.length) return null;
        const max = Math.max(...data.map(d => d.value), 1);
        const min = Math.min(0, ...data.map(d => d.value));
        const bw = innerW / data.length;
        const ys = (v) => padding.top + innerH - ((v - min) / (max - min || 1)) * innerH;
        return (
          <g>
            {[0, 0.25, 0.5, 0.75, 1].map((t, i) => {
              const v = max - (max - min) * t;
              const y = padding.top + innerH * t;
              return (
                <g key={i}>
                  <line x1={padding.left} x2={w - padding.right} y1={y} y2={y} stroke="var(--border)" strokeDasharray="2,3" />
                  <text x={padding.left - 8} y={y + 4} fontSize="10.5" textAnchor="end" fill="var(--text-subtle)" fontFamily="JetBrains Mono">{yFormat(v)}</text>
                </g>
              );
            })}
            {data.map((d, i) => {
              const x = padding.left + bw * i + bw * 0.15;
              const barW = bw * 0.7;
              const y0 = ys(0);
              const y1 = ys(d.value);
              return (
                <g key={i}
                  onMouseEnter={() => setTT({ x: x + barW/2, y: Math.min(y0, y1),
                    content: <div><div style={{fontSize:11, opacity:0.7}}>{d.label}</div><div style={{fontWeight:700, fontFamily:'JetBrains Mono'}}>{yFormat(d.value)}</div></div> })}
                  style={{cursor:'pointer'}}>
                  <rect x={x} y={Math.min(y0, y1)} width={barW} height={Math.abs(y1 - y0)} rx="6" fill={d.color || color} />
                  <rect x={x - 4} y={padding.top} width={barW + 8} height={innerH} fill="transparent" />
                  {showValues && Math.abs(y1 - y0) > 14 && (
                    <text x={x + barW/2} y={Math.min(y0, y1) - 6} fontSize="11" textAnchor="middle" fill="var(--text)" fontFamily="JetBrains Mono" fontWeight="600">
                      {yFormat(d.value)}
                    </text>
                  )}
                  <text x={x + barW/2} y={h - 10} fontSize="11" textAnchor="middle" fill="var(--text-muted)" fontWeight="500">{d.label}</text>
                </g>
              );
            })}
          </g>
        );
      }}
    </ChartContainer>
  );
};

// Chart.js-powered donut with native hover tooltips (label + value + %).
const Donut = ({ data, size = 180, thickness = 28, centerLabel, centerValue }) => {
  const canvasRef = useRef(null);
  const chartRef = useRef(null);
  const total = data.reduce((s, d) => s + Math.max(0, d.value), 0) || 1;
  // Re-create chart whenever the data shape/values change. JSON.stringify handles
  // nested {label,value,color} arrays without needing a deep-equal helper.
  const dataKey = JSON.stringify(data.map(d => [d.label, d.value, d.color]));

  useEffect(() => {
    if (!canvasRef.current || !window.Chart) return;
    if (chartRef.current) { chartRef.current.destroy(); chartRef.current = null; }

    // Resolve theme colors from CSS vars so the chart picks up light/dark themes.
    const css = getComputedStyle(document.documentElement);
    const cssVar = (name, fallback) => (css.getPropertyValue(name).trim() || fallback);
    // Canvas can't parse `var(--x)` — resolve any var() reference to a real color.
    const resolveColor = (c) => {
      if (typeof c !== 'string') return c;
      const m = c.match(/^var\(\s*(--[\w-]+)\s*(?:,\s*([^)]+))?\)$/);
      if (!m) return c;
      return css.getPropertyValue(m[1]).trim() || (m[2] ? m[2].trim() : '#94a3b8');
    };
    const bgElev = cssVar('--bg-elev', '#ffffff');
    const bgSunken = cssVar('--bg-sunken', '#f1f5f9');
    const textColor = cssVar('--text', '#0f172a');

    // cutout pixel = (size/2 - thickness). Convert to percent for Chart.js.
    const cutoutPx = Math.max(0, size / 2 - thickness);
    const cutoutPct = `${Math.round((cutoutPx / (size / 2)) * 100)}%`;

    // If every value is 0 show a single neutral "ring" so the donut isn't empty.
    const allZero = data.every(d => Math.max(0, d.value) === 0);
    const values = allZero ? [1] : data.map(d => Math.max(0, d.value));
    const colors = allZero ? [bgSunken] : data.map(d => resolveColor(d.color));
    const labels = allZero ? ['No data'] : data.map(d => d.label);

    chartRef.current = new window.Chart(canvasRef.current, {
      type: 'doughnut',
      data: {
        labels,
        datasets: [{
          data: values,
          backgroundColor: colors,
          borderColor: bgElev,
          borderWidth: 2,
          hoverOffset: 6,
          hoverBorderColor: bgElev,
        }],
      },
      options: {
        responsive: false,
        maintainAspectRatio: false,
        cutout: cutoutPct,
        animation: { duration: 500, easing: 'easeOutQuart' },
        plugins: {
          legend: { display: false },
          tooltip: {
            enabled: !allZero,
            backgroundColor: textColor,
            titleColor: bgElev,
            bodyColor: bgElev,
            titleFont: { size: 12, weight: '600', family: 'Inter, system-ui, sans-serif' },
            bodyFont: { size: 12, family: 'JetBrains Mono, ui-monospace, monospace' },
            padding: 10,
            cornerRadius: 8,
            displayColors: true,
            boxWidth: 8,
            boxHeight: 8,
            boxPadding: 4,
            callbacks: {
              label: (ctx) => {
                const v = ctx.parsed;
                const pct = total ? ((v / total) * 100).toFixed(1) : '0';
                const item = data[ctx.dataIndex];
                const formatted = item && item.format ? item.format(v) : window.money(v);
                return `  ${formatted}  (${pct}%)`;
              },
            },
          },
        },
      },
    });

    return () => { if (chartRef.current) { chartRef.current.destroy(); chartRef.current = null; } };
  }, [dataKey, size, thickness]);

  return (
    <div style={{display:'flex', alignItems:'center', gap: 24, flexWrap: 'wrap'}}>
      <div style={{position:'relative', width: size, height: size, flexShrink: 0}}>
        <canvas ref={canvasRef} width={size} height={size} style={{width: size, height: size}} />
        {centerValue && (
          <div style={{position:'absolute', top:0, left:0, width:size, height:size,
            display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center',
            pointerEvents:'none', textAlign:'center'}}>
            <div style={{fontSize:20, fontWeight:700, color:'var(--text)', fontFamily:'JetBrains Mono', lineHeight:1.1}}>{centerValue}</div>
            {centerLabel && <div style={{fontSize:11, color:'var(--text-muted)', fontWeight:500, marginTop:4}}>{centerLabel}</div>}
          </div>
        )}
      </div>
      <div style={{display:'flex', flexDirection:'column', gap: 8, flex: 1, minWidth: 160}}>
        {data.map((d, i) => (
          <div key={i} className="row-between" style={{fontSize: 13}}>
            <span className="row" style={{gap: 8}}>
              <span style={{width:10, height:10, borderRadius: 3, background: d.color}}></span>
              <span style={{color:'var(--text-muted)', fontWeight:500}}>{d.label}</span>
            </span>
            <span className="mono" style={{fontWeight:600}}>{d.format ? d.format(d.value) : money(d.value)}</span>
          </div>
        ))}
      </div>
    </div>
  );
};

const StackedBar = ({ segments, height = 16, format }) => {
  const total = segments.reduce((s, d) => s + Math.max(0, d.value), 0) || 1;
  return (
    <div>
      <div style={{display:'flex', height, borderRadius: height/2, overflow:'hidden', background:'var(--bg-sunken)'}}>
        {segments.map((s, i) => (
          <div key={i} style={{width: (Math.max(0, s.value)/total*100) + '%', background: s.color}} title={`${s.label}: ${(format||money)(s.value)}`} />
        ))}
      </div>
      <div className="legend" style={{marginTop: 10}}>
        {segments.map((s, i) => (
          <span key={i} className="legend-dot" style={{'--c': s.color}}>
            {s.label}: <span className="mono" style={{fontWeight:600, color:'var(--text)'}}>{(format||money)(s.value)}</span>
          </span>
        ))}
      </div>
    </div>
  );
};

// expose
Object.assign(window, {
  Field, NumInput, Segmented, Toggle, KPI, Card, Tip,
  LineChart, BarChart, Donut, StackedBar, ChartContainer,
  calcPI, buildAmortization,
  money, pct, num,
  useState, useEffect, useMemo, useRef, useCallback, Fragment,
});
/* Icons — minimal stroke icons */
const Icon = ({ name, size = 18 }) => {
  const props = { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" };
  const paths = {
    home: <><path d="M3 12l9-9 9 9"/><path d="M5 10v10h14V10"/></>,
    grid: <><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></>,
    bookmark: <><path d="M19 21l-7-5-7 5V5a2 2 0 012-2h10a2 2 0 012 2z"/></>,
    calc: <><rect x="4" y="3" width="16" height="18" rx="2"/><line x1="8" y1="7" x2="16" y2="7"/><line x1="8" y1="12" x2="8" y2="12"/><line x1="12" y1="12" x2="12" y2="12"/><line x1="16" y1="12" x2="16" y2="12"/><line x1="8" y1="16" x2="8" y2="16"/><line x1="12" y1="16" x2="12" y2="16"/><line x1="16" y1="16" x2="16" y2="16"/></>,
    compare: <><path d="M3 6h7M3 12h7M3 18h7"/><path d="M14 6h7M14 12h7M14 18h7"/><circle cx="6.5" cy="6" r="1"/><circle cx="17.5" cy="12" r="1"/></>,
    refresh: <><path d="M23 4v6h-6"/><path d="M1 20v-6h6"/><path d="M3.51 9a9 9 0 0114.85-3.36L23 10"/><path d="M20.49 15a9 9 0 01-14.85 3.36L1 14"/></>,
    badge: <><path d="M12 2l3 6 6 1-4.5 4 1 6-5.5-3-5.5 3 1-6L3 9l6-1z"/></>,
    bank: <><path d="M3 21h18M5 21V10M19 21V10M3 10h18L12 3z"/><path d="M9 21v-7M15 21v-7"/></>,
    plus: <><path d="M12 5v14M5 12h14"/></>,
    receipt: <><path d="M5 21V3h14v18l-3-2-3 2-3-2-3 2z"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="9" y1="12" x2="15" y2="12"/><line x1="9" y1="16" x2="13" y2="16"/></>,
    tools: <><path d="M14.7 6.3a3 3 0 014 4l-9 9-4 1 1-4z"/><path d="M5 14l-2 7 7-2"/></>,
    house: <><path d="M3 12l9-9 9 9v9a2 2 0 01-2 2h-4v-7H10v7H6a2 2 0 01-2-2z"/></>,
    trend: <><path d="M3 17l6-6 4 4 7-7"/><path d="M14 8h7v7"/></>,
    chart: <><line x1="3" y1="20" x2="21" y2="20"/><rect x="6" y="10" width="3" height="10"/><rect x="11" y="6" width="3" height="14"/><rect x="16" y="13" width="3" height="7"/></>,
    table: <><rect x="3" y="4" width="18" height="16" rx="2"/><line x1="3" y1="10" x2="21" y2="10"/><line x1="9" y1="4" x2="9" y2="20"/></>,
    dscr: <><circle cx="12" cy="12" r="9"/><path d="M12 6v6l4 2"/></>,
    settings: <><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 01-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 008 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H2a2 2 0 010-4h.09A1.65 1.65 0 003.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06a1.65 1.65 0 001.82.33H8a1.65 1.65 0 001-1.51V2a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V8a1.65 1.65 0 001.51 1H22a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></>,
    save: <><path d="M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></>,
    edit: <><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/><path d="M18.5 2.5a2.12 2.12 0 013 3L12 15l-4 1 1-4z"/></>,
    play: <><polygon points="6 4 20 12 6 20 6 4"/></>,
    sun: <><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></>,
    moon: <><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></>,
    menu: <><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></>,
    chev: <><polyline points="6 9 12 15 18 9"/></>,
    chevR: <><polyline points="9 6 15 12 9 18"/></>,
    copy: <><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></>,
    x: <><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="18" x2="18" y2="6"/></>,
    'third-party-link': <><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></>,
    download: <><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></>,
    phone: <><path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6 19.79 19.79 0 01-3.07-8.67A2 2 0 014.11 2h3a2 2 0 012 1.72c.13.96.37 1.9.72 2.81a2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.91.35 1.85.59 2.81.72A2 2 0 0122 16.92z"/></>,
    mail: <><rect x="2" y="4" width="20" height="16" rx="2"/><polyline points="22 6 12 13 2 6"/></>,
    upload: <><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></>,
    'log-out': <><path d="M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></>,
    'arrow-left': <><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></>,
    google: <><path d="M21.35 11.1H12v3.2h5.35c-.23 1.4-1.65 4.1-5.35 4.1-3.22 0-5.85-2.66-5.85-5.95s2.63-5.95 5.85-5.95c1.83 0 3.06.78 3.76 1.45l2.56-2.47C16.93 4.07 14.7 3 12 3 6.92 3 2.85 7.07 2.85 12s4.07 9 9.15 9c5.28 0 8.78-3.71 8.78-8.94 0-.6-.07-1.06-.13-1.96z" fill="currentColor" stroke="none"/></>,
    lock: <><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0110 0v4"/></>,
  };
  return <svg {...props}>{paths[name] || paths.calc}</svg>;
};
window.Icon = Icon;
/* ========== Calculators set 1: Mortgage, Loan Comparison, Refinance, Pre-Approval ========== */

const CHART_COLORS = ['var(--accent)', 'var(--c-violet)', 'var(--c-teal)', 'var(--c-amber)', 'var(--c-rose)', 'var(--c-emerald)'];

/* -------- 1. Mortgage Calculator -------- */
function MortgageCalc() {
  const [s, setS] = useCalcState('mortgage', {
    homePrice: 500000,
    downPayment: 100000,
    downPercent: 20,
    rate: 6.5,
    term: 30,
    rateType: 'Fixed',
    armInitial: 5,
    armCap: 2,
    armLifeCap: 5,
    armMargin: 2.75,
    armIndex: 4.5,
    tax: 3000,
    ins: 1200,
    hoa: 0,
    pmi: 0,
    downMode: '$',
  });
  const set = (k) => (v) => setS(prev => {
    const n = { ...prev, [k]: v };
    if (k === 'downPayment') n.downPercent = prev.homePrice ? (v / prev.homePrice) * 100 : 0;
    if (k === 'downPercent') n.downPayment = prev.homePrice * (v / 100);
    if (k === 'homePrice') n.downPayment = v * (prev.downPercent / 100);
    return n;
  });

  const loan = Math.max(0, s.homePrice - s.downPayment);
  const pi = calcPI(loan, s.rate, s.term);
  const pmi = s.pmi;
  const monthlyTax = s.tax / 12;
  const monthlyIns = s.ins / 12;
  const total = pi + monthlyTax + monthlyIns + s.hoa + pmi;
  const am = useMemo(() => {
    if (s.rateType === 'ARM') {
      return buildArmAmortization(loan, s.rate, s.term, s.armInitial, s.armCap, s.armLifeCap, s.armIndex, s.armMargin);
    }
    return buildAmortization(loan, s.rate, s.term);
  }, [loan, s.rate, s.term, s.rateType, s.armInitial, s.armCap, s.armLifeCap, s.armIndex, s.armMargin]);
  const totalInterest = am.totalInterest;

  // Worst case: calculate remaining balance after initial fixed period, then P&I at max rate for remaining term, plus PITI
  const worst = useMemo(() => {
    if (s.rateType !== 'ARM') return 0;
    const maxRate = s.rate + s.armLifeCap;
    const rInit = s.rate / 100 / 12;
    let bal = loan;
    for (let m = 0; m < s.armInitial * 12; m++) {
      const intM = bal * rInit;
      bal -= (pi - intM);
    }
    const remainTerm = s.term - s.armInitial;
    const worstPI = calcPI(bal, maxRate, remainTerm);
    return worstPI + monthlyTax + monthlyIns + s.hoa + pmi;
  }, [s.rateType, s.rate, s.armLifeCap, s.armInitial, s.term, loan, pi, monthlyTax, monthlyIns, s.hoa, pmi]);

  // Chart data: balance + cumulative interest
  const series = useMemo(() => {
    const yearly = [];
    for (let y = 0; y <= s.term; y++) {
      const idx = Math.min(am.rows.length - 1, y * 12 - 1);
      if (y === 0) yearly.push({ x: 0, balance: loan, interest: 0 });
      else if (idx >= 0) yearly.push({ x: y, balance: am.rows[idx].balance, interest: am.rows[idx].totalInterest });
    }
    return [
      { color: 'var(--accent)', label: 'Remaining balance', data: yearly.map(d => ({x: d.x, y: d.balance})) },
      { color: 'var(--c-amber)', label: 'Cumulative interest', data: yearly.map(d => ({x: d.x, y: d.interest})) },
    ];
  }, [am, loan, s.term]);

  return (
    <div className="split fade-in">
      <Card title="Loan Details" sub="Enter the loan information">
        <Field label="Home Price"><NumInput prefix="$" value={s.homePrice} onChange={set('homePrice')} /></Field>
        <div className="field-row">
          <Field label="Down Payment"><NumInput prefix="$" value={Math.round(s.downPayment)} onChange={set('downPayment')} /></Field>
          <Field label="Down %"><NumInput suffix="%" value={Number(s.downPercent.toFixed(2))} onChange={set('downPercent')} /></Field>
        </div>
        <div className="field-row">
          <Field label="Interest Rate"><NumInput suffix="%" value={s.rate} onChange={set('rate')} /></Field>
          <Field label="Loan Term"><NumInput suffix="yr" value={s.term} onChange={set('term')} /></Field>
        </div>
        <Field label="Rate Type">
          <Segmented full value={s.rateType} onChange={set('rateType')} options={[{label:'Fixed',value:'Fixed'},{label:'ARM',value:'ARM'}]} />
        </Field>
        {s.rateType === 'ARM' && (
          <div style={{padding: 12, background: 'var(--bg-sunken)', borderRadius: 'var(--radius-md)', marginBottom: 14}}>
            <div className="field-label" style={{marginBottom: 8}}>ARM Settings</div>
            <div className="field-row">
              <Field label="Initial Period"><NumInput suffix="yr" value={s.armInitial} onChange={set('armInitial')} /></Field>
              <Field label="Adj Cap"><NumInput suffix="%" value={s.armCap} onChange={set('armCap')} /></Field>
            </div>
            <div className="field-row">
              <Field label="Life Cap"><NumInput suffix="%" value={s.armLifeCap} onChange={set('armLifeCap')} /></Field>
              <Field label="Margin"><NumInput suffix="%" value={s.armMargin} onChange={set('armMargin')} /></Field>
            </div>
            <Field label="Index Rate (e.g. SOFR)"><NumInput suffix="%" value={s.armIndex} onChange={set('armIndex')} /></Field>
          </div>
        )}
        <div className="field-row">
          <Field label="Property Tax / Year"><NumInput prefix="$" value={s.tax} onChange={set('tax')} /></Field>
          <Field label="Insurance / Year"><NumInput prefix="$" value={s.ins} onChange={set('ins')} /></Field>
        </div>
        <div className="field-row">
          <Field label="HOA / Month"><NumInput prefix="$" value={s.hoa} onChange={set('hoa')} /></Field>
          <Field label="PMI / Month"><NumInput prefix="$" value={s.pmi} onChange={set('pmi')} /></Field>
        </div>
      </Card>

      <div className="results-grid">
        <Tip><strong>How This Calculator Works</strong><br/>
<strong>Monthly Payment (PITI):</strong> Combines Principal &amp; Interest with Taxes, Insurance, HOA, and PMI. The amortization curve traces how your balance falls and interest grows — the crossover point is when you've paid as much in interest as you have remaining principal.</Tip>

        <Card>
          <div className="kpi-grid">
            <KPI accent label="Total Monthly" value={money(total, 2)} sub="Full PITI payment" />
            <KPI label="Principal & Interest" value={money(pi, 2)} />
            <KPI label="Property Tax" value={money(monthlyTax, 2)} />
            <KPI label="Insurance" value={money(monthlyIns, 2)} />
            <KPI label="Other (HOA + PMI)" value={money(s.hoa + pmi, 2)} />
            {s.rateType === 'ARM' && <KPI bad label="Worst Case" value={money(worst, 2)} sub={`Max rate: ${(s.rate + s.armLifeCap).toFixed(2)}%`} />}
          </div>
        </Card>

        <div className="g2" style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap: 20}}>
          <Card title="Payment Breakdown">
            <Donut size={170} thickness={26} centerLabel="per month" centerValue={money(total, 0)}
              data={[
                {label:'Principal & Interest', value: pi, color: 'var(--accent)'},
                {label:'Property Tax', value: monthlyTax, color: 'var(--c-violet)'},
                {label:'Insurance', value: monthlyIns, color: 'var(--c-teal)'},
                {label:'HOA', value: s.hoa, color: 'var(--c-amber)'},
                {label:'PMI', value: pmi, color: 'var(--c-rose)'},
              ]} />
          </Card>
          <Card title="Loan Summary">
            <table className="tbl" style={{fontSize: 13}}>
              <tbody>
                <tr><td style={{color:'var(--text-muted)'}}>Loan Amount</td><td className="num">{money(loan)}</td></tr>
                <tr><td style={{color:'var(--text-muted)'}}>Total Interest</td><td className="num">{money(totalInterest)}</td></tr>
                <tr><td style={{color:'var(--text-muted)'}}>Total P&I</td><td className="num">{money(loan + totalInterest)}</td></tr>
                <tr><td style={{color:'var(--text-muted)'}}>Total Cost (w/ Tax+Ins)</td><td className="num">{money(total * s.term * 12)}</td></tr>
              </tbody>
            </table>
          </Card>
        </div>

        <Card title="Amortization Over Time" sub="Balance reduction and cumulative interest">
          <LineChart series={series} height={280} xFormat={v => 'Yr ' + v} yFormat={v => '$' + (v/1000).toFixed(0) + 'k'} />
          <div className="legend">
            <span className="legend-dot" style={{'--c': 'var(--accent)'}}>Remaining balance</span>
            <span className="legend-dot" style={{'--c': 'var(--c-amber)'}}>Cumulative interest</span>
          </div>
        </Card>
      </div>
    </div>
  );
}

/* -------- 2. Loan Comparison -------- */
function ComparisonCalc() {
  const newScenario = (over = {}) => ({
    name: 'Scenario',
    product: 'Conventional',
    loanType: 'Purchase',
    rateType: 'Fixed',
    propertyValue: 500000,
    downPayment: 100000,
    downPct: 20,
    currentBalance: 400000,
    cashout: 0,
    armInitial: 5,
    armCap: 2,
    armLifeCap: 5,
    armMargin: 2.75,
    armIndex: 4.5,
    lenderFees: 2000,
    otherClosing: 3000,
    rate: 6.5,
    manualAPR: 0,
    term: 30,
    points: 0,
    upfrontMIP: 0,
    financeMIP: true,
    tax: 3000,
    ins: 1200,
    hoa: 0,
    mi: 0,
    appreciation: 3,
    extra: 0,
    ...over,
  });
  const loadedComp = window.__calcLoadState['comparison'];
  const [scenarios, setScenarios] = useState(loadedComp ? loadedComp.scenarios : [
    newScenario({name:'Scenario A', downPct: 20, downPayment: 100000, lenderFees: 2000, otherClosing: 6000}),
    newScenario({name:'Scenario B', rate: 6.25, downPct: 30, downPayment: 150000, otherClosing: 6000}),
  ]);
  const [horizon, setHorizon] = useState(loadedComp ? loadedComp.horizon : 15);
  useEffect(() => { if (loadedComp) delete window.__calcLoadState['comparison']; }, []);
  useEffect(() => { window.__calcState['comparison'] = { scenarios, horizon }; }, [scenarios, horizon]);

  const update = (i, k, v) => setScenarios(arr => arr.map((s, idx) => {
    if (idx !== i) return s;
    const n = {...s, [k]: v};
    if (k === 'downPayment') n.downPct = s.propertyValue ? (v/s.propertyValue)*100 : 0;
    if (k === 'downPct') n.downPayment = s.propertyValue * (v/100);
    if (k === 'propertyValue') n.downPayment = v * (s.downPct/100);
    if (k === 'product' && v === 'VA') n.mi = 0;
    return n;
  }));
  const dup = (i) => setScenarios(arr => arr.length < 4 ? [...arr, {...arr[i], name: arr[i].name + ' copy'}] : arr);
  const remove = (i) => setScenarios(arr => arr.length > 1 ? arr.filter((_, idx) => idx !== i) : arr);

  const computed = scenarios.map(s => {
    const baseLoan = s.loanType === 'Purchase' ? s.propertyValue - s.downPayment : s.currentBalance + s.cashout;
    const upfront = s.financeMIP ? baseLoan * (s.upfrontMIP/100) : 0;
    const loan = baseLoan + upfront;
    const pi = calcPI(loan, s.rate, s.term);
    const total = pi + s.tax/12 + s.ins/12 + s.hoa + s.mi;
    const buildScenarioAmortization = () => {
      if (s.rateType !== 'ARM') return buildAmortization(loan, s.rate, s.term, s.extra);
      const months = s.term * 12;
      const initialMonths = Math.min(months, s.armInitial * 12);
      const initialRate = s.rate / 100;
      const initialMonthlyRate = initialRate / 12;
      const initialPI = calcPI(loan, s.rate, s.term);
      const lifetimeMaxRate = initialRate + (s.armLifeCap / 100);
      const targetRate = (s.armIndex + s.armMargin) / 100;
      let activeAnnualRate = initialRate;
      let activePI = initialPI;
      let balance = loan;
      let totalInterest = 0;
      const rows = [];
      for (let m = 1; m <= months && balance > 0.01; m++) {
        if (m > initialMonths && (m - initialMonths) % 12 === 1) {
          const upper = Math.min(activeAnnualRate + s.armCap / 100, lifetimeMaxRate);
          const lower = Math.max(activeAnnualRate - s.armCap / 100, initialRate);
          activeAnnualRate = Math.min(Math.max(targetRate, lower), upper);
          const remainingMonths = months - m + 1;
          const r = activeAnnualRate / 12;
          activePI = balance * (r * Math.pow(1 + r, remainingMonths)) / (Math.pow(1 + r, remainingMonths) - 1);
        }
        const interest = balance * (activeAnnualRate / 12);
        let principalPay = activePI - interest + s.extra;
        if (principalPay > balance) principalPay = balance;
        balance -= principalPay;
        totalInterest += interest;
        rows.push({ month: m, payment: activePI + s.extra, principal: principalPay, interest, balance: Math.max(0, balance), totalInterest });
      }
      return { rows, pi: initialPI, totalInterest, payoffMonths: rows.length };
    };
    const am = buildScenarioAmortization();
    const calcWorstCaseBalanceAtHorizon = () => {
      if (s.rateType !== 'ARM') return null;
      const months = s.term * 12;
      const initialMonths = Math.min(months, s.armInitial * 12);
      const horizonMonths = Math.min(months, horizon * 12);
      let balance = loan;
      const initialPI = calcPI(loan, s.rate, s.term);
      const initialMonthlyRate = (s.rate / 100) / 12;
      for (let m = 1; m <= Math.min(initialMonths, horizonMonths) && balance > 0.01; m++) {
        const interest = balance * initialMonthlyRate;
        balance -= Math.min(balance, initialPI - interest + s.extra);
      }
      if (horizonMonths > initialMonths && balance > 0.01) {
        const maxRate = s.rate + s.armLifeCap;
        // Competitor's net-worth view stress-tests the post-initial ARM balance with a max-rate recast,
        // while the worst-case payment card separately shows the max-rate remaining-term PITIA payment.
        const stressedPI = calcPI(balance, maxRate, s.term);
        const stressedMonthlyRate = (maxRate / 100) / 12;
        for (let m = initialMonths + 1; m <= horizonMonths && balance > 0.01; m++) {
          const interest = balance * stressedMonthlyRate;
          balance -= Math.min(balance, stressedPI - interest + s.extra);
        }
      }
      return Math.max(0, balance);
    };
    const futureValue = s.propertyValue * Math.pow(1 + s.appreciation/100, horizon);
    const remainingIdx = Math.min(am.rows.length - 1, horizon*12 - 1);
    const scheduledRemaining = remainingIdx >= 0 ? am.rows[remainingIdx].balance : 0;
    const stressRemaining = calcWorstCaseBalanceAtHorizon();
    const remaining = stressRemaining === null ? scheduledRemaining : stressRemaining;
    const netWorth = futureValue - remaining;
    const pointsCost = (s.points/100)*loan;
    const totalCash = s.loanType === 'Purchase'
      ? s.downPayment + s.lenderFees + s.otherClosing + pointsCost
      : s.lenderFees + s.otherClosing + pointsCost - s.cashout;
    const totalCost = total * s.term * 12;
    // Worst-case ARM: competitor calculates the max-rate payment after the initial fixed period,
    // using the remaining balance and remaining term, then adds monthly PITIA.
    const worstRate = s.rate + s.armLifeCap;
    let worstPayment = 0;
    if (s.rateType === 'ARM') {
      const initialMonths = Math.min(s.armInitial * 12, s.term * 12);
      const remainingRows = buildAmortization(loan, s.rate, s.term).rows;
      const remainingBalance = initialMonths > 0 && remainingRows[initialMonths - 1]
        ? remainingRows[initialMonths - 1].balance
        : loan;
      const remainingYears = Math.max(1 / 12, s.term - s.armInitial);
      worstPayment = calcPI(remainingBalance, worstRate, remainingYears) + s.tax/12 + s.ins/12 + s.hoa + s.mi;
    }
    // APR calc via bisection
    let apr = s.rate;
    if (s.manualAPR > 0) {
      apr = s.manualAPR;
    } else {
      const totalFees = s.lenderFees + pointsCost;
      if (totalFees > 0 && loan > 0) {
        let lo = s.rate * 0.8, hi = s.rate * 2;
        for (let i = 0; i < 50; i++) {
          const mid = (lo + hi) / 2;
          const testPmt = calcPI(loan - totalFees, mid, s.term);
          if (testPmt < pi) lo = mid; else hi = mid;
        }
        apr = (lo + hi) / 2;
      }
    }
    return { s, loan, pi, total, am, futureValue, remaining, netWorth, totalCash, totalCost, apr, pointsCost, worstRate, worstPayment, payoffYears: am.payoffMonths/12 };
  });

  return (
    <div className="fade-in">
      <Tip><strong>How This Calculator Works</strong><br/>
<strong>Monthly Payment:</strong> Includes principal, interest, taxes, insurance, HOA, and PMI for a complete side-by-side comparison of each scenario's true monthly cost.<br/>
<strong>Net Worth Projection:</strong> Estimates future home equity by factoring in property appreciation and mortgage paydown over the selected timeline. Use this to see which scenario builds the most wealth long-term.</Tip>

      <Card className="comp-inputs" style={{marginBottom: 20}}>
        <div style={{display:'flex',alignItems:'center',gap:12,flexWrap:'wrap'}}>
          <span style={{fontWeight:600,fontSize:14}}>Net Worth Timeline:</span>
          <div style={{display:'flex',alignItems:'center',gap:8,flexShrink:0}}>
            <NumInput value={horizon} onChange={v => setHorizon(v)} suffix="" style={{width:60}} />
            <span style={{fontSize:14}}>years</span>
          </div>
          <span style={{fontSize:12,color:'var(--text-muted)', flex:1}}>Calculate net worth projection at this timeframe</span>
          {scenarios.length < 4 && (
            <button className="btn btn-primary" style={{flexShrink:0, display:'inline-flex', alignItems:'center', justifyContent:'center', gap:6, padding:'7px 16px', fontSize:13, fontWeight:600, borderRadius:8}} onClick={() => setScenarios(a => [...a, newScenario({name:'Scenario ' + String.fromCharCode(65 + a.length)})])}>
              <Icon name="plus" size={14} /> Add Scenario
            </button>
          )}
        </div>
      </Card>

      <div className="comp-inputs" style={{display:'grid', gridTemplateColumns: `repeat(${scenarios.length}, minmax(200px, 1fr))`, gap: 16, overflowX:'auto', paddingBottom: 8}}>
        {scenarios.map((sc, i) => (
          <ScenarioCard key={i} sc={sc} idx={i} update={update} dup={() => dup(i)} remove={() => remove(i)} canRemove={scenarios.length > 1} color={CHART_COLORS[i]} comp={computed[i]} />
        ))}
      </div>

      {/* Winner Cards */}
      <div className="g3" style={{marginTop: 20, display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap: 16}}>
        {(() => {
          const winners = [
            {label: 'Lowest Monthly Payment', key: 'total', fmt: v => money(v, 2)},
            {label: 'Lowest Cash to Close', key: 'totalCash', fmt: v => money(v)},
            {label: 'Lowest Total Cost', key: 'totalInterest', fmt: v => money(v), sub: 'Total interest over loan life'},
          ];
          return winners.map(w => {
            const valOf = (c) => w.key === 'totalInterest' ? c.am.totalInterest : c[w.key];
            const best = computed.reduce((min, c, i) => valOf(c) < min.val ? {val: valOf(c), idx: i, name: c.s.name} : min, {val: Infinity, idx: 0, name: ''});
            return (
              <div key={w.label} className="card" style={{padding: 16, textAlign:'center', borderTop: `3px solid ${CHART_COLORS[best.idx]}`}}>
                <div style={{fontSize: 11, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: 1, marginBottom: 8}}>{w.label}</div>
                <div style={{fontSize: 18, fontWeight: 700, color: CHART_COLORS[best.idx]}}>{best.name}</div>
                <div className="mono" style={{fontSize: 14, marginTop: 4}}>{w.fmt(best.val)}</div>
                {w.sub && <div style={{fontSize: 11, color: 'var(--text-muted)', marginTop: 4}}>{w.sub}</div>}
              </div>
            );
          });
        })()}
      </div>

      <div className="g2" style={{marginTop: 24, display:'grid', gridTemplateColumns:'1fr 1fr', gap: 20}}>
        <Card title="Monthly Payment Comparison">
          <BarChart data={computed.map((c, i) => ({label: c.s.name, value: c.total, color: CHART_COLORS[i]}))} height={220} />
        </Card>
        <Card title="Total Interest Comparison">
          <BarChart data={computed.map((c, i) => ({label: c.s.name, value: c.am.totalInterest, color: CHART_COLORS[i]}))} height={220} />
        </Card>
      </div>

      <div style={{marginTop: 20}}>
        <Card title={`Net Worth at ${horizon} Years`} sub="Future home value minus remaining loan balance"
          action={
            <div className="row" style={{gap: 8}}>
              <span className="field-hint">Timeline:</span>
              <input type="range" min={1} max={30} value={horizon} onChange={e => setHorizon(+e.target.value)} style={{width: 140}} />
              <span className="mono" style={{fontWeight: 600, fontSize: 12}}>{horizon}y</span>
            </div>
          }>
          <BarChart data={computed.map((c, i) => ({label: c.s.name, value: c.netWorth, color: CHART_COLORS[i]}))} height={240} />
          <div className="g2" style={{display:'grid', gridTemplateColumns: `repeat(${computed.length}, 1fr)`, gap: 12, marginTop: 16}}>
            {computed.map((c, i) => (
              <div key={i} style={{padding: 12, background: 'var(--bg-sunken)', borderRadius: 8, borderLeft: `3px solid ${CHART_COLORS[i]}`}}>
                <div style={{fontWeight: 700, fontSize: 13, marginBottom: 8, color: CHART_COLORS[i]}}>{c.s.name}</div>
                <div style={{fontSize: 12, color: 'var(--text-muted)', marginBottom: 2}}>Home Value: <span className="mono" style={{color:'var(--text)'}}>{money(c.futureValue)}</span></div>
                <div style={{fontSize: 12, color: 'var(--text-muted)', marginBottom: 2}}>Remaining Bal: <span className="mono" style={{color:'var(--text)'}}>{money(c.remaining)}</span></div>
                <div style={{fontSize: 12, color: 'var(--text-muted)'}}>Net Worth: <span className="mono" style={{color:'var(--good)', fontWeight: 700}}>{money(c.netWorth)}</span></div>
              </div>
            ))}
          </div>
        </Card>
      </div>

      <div style={{marginTop: 20}}>
        <Card title="Detailed Comparison">
          <div style={{overflowX: 'auto'}}>
            <table className="tbl" style={{width:'100%'}}>
              <thead><tr>
                <th>Metric</th>
                {computed.map((c, i) => <th key={i} style={{color: CHART_COLORS[i], textAlign:'right'}}>{c.s.name}</th>)}
              </tr></thead>
              <tbody>
                {/* Loan Details */}
                <tr><td colSpan={1 + computed.length} style={{fontWeight:700, fontSize:11, textTransform:'uppercase', letterSpacing:1, color:'var(--text-muted)', paddingTop:12, paddingBottom:4, borderBottom:'1px solid var(--border)'}}>Loan Details</td></tr>
                {[
                  ['Loan Type', c => c.s.loanType === 'Purchase' ? 'Purchase' : 'Refi'],
                  ['Product', c => c.s.product],
                  ['Rate Type', c => c.s.rateType],
                  ['Home Price / Value', c => money(c.s.propertyValue)],
                  ['Down Payment', c => c.s.loanType === 'Purchase' ? money(c.s.downPayment) + ' (' + c.s.downPct.toFixed(1) + '%)' : 'N/A'],
                  ['Loan Amount', c => money(c.loan)],
                  ['Interest Rate', c => pct(c.s.rate)],
                  ['APR', c => pct(c.apr)],
                  ['Loan Term', c => c.s.term + ' yr'],
                ].map(([label, fn]) => (
                  <tr key={label}><td style={{color:'var(--text-muted)'}}>{label}</td>
                    {computed.map((c, i) => <td key={i} className="num">{fn(c)}</td>)}
                  </tr>
                ))}

                {/* Monthly Payments */}
                <tr><td colSpan={1 + computed.length} style={{fontWeight:700, fontSize:11, textTransform:'uppercase', letterSpacing:1, color:'var(--text-muted)', paddingTop:16, paddingBottom:4, borderBottom:'1px solid var(--border)'}}>Monthly Payments</td></tr>
                {[
                  ['Principal & Interest', c => money(c.pi, 2), {background:'rgba(16,185,129,0.08)'}],
                  ['Property Tax', c => money(c.s.tax/12, 2), null],
                  ['Home Insurance', c => money(c.s.ins/12, 2), null],
                  ['HOA', c => money(c.s.hoa, 2), null],
                  ['Mortgage Insurance', c => money(c.s.mi, 2), null],
                  ['Total Monthly Payment', c => money(c.total, 2), {background:'rgba(16,185,129,0.08)', fontWeight:700}],
                ].map(([label, fn, style]) => (
                  <tr key={label} style={style || {}}><td style={{color:'var(--text-muted)', fontWeight: style && style.fontWeight ? style.fontWeight : 'normal'}}>{label}</td>
                    {computed.map((c, i) => <td key={i} className="num" style={{fontWeight: style && style.fontWeight ? style.fontWeight : 'normal'}}>{fn(c)}</td>)}
                  </tr>
                ))}

                {/* ARM Details - only if any scenario is ARM */}
                {computed.some(c => c.s.rateType === 'ARM') && (
                  <>
                    <tr><td colSpan={1 + computed.length} style={{fontWeight:700, fontSize:11, textTransform:'uppercase', letterSpacing:1, color:'var(--text-muted)', paddingTop:16, paddingBottom:4, borderBottom:'1px solid var(--border)'}}>ARM Details</td></tr>
                    <tr><td style={{color:'var(--text-muted)'}}>Worst-Case Rate</td>
                      {computed.map((c, i) => <td key={i} className="num">{c.s.rateType === 'ARM' ? pct(c.worstRate) : 'N/A'}</td>)}
                    </tr>
                    <tr><td style={{color:'var(--text-muted)'}}>Worst-Case Payment</td>
                      {computed.map((c, i) => <td key={i} className="num">{c.s.rateType === 'ARM' ? money(c.worstPayment, 2) : 'N/A'}</td>)}
                    </tr>
                  </>
                )}

                {/* Costs */}
                <tr><td colSpan={1 + computed.length} style={{fontWeight:700, fontSize:11, textTransform:'uppercase', letterSpacing:1, color:'var(--text-muted)', paddingTop:16, paddingBottom:4, borderBottom:'1px solid var(--border)'}}>Costs</td></tr>
                {[
                  ['Points (%)', c => pct(c.s.points), null],
                  ['Points ($)', c => money(c.pointsCost), null],
                  ['Lender Fees', c => money(c.s.lenderFees), null],
                  ['Other Closing Costs', c => money(c.s.otherClosing), null],
                  ['Cash to Close', c => money(c.totalCash), {background:'rgba(245,158,11,0.08)'}],
                ].map(([label, fn, style]) => (
                  <tr key={label} style={style || {}}><td style={{color:'var(--text-muted)', fontWeight: label === 'Cash to Close' ? 700 : 'normal'}}>{label}</td>
                    {computed.map((c, i) => <td key={i} className="num" style={{fontWeight: label === 'Cash to Close' ? 700 : 'normal'}}>{fn(c)}</td>)}
                  </tr>
                ))}

                {/* Lifetime */}
                <tr><td colSpan={1 + computed.length} style={{fontWeight:700, fontSize:11, textTransform:'uppercase', letterSpacing:1, color:'var(--text-muted)', paddingTop:16, paddingBottom:4, borderBottom:'1px solid var(--border)'}}>Lifetime</td></tr>
                {[
                  ['Total Interest', c => money(c.am.totalInterest), null],
                  ['Total Cost', c => money(c.totalCost), {background:'rgba(245,158,11,0.08)'}],
                  ['Extra Payments (/mo)', c => money(c.s.extra), null],
                  ['Payoff', c => c.am.payoffMonths + ' mo (' + c.payoffYears.toFixed(1) + ' yr)', null],
                ].map(([label, fn, style]) => (
                  <tr key={label} style={style || {}}><td style={{color:'var(--text-muted)', fontWeight: label === 'Total Cost' ? 700 : 'normal'}}>{label}</td>
                    {computed.map((c, i) => <td key={i} className="num" style={{fontWeight: label === 'Total Cost' ? 700 : 'normal'}}>{fn(c)}</td>)}
                  </tr>
                ))}

                {/* Net Worth */}
                <tr><td colSpan={1 + computed.length} style={{fontWeight:700, fontSize:11, textTransform:'uppercase', letterSpacing:1, color:'var(--text-muted)', paddingTop:16, paddingBottom:4, borderBottom:'1px solid var(--border)'}}>{'Net Worth (' + horizon + 'yr)'}</td></tr>
                {[
                  ['Home Value', c => money(c.futureValue), null],
                  ['Remaining Balance', c => money(c.remaining), null],
                  ['Net Worth', c => money(c.netWorth), {background:'rgba(16,185,129,0.08)'}],
                ].map(([label, fn, style]) => (
                  <tr key={label} style={style || {}}><td style={{color:'var(--text-muted)', fontWeight: label === 'Net Worth' ? 700 : 'normal'}}>{label}</td>
                    {computed.map((c, i) => <td key={i} className="num" style={{fontWeight: label === 'Net Worth' ? 700 : 'normal'}}>{fn(c)}</td>)}
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </Card>
      </div>

    </div>
  );
}

function ScenarioCard({ sc, idx, update, dup, remove, canRemove, color, comp }) {
  return (
    <div className="card" style={{borderTop: `3px solid ${color}`, padding: 18}}>
      <div className="row-between" style={{marginBottom: 14}}>
        <input value={sc.name} onChange={e => update(idx, 'name', e.target.value)}
          style={{fontFamily: 'inherit', fontWeight: 700, fontSize: 14, border:'none', background:'transparent', padding: 0, color: 'var(--text)', minWidth: 0, flex: 1}} />
        <div className="row" style={{gap:4, flexShrink: 0}}>
          <button className="btn btn-ghost btn-sm" onClick={dup} title="Duplicate"><Icon name="copy" size={14}/></button>
          {canRemove && <button className="btn btn-ghost btn-sm" onClick={remove} title="Remove"><Icon name="x" size={14}/></button>}
        </div>
      </div>
      <Field label="Loan Product">
        <select value={sc.product} onChange={e => update(idx, 'product', e.target.value)}>
          <option>Conventional</option><option>FHA</option><option>VA</option><option>USDA</option><option>Non-QM</option>
        </select>
      </Field>
      <Field label="Loan Type">
        <Segmented full value={sc.loanType} onChange={v=>update(idx,'loanType',v)} options={[{label:'Purchase',value:'Purchase'},{label:'Refi',value:'Refinance'}]} />
      </Field>
      <Field label="Rate Type">
        <Segmented full value={sc.rateType} onChange={v=>update(idx,'rateType',v)} options={[{label:'Fixed',value:'Fixed'},{label:'ARM',value:'ARM'}]} />
      </Field>
      {sc.loanType === 'Purchase' ? (
        <>
          <Field label="Property Value"><NumInput prefix="$" value={sc.propertyValue} onChange={v=>update(idx,'propertyValue',v)} /></Field>
          <Field label="Down $"><NumInput prefix="$" value={Math.round(sc.downPayment)} onChange={v=>update(idx,'downPayment',v)} /></Field>
          <Field label="Down %"><NumInput suffix="%" value={Number(sc.downPct.toFixed(2))} onChange={v=>update(idx,'downPct',v)} /></Field>
        </>
      ) : (
        <>
          <Field label="Property Value"><NumInput prefix="$" value={sc.propertyValue} onChange={v=>update(idx,'propertyValue',v)} /></Field>
          <Field label="Current Balance"><NumInput prefix="$" value={sc.currentBalance} onChange={v=>update(idx,'currentBalance',v)} /></Field>
          <Field label="Cashout"><NumInput prefix="$" value={sc.cashout} onChange={v=>update(idx,'cashout',v)} /></Field>
        </>
      )}
      {sc.rateType === 'ARM' && (
        <div style={{padding: 10, background: 'var(--bg-sunken)', borderRadius: 8, marginBottom: 12}}>
          <div className="field-label" style={{marginBottom: 8, fontSize: 11}}>ARM Settings</div>
          <Field label="Initial Period"><NumInput suffix="yr" value={sc.armInitial} onChange={v=>update(idx,'armInitial',v)} /></Field>
          <Field label="Adj Cap"><NumInput suffix="%" value={sc.armCap} onChange={v=>update(idx,'armCap',v)} /></Field>
          <Field label="Life Cap"><NumInput suffix="%" value={sc.armLifeCap} onChange={v=>update(idx,'armLifeCap',v)} /></Field>
          <Field label="Margin"><NumInput suffix="%" value={sc.armMargin} onChange={v=>update(idx,'armMargin',v)} /></Field>
          <Field label="Index Rate"><NumInput suffix="%" value={sc.armIndex} onChange={v=>update(idx,'armIndex',v)} /></Field>
        </div>
      )}
      <Field label="Rate"><NumInput suffix="%" value={sc.rate} onChange={v=>update(idx,'rate',v)} /></Field>
      <Field label="Term"><NumInput suffix="yr" value={sc.term} onChange={v=>update(idx,'term',v)} /></Field>
      <Field label="Points"><NumInput suffix="%" value={sc.points} onChange={v=>update(idx,'points',v)} /></Field>
      <Field label="Manual APR"><NumInput suffix="%" value={sc.manualAPR} onChange={v=>update(idx,'manualAPR',v)} /></Field>
      <Field label="Lender Fees"><NumInput prefix="$" value={sc.lenderFees} onChange={v=>update(idx,'lenderFees',v)} /></Field>
      <Field label="Other Closing"><NumInput prefix="$" value={sc.otherClosing} onChange={v=>update(idx,'otherClosing',v)} /></Field>
      <Field label="Upfront/MIP %"><NumInput suffix="%" value={sc.upfrontMIP} onChange={v=>update(idx,'upfrontMIP',v)} /></Field>
      <Field label="Mortgage Ins/mo"><NumInput prefix="$" value={sc.mi} onChange={v=>update(idx,'mi',v)} /></Field>
      <Toggle value={sc.financeMIP} onChange={v=>update(idx,'financeMIP',v)}>Finance MIP into loan</Toggle>
      <Field label="Tax/yr"><NumInput prefix="$" value={sc.tax} onChange={v=>update(idx,'tax',v)} /></Field>
      <Field label="Ins/yr"><NumInput prefix="$" value={sc.ins} onChange={v=>update(idx,'ins',v)} /></Field>
      <Field label="HOA/mo"><NumInput prefix="$" value={sc.hoa} onChange={v=>update(idx,'hoa',v)} /></Field>
      <Field label="Extra/mo"><NumInput prefix="$" value={sc.extra} onChange={v=>update(idx,'extra',v)} /></Field>
      <Field label="Appreciation/yr"><NumInput suffix="%" value={sc.appreciation} onChange={v=>update(idx,'appreciation',v)} /></Field>
      {comp && (
        <div style={{marginTop: 14, padding: 12, background: 'var(--bg-sunken)', borderRadius: 8}}>
          <div className="g2" style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap: 8}}>
            <div>
              <div style={{fontSize: 10, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: 0.5}}>Monthly Payment</div>
              <div className="mono" style={{fontSize: 15, fontWeight: 700}}>{money(comp.total, 2)}</div>
            </div>
            <div>
              <div style={{fontSize: 10, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: 0.5}}>Rate</div>
              <div className="mono" style={{fontSize: 15, fontWeight: 700}}>{pct(comp.s.rate)}</div>
            </div>
            {comp.s.rateType === 'ARM' && (
              <div>
                <div style={{fontSize: 10, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: 0.5}}>Worst Case Payment</div>
                <div className="mono" style={{fontSize: 15, fontWeight: 700, color: 'var(--bad)'}}>{money(comp.worstPayment, 2)}</div>
              </div>
            )}
            {comp.s.rateType === 'ARM' && (
              <div>
                <div style={{fontSize: 10, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: 0.5}}>Max Rate</div>
                <div className="mono" style={{fontSize: 15, fontWeight: 700, color: 'var(--bad)'}}>{pct(comp.worstRate)}</div>
              </div>
            )}
            <div>
              <div style={{fontSize: 10, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: 0.5}}>APR</div>
              <div className="mono" style={{fontSize: 15, fontWeight: 700}}>{pct(comp.apr)}</div>
            </div>
            <div>
              <div style={{fontSize: 10, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: 0.5}}>Cash to Close</div>
              <div className="mono" style={{fontSize: 15, fontWeight: 700}}>{money(comp.totalCash)}</div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* -------- 3. Refinance Calculator -------- */
function RefinanceCalc() {
  const [s, setS] = useCalcState('refinance', {
    balance: 500000, currRate: 7.0, currPay: 3326, yearsLeft: 28,
    newRate: 5.5, newTerm: 30, closing: 5000, rollIn: true, cashout: 0, includeTI: false,
    tax: 3000, ins: 1200,
  });
  const set = k => v => setS(p => ({...p, [k]: v}));

  const newLoan = s.balance + s.cashout + (s.rollIn ? s.closing : 0);
  const newPI = calcPI(newLoan, s.newRate, s.newTerm);
  const ti = (s.tax + s.ins) / 12;
  const newPay = newPI + (s.includeTI ? ti : 0);
  const currPayCalc = s.currPay + (s.includeTI ? ti : 0);
  const monthlySavings = currPayCalc - newPay;
  const currTotalInterest = (s.currPay * s.yearsLeft * 12) - s.balance;
  const newAm = buildAmortization(newLoan, s.newRate, s.newTerm);
  const interestSavings = currTotalInterest - newAm.totalInterest;
  const breakEven = monthlySavings > 0 ? Math.ceil(s.closing / monthlySavings) : 0;
  const lifetimeNet = (currPayCalc * s.yearsLeft * 12) - (newPay * s.newTerm * 12);

  const series = useMemo(() => {
    const savings = [], costs = [];
    const maxYears = Math.min(s.newTerm, 10);
    const steps = maxYears * 2; // every 0.5 years
    for (let i = 0; i <= steps; i++) {
      const yr = i * 0.5;
      const months = yr * 12;
      savings.push({x: yr, y: monthlySavings * months});
      costs.push({x: yr, y: s.closing});
    }
    return [
      {color: 'var(--good)', label: 'Cumulative Savings', data: savings},
      {color: 'var(--c-amber)', label: 'Closing Costs', data: costs, dashed: true},
    ];
  }, [monthlySavings, s.closing, s.newTerm]);

  return (
    <div className="split fade-in">
      <div>
        <Card title="Current Loan" style={{marginBottom: 16}}>
          <Field label="Current Balance"><NumInput prefix="$" value={s.balance} onChange={set('balance')} /></Field>
          <div className="field-row">
            <Field label="Current Rate"><NumInput suffix="%" value={s.currRate} onChange={set('currRate')} /></Field>
            <Field label="Current Payment"><NumInput prefix="$" value={s.currPay} onChange={set('currPay')} /></Field>
          </div>
          <Field label="Years Remaining"><NumInput suffix="yr" value={s.yearsLeft} onChange={set('yearsLeft')} /></Field>
          <Toggle value={s.includeTI} onChange={set('includeTI')}>Include taxes & insurance</Toggle>
          {s.includeTI && (
            <div className="field-row">
              <Field label="Property Tax"><NumInput prefix="$" suffix="/yr" value={s.tax} onChange={set('tax')} /></Field>
              <Field label="Insurance"><NumInput prefix="$" suffix="/yr" value={s.ins} onChange={set('ins')} /></Field>
            </div>
          )}
        </Card>
        <Card title="New Loan">
          <div className="field-row">
            <Field label="New Rate"><NumInput suffix="%" value={s.newRate} onChange={set('newRate')} /></Field>
            <Field label="New Term"><NumInput suffix="yr" value={s.newTerm} onChange={set('newTerm')} /></Field>
          </div>
          <Field label="Closing Costs"><NumInput prefix="$" value={s.closing} onChange={set('closing')} /></Field>
          <Toggle value={s.rollIn} onChange={set('rollIn')}>Roll closing costs into loan</Toggle>
          <Field label="Cash Out"><NumInput prefix="$" value={s.cashout} onChange={set('cashout')} /></Field>
        </Card>
      </div>

      <div className="results-grid">
        <Tip><strong>How This Calculator Works</strong><br/>
<strong>Monthly Savings:</strong> Compares your current monthly payment against the new refinanced payment. The difference is your monthly savings (or increase if shortening the term).<br/>
<strong>Break-Even Point:</strong> Calculates how long it takes for your cumulative monthly savings to offset the closing costs of the refinance. After that point, every dollar saved is pure benefit.</Tip>

        <div className="kpi-grid">
          <KPI accent label="New Monthly Payment" value={money(newPay, 2)} sub="P&I" />
          <KPI good={monthlySavings > 0} bad={monthlySavings < 0} label="Monthly Savings"
            value={(monthlySavings >= 0 ? '↓ ' : '↑ ') + money(Math.abs(monthlySavings), 2)}
            sub={monthlySavings >= 0 ? "Lower payment" : "Higher payment"} />
          <KPI label="Interest Savings" value={money(interestSavings)} sub="Over loan life" />
          <KPI label="Break-Even" value={breakEven > 0 ? breakEven + ' mo' : '—'} sub="Months to recoup" />
          <KPI good label="Lifetime Net" value={money(lifetimeNet)} sub="After closing costs" />
        </div>

        <div className="g2" style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap: 20}}>
          <Card title="Payment Comparison">
            <BarChart data={[
              {label: 'Current', value: currPayCalc, color: 'var(--c-rose)'},
              {label: 'New', value: newPay, color: 'var(--accent)'},
            ]} height={200} />
          </Card>
          <Card title="Total Interest">
            <BarChart data={[
              {label: 'Current', value: currTotalInterest, color: 'var(--c-rose)'},
              {label: 'New', value: newAm.totalInterest, color: 'var(--accent)'},
            ]} height={200} />
          </Card>
        </div>

        <Card title="Break-Even Analysis" sub="When monthly savings offset closing costs">
          <LineChart series={series} height={260} xFormat={v => v.toFixed(1)} yFormat={v => '$' + (v/1000).toFixed(0) + 'k'} />
          <div className="legend">
            <span className="legend-dot" style={{'--c': 'var(--good)'}}>Cumulative Savings</span>
            <span className="legend-dot" style={{'--c': 'var(--c-amber)'}}>Closing Costs</span>
          </div>
          {monthlySavings > 0 && breakEven > 0 && (
            <div style={{marginTop: 12, padding: '10px 16px', background: 'var(--accent-soft)', borderRadius: 'var(--radius-md)', fontSize: 13}}>
              <strong>Break-even in {(breakEven/12).toFixed(1)} years</strong> ({breakEven} months). After that, you'll save {money(monthlySavings, 2)} monthly. Totaling <strong>{money(lifetimeNet)}</strong> in savings over the loan life.
            </div>
          )}
        </Card>

        <Card title="The Bottom Line">
          <div style={{padding: 16, background: 'var(--accent-soft)', borderRadius: 'var(--radius-md)', fontSize: 14}}>
            {monthlySavings > 0 && breakEven > 0 ? (
              <>You'll save <strong className="mono">{money(monthlySavings, 2)}/month</strong> and break even in <strong>{breakEven} months</strong>. Over the life of the loan, you'll save <strong className="mono">{money(lifetimeNet)}</strong> after closing costs.</>
            ) : (
              <>This refinance increases your payment by <strong className="mono">{money(Math.abs(monthlySavings), 2)}/month</strong>. Consider only if the term reduction or cash-out is the goal.</>
            )}
          </div>
        </Card>

        <Card title="Refinance Summary" sub="Current vs. new loan side-by-side">
          <div>
            <table className="tbl" style={{tableLayout:'fixed', width:'100%'}}>
              <thead><tr>
                <th></th>
                <th style={{color:'var(--c-rose)', textAlign:'right'}}>Current</th>
                <th style={{color:'var(--accent)', textAlign:'right'}}>Refinance</th>
              </tr></thead>
              <tbody>
                <tr><td style={{color:'var(--text-muted)'}}>Monthly Payment</td><td className="num">{money(currPayCalc, 2)}</td><td className="num" style={{fontWeight:700}}>{money(newPay, 2)}</td></tr>
                <tr><td style={{color:'var(--text-muted)'}}>Loan Amount</td><td className="num">{money(s.balance)}</td><td className="num">{money(newLoan)}</td></tr>
                <tr><td style={{color:'var(--text-muted)'}}>Total Interest</td><td className="num">{money(currTotalInterest)}</td><td className="num">{money(newAm.totalInterest)}</td></tr>
                <tr><td style={{color:'var(--text-muted)'}}>Remaining</td><td className="num">{s.yearsLeft * 12} mo</td><td className="num">{s.newTerm * 12} mo</td></tr>
                <tr><td style={{color:'var(--text-muted)'}}>Closing Costs</td><td className="num">&mdash;</td><td className="num">{money(s.closing)}{s.rollIn ? ' (Rolled in)' : ''}</td></tr>
              </tbody>
            </table>
          </div>
        </Card>
      </div>
    </div>
  );
}

/* -------- 4. Pre-Approval -------- */
function PreApprovalCalc() {
  const [s, setS] = useCalcState('preapproval', {
    income: 75000, debts: 500, minDown: 20, maxFront: 28, maxBack: 36,
    rate: 6.5, term: 30, monthlyTax: 250, monthlyIns: 100,
  });
  const set = k => v => setS(p => ({...p, [k]: v}));

  const monthlyIncome = s.income / 12;
  const maxHousing = monthlyIncome * (s.maxFront / 100);
  const maxTotal = monthlyIncome * (s.maxBack / 100) - s.debts;
  const maxPITI = Math.min(maxHousing, maxTotal);
  const maxPI = Math.max(0, maxPITI - s.monthlyTax - s.monthlyIns);

  const r = (s.rate/100)/12;
  const n = s.term * 12;
  const maxLoan = r > 0 ? maxPI * (Math.pow(1+r, n) - 1) / (r * Math.pow(1+r, n)) : maxPI * n;
  const maxPrice = maxLoan / (1 - s.minDown/100);
  const downAmount = maxPrice * (s.minDown/100);

  const housingDTI = maxPI > 0 ? (maxPITI / monthlyIncome) * 100 : 0;
  const totalDTI = maxPI > 0 ? ((maxPITI + s.debts) / monthlyIncome) * 100 : 0;

  return (
    <div className="split fade-in">
      <div>
        <Card title="Borrower Information" sub="Income and debt details" style={{marginBottom: 16}}>
          <Field label="Annual Income" hint={`Monthly: ${money(monthlyIncome)}`}><NumInput prefix="$" value={s.income} onChange={set('income')} /></Field>
          <Field label="Monthly Debts" hint="Cards, loans, etc."><NumInput prefix="$" value={s.debts} onChange={set('debts')} /></Field>
        </Card>
        <Card title="Loan Program" style={{marginBottom: 16}}>
          <Field label="Min Down Payment %" hint="Conv 5–20%, FHA 3.5%, VA 0%"><NumInput suffix="%" value={s.minDown} onChange={set('minDown')} /></Field>
          <div className="field-row">
            <Field label="Max Housing DTI"><NumInput suffix="%" value={s.maxFront} onChange={set('maxFront')} /></Field>
            <Field label="Max Total DTI"><NumInput suffix="%" value={s.maxBack} onChange={set('maxBack')} /></Field>
          </div>
        </Card>
        <Card title="Loan Parameters">
          <div className="field-row">
            <Field label="Interest Rate"><NumInput suffix="%" value={s.rate} onChange={set('rate')} /></Field>
            <Field label="Term (yr)"><NumInput value={s.term} onChange={set('term')} /></Field>
          </div>
          <div className="field-row">
            <Field label="Tax / mo"><NumInput prefix="$" value={s.monthlyTax} onChange={set('monthlyTax')} /></Field>
            <Field label="Insurance / mo"><NumInput prefix="$" value={s.monthlyIns} onChange={set('monthlyIns')} /></Field>
          </div>
        </Card>
      </div>

      <div className="results-grid">
        <Tip><strong>How This Calculator Works</strong><br/>
<strong>Front-End DTI:</strong> Limits your total housing payment (principal, interest, taxes &amp; insurance) to a percentage of your gross monthly income.<br/>
<strong>Back-End DTI:</strong> Limits your total monthly debt obligations (housing + car loans, credit cards, student loans, etc.) to a percentage of your gross monthly income. The calculator uses the more conservative of the two limits.</Tip>

        <Card>
          <div className="kpi-grid">
            <KPI accent label="Max Home Price" value={money(maxPrice)} sub="Based on income & DTI" />
            <KPI label="Max Loan Amount" value={money(maxLoan)} />
            <KPI label="Down Payment" value={money(downAmount)} sub={`${s.minDown}% min`} />
          </div>
        </Card>

        <div className="g2" style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap: 20}}>
          <Card title="Payment Breakdown">
            <table className="tbl">
              <tbody>
                <tr><td>Principal & Interest</td><td className="num">{money(maxPI, 2)}</td></tr>
                <tr><td>Property Tax</td><td className="num">{money(s.monthlyTax, 2)}</td></tr>
                <tr><td>Insurance</td><td className="num">{money(s.monthlyIns, 2)}</td></tr>
                <tr style={{fontWeight: 800, background:'rgba(99,102,241,0.10)', borderTop:'2px solid var(--accent)'}}><td>Total Monthly Payment (PITI)</td><td className="num">{money(maxPITI, 2)}</td></tr>
              </tbody>
            </table>
          </Card>
          <Card title="DTI Analysis">
            <div style={{marginBottom: 18}}>
              <div className="row-between" style={{marginBottom: 6}}>
                <span style={{fontSize: 12, color:'var(--text-muted)', fontWeight: 600}}>Housing DTI</span>
                <span className="mono" style={{fontWeight: 700}}>{pct(housingDTI, 1)}</span>
              </div>
              <div style={{height: 10, background: 'var(--bg-sunken)', borderRadius: 5, overflow:'hidden'}}>
                <div style={{width: `${Math.min(100, (housingDTI/s.maxFront)*100)}%`, height:'100%', background: housingDTI > s.maxFront ? 'var(--bad)' : 'var(--good)'}}></div>
              </div>
              <div className="field-hint" style={{marginTop: 4}}>Max {s.maxFront}%</div>
            </div>
            <div>
              <div className="row-between" style={{marginBottom: 6}}>
                <span style={{fontSize: 12, color:'var(--text-muted)', fontWeight: 600}}>Total DTI</span>
                <span className="mono" style={{fontWeight: 700}}>{pct(totalDTI, 1)}</span>
              </div>
              <div style={{height: 10, background: 'var(--bg-sunken)', borderRadius: 5, overflow:'hidden'}}>
                <div style={{width: `${Math.min(100, (totalDTI/s.maxBack)*100)}%`, height:'100%', background: totalDTI > s.maxBack ? 'var(--bad)' : 'var(--good)'}}></div>
              </div>
              <div className="field-hint" style={{marginTop: 4}}>Max {s.maxBack}%</div>
            </div>
          </Card>
        </div>

        <Card title="Quick Reference: Common Programs" style={{background:'linear-gradient(180deg, rgba(99,102,241,0.06), var(--bg-elev))'}}>
          <div style={{display:'grid', gap: 12}}>
            {[
              ['Conventional Loan', 'Down: 5-20% • Front DTI: 28% • Back DTI: 36%'],
              ['FHA Loan', 'Down: 3.5% • Front DTI: 31% • Back DTI: 43%'],
              ['VA Loan', 'Down: 0% • Front DTI: N/A • Back DTI: 41%'],
              ['USDA Loan', 'Down: 0% • Front DTI: 29% • Back DTI: 41%'],
            ].map(([program, detail]) => (
              <div key={program} style={{padding: '12px 14px', border:'1px solid var(--border)', borderRadius: 10, background:'var(--bg-elev)'}}>
                <div style={{fontWeight: 700, fontSize: 13, marginBottom: 6}}>{program}</div>
                <div style={{fontSize: 13, color:'var(--text-muted)'}}>{detail}</div>
              </div>
            ))}
          </div>
        </Card>
      </div>
    </div>
  );
}

Object.assign(window, { MortgageCalc, ComparisonCalc, RefinanceCalc, PreApprovalCalc, CHART_COLORS });
/* ========== Calculators set 2: HELOC, Extra Payment, Closing Costs, Fix & Flip ========== */

/* -------- 5. HELOC -------- */
function HELOCCalc() {
  const [s, setS] = useCalcState('heloc', {
    homeValue: 400000, mortgageBalance: 250000, maxLTV: 85, helocAmount: 90000,
    rate: 8.5, drawYears: 10, repayYears: 20,
  });
  const set = k => v => setS(p => ({...p, [k]: v}));

  const maxTotal = s.homeValue * (s.maxLTV/100);
  const available = Math.max(0, maxTotal - s.mortgageBalance);
  const equity = Math.max(0, s.homeValue - s.mortgageBalance);
  const equityPct = s.homeValue ? (equity / s.homeValue) * 100 : 0;
  const drawPay = s.helocAmount * (s.rate/100) / 12;
  const repayPay = calcPI(s.helocAmount, s.rate, s.repayYears);
  const drawInterest = drawPay * 12 * s.drawYears;
  const repayInterest = (repayPay * 12 * s.repayYears) - s.helocAmount;
  const totalInterest = drawInterest + repayInterest;
  const maxLoan = Math.max(0, s.homeValue * (s.maxLTV / 100) - s.mortgageBalance);

  const series = useMemo(() => {
    const data = [];
    let bal = s.helocAmount;
    for (let y = 0; y <= s.drawYears + s.repayYears; y++) {
      if (y <= s.drawYears) data.push({x: y, payment: drawPay, balance: bal});
      else {
        const idx = (y - s.drawYears) * 12 - 1;
        const am = buildAmortization(s.helocAmount, s.rate, s.repayYears);
        bal = idx >= 0 && idx < am.rows.length ? am.rows[idx].balance : 0;
        data.push({x: y, payment: repayPay, balance: bal});
      }
    }
    return [{color: 'var(--accent)', label: 'Monthly payment', data: data.map(d => ({x: d.x, y: d.payment}))}];
  }, [s, drawPay, repayPay]);

  return (
    <div className="split fade-in">
      <Card title="Property & HELOC Details">
        <Field label="Home Value"><NumInput prefix="$" value={s.homeValue} onChange={set('homeValue')} /></Field>
        <Field label="Mortgage Balance"><NumInput prefix="$" value={s.mortgageBalance} onChange={set('mortgageBalance')} /></Field>
        <Field label="Max LTV %" hint="Typically 80–85%"><NumInput suffix="%" value={s.maxLTV} onChange={set('maxLTV')} /></Field>
        <Field label="HELOC Amount"><NumInput prefix="$" value={s.helocAmount} onChange={set('helocAmount')} /></Field>
        <Field label="Interest Rate" hint="Variable estimate"><NumInput suffix="%" value={s.rate} onChange={set('rate')} /></Field>
        <div className="field-row">
          <Field label="Draw Period" hint="Interest-only"><NumInput suffix="yr" value={s.drawYears} onChange={set('drawYears')} /></Field>
          <Field label="Repayment" hint="P+I"><NumInput suffix="yr" value={s.repayYears} onChange={set('repayYears')} /></Field>
        </div>
      </Card>

      <div className="results-grid">
        <Tip><strong>How This Calculator Works</strong><br/>
<strong>Draw Period:</strong> During this phase (typically 10 years), you can borrow against your line of credit and make interest-only payments on the amount drawn.<br/>
<strong>Repayment Period:</strong> After the draw period ends, you can no longer borrow and must repay the outstanding balance with fully amortizing principal &amp; interest payments over the remaining term.</Tip>

        <div className="kpi-grid">
          <KPI accent label="Available Equity" value={money(available)} sub={`Up to ${pct(s.maxLTV, 0)} LTV`} />
          <KPI label="Max Loan Amount" value={money(maxLoan)} sub="Based on LTV" />
          <KPI label="Current Equity" value={money(equity)} sub={pct(equityPct, 1)} />
          <KPI label="Draw Payment" value={money(drawPay, 2)} sub="Interest only" />
          <KPI label="Repayment" value={money(repayPay, 2)} sub="Principal + interest" />
        </div>

        <Card title="Equity Position" sub={`${pct(equityPct, 1)} equity in your home`}>
          <StackedBar height={20}
            segments={[
              {label: 'Mortgage', value: s.mortgageBalance, color: 'var(--c-rose)'},
              {label: 'HELOC', value: s.helocAmount, color: 'var(--c-amber)'},
              {label: 'Remaining Equity', value: Math.max(0, equity - s.helocAmount), color: 'var(--good)'},
            ]} />
        </Card>

        <div className="g2" style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap: 20}}>
          <Card title="Payment Timeline" sub="Draw → Repayment">
            <LineChart series={series} height={220} xFormat={v => 'Yr ' + v} yFormat={v => money(v)} area={false} />
          </Card>
          <Card title="Cost Breakdown">
            <Donut size={170} thickness={26} centerLabel="total" centerValue={money(s.helocAmount + totalInterest)}
              data={[
                {label: 'Principal', value: s.helocAmount, color: 'var(--accent)'},
                {label: 'Draw Interest', value: drawInterest, color: 'var(--c-amber)'},
                {label: 'Repay Interest', value: repayInterest, color: 'var(--c-rose)'},
              ]} />
          </Card>
        </div>

        <Card title="Cost Summary">
          <table className="tbl">
            <tbody>
              <tr><td>HELOC Amount</td><td className="num">{money(s.helocAmount)}</td></tr>
              <tr><td>Interest (Draw)</td><td className="num">{money(drawInterest)}</td></tr>
              <tr><td>Interest (Repay)</td><td className="num">{money(repayInterest)}</td></tr>
              <tr style={{fontWeight: 700}}><td>Total Interest</td><td className="num">{money(totalInterest)}</td></tr>
              <tr style={{fontWeight: 700}}><td>Total Paid</td><td className="num">{money(s.helocAmount + totalInterest)}</td></tr>
            </tbody>
          </table>
        </Card>

        <Card title="Important HELOC Considerations">
          <ul style={{fontSize:13,color:'var(--text-muted)',lineHeight:1.8,paddingLeft:18}}>
            <li>HELOCs typically have variable interest rates that can change over time</li>
            <li>Draw period allows access to funds with interest-only payments</li>
            <li>Repayment period requires full principal + interest payments</li>
            <li>Your home serves as collateral — failure to repay could result in foreclosure</li>
            <li>Some HELOCs may have annual fees or closing costs</li>
            <li>Interest may be tax-deductible if used for home improvements (consult tax advisor)</li>
          </ul>
        </Card>
      </div>
    </div>
  );
}

/* -------- 6. Extra Payment -------- */
function ExtraPaymentCalc() {
  const [s, setS] = useCalcState('extra', {
    loan: 300000, rate: 6.5, term: 30,
    extraMonthly: 200, extraYearly: 0, oneTime: 0, oneTimeMonth: 12,
  });
  const set = k => v => setS(p => ({...p, [k]: v}));

  const original = useMemo(() => buildAmortization(s.loan, s.rate, s.term), [s.loan, s.rate, s.term]);
  const withExtra = useMemo(() => buildAmortization(s.loan, s.rate, s.term, s.extraMonthly, s.extraYearly, s.oneTime, s.oneTimeMonth),
    [s]);

  const interestSaved = original.totalInterest - withExtra.totalInterest;
  const monthsSaved = original.payoffMonths - withExtra.payoffMonths;

  const series = useMemo(() => {
    const orig = [], ext = [];
    for (let y = 0; y <= s.term; y++) {
      const i = y*12 - 1;
      orig.push({x: y, y: i >= 0 && i < original.rows.length ? original.rows[i].balance : (y === 0 ? s.loan : 0)});
      ext.push({x: y, y: i >= 0 && i < withExtra.rows.length ? withExtra.rows[i].balance : (y === 0 ? s.loan : 0)});
    }
    return [
      {color: 'var(--c-rose)', label: 'Regular', data: orig},
      {color: 'var(--good)', label: 'With extra', data: ext},
    ];
  }, [s, original, withExtra]);

  // Yearly breakdown table
  const breakdown = useMemo(() => {
    const out = [];
    for (let y = 1; y <= s.term; y++) {
      const i = y*12 - 1;
      const reg = i < original.rows.length ? original.rows[i] : null;
      const ex = i < withExtra.rows.length ? withExtra.rows[i] : null;
      if (!reg && !ex) break;
      out.push({
        year: y,
        regBalance: reg ? reg.balance : 0,
        extBalance: ex ? ex.balance : 0,
        regInterest: reg ? reg.totalInterest : original.totalInterest,
        extInterest: ex ? ex.totalInterest : withExtra.totalInterest,
      });
    }
    return out;
  }, [original, withExtra, s.term]);

  return (
    <div className="split fade-in">
      <div>
        <Card title="Loan Details" style={{marginBottom: 16}}>
          <Field label="Loan Amount"><NumInput prefix="$" value={s.loan} onChange={set('loan')} /></Field>
          <div className="field-row">
            <Field label="Interest Rate"><NumInput suffix="%" value={s.rate} onChange={set('rate')} /></Field>
            <Field label="Term (yr)"><NumInput value={s.term} onChange={set('term')} /></Field>
          </div>
          <div style={{padding: 10, background: 'var(--accent-soft)', borderRadius: 8, fontSize: 13}}>
            <span style={{color:'var(--text-muted)', fontWeight: 600}}>Regular Monthly Payment: </span>
            <span className="mono" style={{fontWeight: 700, color:'var(--accent)'}}>{money(original.pi, 2)}</span>
          </div>
        </Card>
        <Card title="Extra Payments">
          <Field label="Extra Monthly" hint="Each month"><NumInput prefix="$" value={s.extraMonthly} onChange={set('extraMonthly')} /></Field>
          <Field label="Extra Yearly" hint="Annual lump sum"><NumInput prefix="$" value={s.extraYearly} onChange={set('extraYearly')} /></Field>
          <div className="field-row">
            <Field label="One-Time Extra"><NumInput prefix="$" value={s.oneTime} onChange={set('oneTime')} /></Field>
            <Field label="At Month #"><NumInput value={s.oneTimeMonth} onChange={set('oneTimeMonth')} /></Field>
          </div>
        </Card>
      </div>

      <div className="results-grid">
        <Tip><strong>How This Calculator Works</strong><br/>
<strong>Extra Payments:</strong> Shows the impact of additional principal payments on your loan payoff timeline and total interest paid. Compare the original amortization schedule against the accelerated payoff to see exactly how much time and money you save.</Tip>

        <div className="kpi-grid">
          <KPI good label="Interest Saved" value={money(interestSaved)} sub="Over loan life" />
          <KPI good label="Time Saved" value={`${Math.floor(monthsSaved/12)}y ${monthsSaved%12}m`} sub="Faster payoff" />
          <KPI label="New Payoff" value={`${(withExtra.payoffMonths/12).toFixed(1)} yr`} sub={`vs ${s.term} yr`} />
          <KPI label="Total New Payment" value={money(original.pi + s.extraMonthly, 2)} sub="P&I + extra" />
        </div>

        <Card title="Payoff Timeline" sub="Watch your balance fall faster">
          <LineChart series={series} height={260} xFormat={v => 'Yr ' + v} yFormat={v => '$' + (v/1000).toFixed(0) + 'k'} />
          <div className="legend">
            <span className="legend-dot" style={{'--c': 'var(--c-rose)'}}>Regular</span>
            <span className="legend-dot" style={{'--c': 'var(--good)'}}>With extra payments</span>
          </div>
        </Card>

        <div className="g2" style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap: 20}}>
          <Card title="Regular Plan">
            <KPI label="Monthly Payment" value={money(original.pi, 2)} />
            <div style={{marginTop: 8}}><KPI label="Total Interest" value={money(original.totalInterest)} /></div>
            <div style={{marginTop: 8}}><KPI label="Total Paid" value={money(s.loan + original.totalInterest)} /></div>
            <div style={{marginTop: 8}}><KPI label="Payoff Time" value={`${(original.payoffMonths/12).toFixed(1)} yr`} /></div>
          </Card>
          <Card title="With Extra Payments">
            <KPI label="Monthly Payment" value={money(original.pi + s.extraMonthly, 2)} />
            <div style={{marginTop: 8}}><KPI good label="Total Interest" value={money(withExtra.totalInterest)} /></div>
            <div style={{marginTop: 8}}><KPI good label="Total Paid" value={money(s.loan + withExtra.totalInterest)} /></div>
            <div style={{marginTop: 8}}><KPI good label="Payoff Time" value={`${(withExtra.payoffMonths/12).toFixed(1)} yr`} /></div>
          </Card>
        </div>

        <Card title="Interest Comparison">
          <BarChart height={220} data={[
            {label: 'Regular', value: original.totalInterest, color: 'var(--c-rose)'},
            {label: 'With Extra', value: withExtra.totalInterest, color: 'var(--good)'},
          ]} />
        </Card>

        <Card title="Year-by-Year Breakdown">
          <table className="tbl" style={{width:'100%'}}>
            <thead><tr><th>Year</th><th style={{textAlign:'right'}}>Regular Balance</th><th style={{textAlign:'right'}}>Extra Balance</th><th style={{textAlign:'right'}}>Interest Saved</th></tr></thead>
            <tbody>
              {breakdown.map(b => {
                const paidOff = b.extBalance <= 0 && b.regBalance > 0;
                const bigSave = (b.regInterest - b.extInterest) > 5000;
                return (
                  <tr key={b.year} style={paidOff ? {background:'var(--accent-soft)', fontWeight:700} : bigSave ? {background:'rgba(16,185,129,0.06)'} : {}}>
                    <td><strong>{b.year}</strong></td>
                    <td className="num">{money(b.regBalance)}</td>
                    <td className="num" style={paidOff ? {color:'var(--good)', fontWeight:700} : {}}>{b.extBalance <= 0 ? '$0' : money(b.extBalance)}</td>
                    <td className="num" style={{color:'var(--good)', fontWeight: 600}}>{money(b.regInterest - b.extInterest)}</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </Card>

        <Card title="Smart Extra Payment Strategies">
          <ul style={{fontSize:13,color:'var(--text-muted)',lineHeight:1.8,paddingLeft:18}}>
            <li>Even small extra payments can significantly reduce interest over time</li>
            <li>Apply windfalls (bonuses, tax refunds) directly to principal</li>
            <li>Biweekly payments (half payment every 2 weeks) = 1 extra payment/year</li>
            <li>Round up payments to nearest $50 or $100 for easy extra contributions</li>
            <li>Specify "apply to principal" when making extra payments</li>
            <li>Front-load extra payments early in the loan for maximum impact</li>
            <li>Consider your opportunity cost — sometimes investing may be better</li>
          </ul>
        </Card>
      </div>
    </div>
  );
}

/* -------- 7. Closing Costs -------- */
function ClosingCostsCalc() {
  const [s, setS] = useCalcState('closing', {
    homePrice: 350000, downPayment: 70000, loanAmount: 280000, rate: 6.5,
    taxYr: 4200, insYr: 1200, taxRate: 1.2,
    fees: {
      origination: { v: 1, on: true, pctOf: 'loan' },
      application: { v: 300, on: true },
      underwriting: { v: 500, on: true },
      processing: { v: 400, on: true },
      appraisal: { v: 500, on: true },
      credit: { v: 50, on: true },
      inspection: { v: 400, on: true },
      survey: { v: 400, on: true },
      titleSearch: { v: 300, on: true },
      titleIns: { v: 0.5, on: true, pctOf: 'price' },
      escrow: { v: 500, on: true },
      attorney: { v: 1000, on: true },
      recording: { v: 250, on: true },
      transfer: { v: 1.0, on: true, pctOf: 'price' },
      prepaidInterest: { v: 15, on: true, days: true },
      prepaidTax: { v: 3, on: true, months: true },
      prepaidIns: { v: 12, on: true, months: true },
      pmiRate: { v: 0.55, on: false, pctOf: 'loan' },
      pmiMonths: { v: 2, on: false, months: true },
      taxReserve: { v: 2, on: true, months: true },
      insReserve: { v: 2, on: true, months: true },
    },
  });
  const set = k => v => setS(p => {
    const n = {...p, [k]: v};
    // PMI auto-trigger: on when down < 20% of price
    if (k === 'downPayment' || k === 'homePrice') {
      const dp = k === 'downPayment' ? v : p.downPayment;
      const hp = k === 'homePrice' ? v : p.homePrice;
      const pmiOn = hp > 0 && (dp / hp) < 0.2;
      n.fees = {...p.fees, pmiRate: {...p.fees.pmiRate, on: pmiOn}, pmiMonths: {...p.fees.pmiMonths, on: pmiOn}};
    }
    // Tax rate <-> tax/yr sync
    if (k === 'taxRate') n.taxYr = Math.round(n.homePrice * v / 100);
    if (k === 'taxYr') n.taxRate = n.homePrice > 0 ? Number((v / n.homePrice * 100).toFixed(4)) : 0;
    return n;
  });
  const setFee = (key, prop, val) => setS(p => ({...p, fees: {...p.fees, [key]: {...p.fees[key], [prop]: val}}}));

  const f = s.fees;
  const calc = (k) => {
    const fee = f[k]; if (!fee || !fee.on) return 0;
    if (fee.pctOf === 'loan') return s.loanAmount * (fee.v / 100);
    if (fee.pctOf === 'price') return s.homePrice * (fee.v / 100);
    if (fee.days) return (fee.v * s.loanAmount * (s.rate/100) / 365);
    if (fee.months) {
      if (k === 'prepaidTax' || k === 'taxReserve') return (s.taxYr / 12) * fee.v;
      if (k === 'prepaidIns' || k === 'insReserve') return (s.insYr / 12) * fee.v;
      if (k === 'pmiMonths') return (s.loanAmount * (f.pmiRate.v/100) / 12) * fee.v;
    }
    return fee.v;
  };

  const categories = {
    'Lender': ['origination','application','underwriting','processing'],
    'Third-Party': ['appraisal','credit','inspection','survey'],
    'Title & Escrow': ['titleSearch','titleIns','escrow','attorney'],
    'Government': ['recording','transfer'],
    'Prepaid': ['prepaidInterest','prepaidTax','prepaidIns','pmiRate','pmiMonths'],
    'Reserves': ['taxReserve','insReserve'],
  };
  const labels = {
    origination: 'Origination Fee', application: 'Application Fee', underwriting: 'Underwriting', processing: 'Processing',
    appraisal: 'Appraisal', credit: 'Credit Report', inspection: 'Inspection', survey: 'Survey',
    titleSearch: 'Title Search', titleIns: 'Title Insurance', escrow: 'Escrow Fee', attorney: 'Attorney Fee',
    recording: 'Recording Fees', transfer: 'Transfer Tax',
    prepaidInterest: 'Prepaid Interest', prepaidTax: 'Prepaid Tax', prepaidIns: 'Prepaid Insurance',
    pmiRate: 'PMI Rate', pmiMonths: 'PMI Months',
    taxReserve: 'Tax Reserve', insReserve: 'Insurance Reserve',
  };

  const catTotals = Object.entries(categories).map(([cat, keys]) => ({
    label: cat, value: keys.reduce((s2, k) => s2 + calc(k), 0)
  }));
  const totalClosing = catTotals.reduce((s2, c) => s2 + c.value, 0);
  const cashToClose = s.downPayment + totalClosing;
  const palette = ['var(--accent)','var(--c-violet)','var(--c-teal)','var(--c-amber)','var(--c-rose)','var(--c-emerald)'];

  return (
    <div className="split fade-in">
      <div>
        <Card title="Property Details" style={{marginBottom: 16}}>
          <Field label="Home Price"><NumInput prefix="$" value={s.homePrice} onChange={set('homePrice')} /></Field>
          <div className="field-row">
            <Field label="Down Payment"><NumInput prefix="$" value={s.downPayment} onChange={set('downPayment')} /></Field>
            <Field label="Loan Amount"><NumInput prefix="$" value={s.loanAmount} onChange={set('loanAmount')} /></Field>
          </div>
          <div className="field-row">
            <Field label="Interest Rate"><NumInput suffix="%" value={s.rate} onChange={set('rate')} /></Field>
            <Field label="Tax Rate"><NumInput suffix="%" value={s.taxRate} onChange={set('taxRate')} /></Field>
          </div>
          <div className="field-row">
            <Field label="Tax / yr"><NumInput prefix="$" value={s.taxYr} onChange={set('taxYr')} /></Field>
            <Field label="Insurance / yr"><NumInput prefix="$" value={s.insYr} onChange={set('insYr')} /></Field>
          </div>
        </Card>

        <Card title="Fee Categories">
          {Object.entries(categories).map(([cat, keys]) => (
            <details key={cat} open style={{marginBottom: 10}}>
              <summary style={{cursor:'pointer', padding: 8, background:'var(--bg-sunken)', borderRadius: 8, fontWeight: 600, fontSize: 13, listStyle:'none', display:'flex', justifyContent:'space-between'}}>
                <span>{cat}</span>
                <span className="mono" style={{color:'var(--accent)'}}>{money(catTotals.find(c => c.label === cat).value)}</span>
              </summary>
              <div style={{padding: '10px 4px'}}>
                {keys.map(k => (
                  <div key={k} className="row" style={{gap: 8, marginBottom: 8}}>
                    <input type="checkbox" checked={f[k].on} onChange={e => setFee(k, 'on', e.target.checked)} />
                    <span style={{fontSize: 12, flex: 1, color: f[k].on ? 'var(--text)' : 'var(--text-subtle)'}}>{labels[k]}</span>
                    <div style={{width: 80}}>
                      <NumInput value={f[k].v}
                        prefix={f[k].pctOf || f[k].days || f[k].months ? '' : '$'}
                        suffix={f[k].pctOf ? '%' : f[k].days ? 'd' : f[k].months ? 'mo' : ''}
                        onChange={v => setFee(k, 'v', v)} />
                    </div>
                  </div>
                ))}
              </div>
            </details>
          ))}
        </Card>
      </div>

      <div className="results-grid">
        <Tip><strong>How This Calculator Works</strong><br/>
<strong>Closing Costs:</strong> This tool estimates the fees and expenses you'll pay at closing — including lender fees, third-party services, title &amp; escrow, government taxes, prepaid items, and escrow reserves. Toggle individual fees on or off and adjust amounts to match your specific loan scenario.<br/>
<strong>Fee Presets:</strong> Save your customized fee configurations as reusable presets so you can quickly load them for future estimates without re-entering every amount.</Tip>

        <div className="kpi-grid">
          <KPI accent label="Cash to Close" value={money(cashToClose)} sub="All in" />
          <KPI label="Closing Costs" value={money(totalClosing)} sub={pct(s.homePrice ? totalClosing/s.homePrice*100 : 0, 2) + ' of price'} />
          <KPI label="Down Payment" value={money(s.downPayment)} />
        </div>

        <Card title="Cost by Category">
          <Donut size={170} thickness={26} centerLabel="closing" centerValue={money(totalClosing)}
            data={catTotals.map((c, i) => ({label: c.label, value: c.value, color: palette[i]}))} />
        </Card>

        <Card title="Itemized Cost Breakdown">
          <table className="tbl" style={{width:'100%'}}>
            <thead><tr><th style={{textAlign:'left'}}>Item</th><th style={{textAlign:'right'}}>Amount</th></tr></thead>
            <tbody>
              {Object.entries(categories).flatMap(([cat, keys]) => {
                const activeKeys = keys.filter(k => f[k].on && calc(k) > 0);
                if (!activeKeys.length) return [];
                const catTotal = activeKeys.reduce((s2, k) => s2 + calc(k), 0);
                const highlight = ['origination','titleIns','transfer','prepaidInterest'];
                return [
                  <tr key={'cat-' + cat} style={{background:'var(--bg-sunken)'}}>
                    <td colSpan={2} style={{fontWeight:700, fontSize:12, textTransform:'uppercase', letterSpacing:'0.04em', color:'var(--text-muted)', padding:'10px 12px 6px'}}>{cat}</td>
                  </tr>,
                  ...activeKeys.map(k => (
                    <tr key={k} style={highlight.includes(k) ? {background:'var(--accent-soft)', fontWeight:600} : {}}>
                      <td style={{paddingLeft:20}}>{labels[k]}</td>
                      <td className="num" style={highlight.includes(k) ? {color:'var(--accent)', fontWeight:700} : {}}>{money(calc(k), 2)}</td>
                    </tr>
                  )),
                  <tr key={'subtotal-' + cat} style={{borderBottom:'2px solid var(--border)'}}>
                    <td style={{paddingLeft:20, fontWeight:600, fontStyle:'italic', color:'var(--text-muted)', fontSize:12}}>Subtotal</td>
                    <td className="num" style={{fontWeight:600, color:'var(--text-muted)', fontSize:12}}>{money(catTotal, 2)}</td>
                  </tr>
                ];
              })}
              <tr style={{fontWeight:700, borderTop:'3px solid var(--accent)', background:'var(--accent-soft)'}}>
                <td style={{fontSize:14, padding:'12px'}}>Total Closing Costs</td>
                <td className="num" style={{fontSize:14, padding:'12px', color:'var(--accent)'}}>{money(totalClosing, 2)}</td>
              </tr>
              <tr style={{fontWeight:700, background:'var(--accent-soft)'}}>
                <td style={{fontSize:14, padding:'12px'}}>Total Cash to Close</td>
                <td className="num" style={{fontSize:14, padding:'12px', color:'var(--accent)'}}>{money(cashToClose, 2)}</td>
              </tr>
            </tbody>
          </table>
        </Card>

        <Card title="Important Notes">
          <ul style={{fontSize:13,color:'var(--text-muted)',lineHeight:1.8,paddingLeft:18}}>
            <li>These are estimates — actual costs may vary by location and lender</li>
            <li>Closing costs typically range from 2-5% of the home price</li>
            <li>Some fees may be negotiable with the seller or lender</li>
            <li>Ask for a Loan Estimate within 3 days of applying for accurate costs</li>
            <li>Transfer taxes and recording fees vary significantly by state/county</li>
            <li>You may be able to roll some closing costs into your loan amount</li>
            <li>Budget extra for moving costs, furniture, and immediate home needs</li>
          </ul>
        </Card>
      </div>
    </div>
  );
}

/* -------- 8. Fix & Flip -------- */
function FixFlipCalc() {
  const [s, setS] = useCalcState('flip', {
    purchase: 1550000, rehab: 600000, arv: 2700000,
    downPct: 25, rate: 10.49, term: 12, holding: 6,
    underwriting: 1995, attorney: 635, originPoints: 2.5, reserves: 6, other: 0,
    sellingCostPct: 6,
  });
  const set = k => v => setS(p => ({...p, [k]: v}));

  const downPayment = s.purchase * (s.downPct / 100);
  const loanAmount = s.purchase - downPayment + s.rehab;
  const monthlyInterest = loanAmount * (s.rate/100) / 12;
  const totalInterest = monthlyInterest * s.holding;
  const points = loanAmount * (s.originPoints / 100);
  const reserves = monthlyInterest * s.reserves;
  const closingCosts = s.underwriting + s.attorney + points + s.other;
  const cashAtClose = downPayment + closingCosts;
  const totalLiquid = cashAtClose + reserves;

  const sellingCosts = s.arv * (s.sellingCostPct/100);
  const totalCost = s.purchase + s.rehab + closingCosts + totalInterest;
  const netProfit = s.arv - totalCost;
  const roi = cashAtClose > 0 ? (netProfit / cashAtClose) * 100 : 0;

  const ltvARV = s.arv ? (loanAmount / s.arv) * 100 : 0;
  const ltvAsIs = s.purchase ? (loanAmount / s.purchase) * 100 : 0;
  const ltc = (s.purchase + s.rehab) ? (loanAmount / (s.purchase + s.rehab)) * 100 : 0;

  const waterfallData = [
    {label: 'ARV', value: s.arv, color: 'var(--good)'},
    {label: 'Purchase', value: s.purchase, color: 'var(--c-rose)'},
    {label: 'Rehab', value: s.rehab, color: 'var(--c-amber)'},
    {label: 'Closing', value: closingCosts, color: 'var(--c-violet)'},
    {label: 'Interest', value: totalInterest, color: 'var(--c-teal)'},
    {label: 'Selling', value: sellingCosts, color: 'var(--c-blue)'},
    {label: 'Profit', value: netProfit, color: netProfit > 0 ? 'var(--good)' : 'var(--bad)'},
  ];

  return (
    <div className="split fade-in">
      <div>
        <Card title="Property Details" style={{marginBottom: 16}}>
          <Field label="Purchase Price"><NumInput prefix="$" value={s.purchase} onChange={set('purchase')} /></Field>
          <Field label="Rehab Budget"><NumInput prefix="$" value={s.rehab} onChange={set('rehab')} /></Field>
          <Field label="After Repair Value (ARV)"><NumInput prefix="$" value={s.arv} onChange={set('arv')} /></Field>
        </Card>
        <Card title="Loan Details" style={{marginBottom: 16}}>
          <Field label="Down Payment %"><NumInput suffix="%" value={s.downPct} onChange={set('downPct')} /></Field>
          <div className="field-row">
            <Field label="Interest Rate"><NumInput suffix="%" value={s.rate} onChange={set('rate')} /></Field>
            <Field label="Term (mo)"><NumInput value={s.term} onChange={set('term')} /></Field>
          </div>
          <Field label="Holding Period"><NumInput suffix="mo" value={s.holding} onChange={set('holding')} /></Field>
        </Card>
        <Card title="Additional Costs">
          <div className="field-row">
            <Field label="Underwriting"><NumInput prefix="$" value={s.underwriting} onChange={set('underwriting')} /></Field>
            <Field label="Attorney"><NumInput prefix="$" value={s.attorney} onChange={set('attorney')} /></Field>
          </div>
          <div className="field-row">
            <Field label="Origination Points"><NumInput suffix="%" value={s.originPoints} onChange={set('originPoints')} /></Field>
            <Field label="Reserves (mo)"><NumInput value={s.reserves} onChange={set('reserves')} /></Field>
          </div>
          <div className="field-row">
            <Field label="Other Costs"><NumInput prefix="$" value={s.other} onChange={set('other')} /></Field>
            <Field label="Selling Cost"><NumInput suffix="%" value={s.sellingCostPct} onChange={set('sellingCostPct')} /></Field>
          </div>
        </Card>
      </div>

      <div className="results-grid">
        <Tip><strong>Fix & Flip economics.</strong> ARV minus project cost (purchase, rehab, closing, and holding interest) = net profit. Selling costs are tracked separately for underwriting review.</Tip>

        <div className="kpi-grid">
          <KPI accent good={netProfit > 0} bad={netProfit < 0} label="Net Profit" value={money(netProfit)} sub={pct(roi, 1) + ' ROI'} />
          <KPI label="Cash at Closing" value={money(cashAtClose)} />
          <KPI label="Total Liquid Required" value={money(totalLiquid)} sub="Cash at close + reserves" />
          <KPI label="Loan Amount" value={money(loanAmount)} />
        </div>

        <div className="g2" style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap: 20}}>
          {/* Project Metrics card */}
          <Card title="Project Metrics">
            <table className="tbl">
              <tbody>
                <tr><td>Purchase Price</td><td className="num">{money(s.purchase)}</td></tr>
                <tr><td>Rehab Budget</td><td className="num">{money(s.rehab)}</td></tr>
                <tr><td>After Repair Value</td><td className="num">{money(s.arv)}</td></tr>
                <tr><td colSpan={2} style={{borderTop:'1px solid var(--border)', height:4, padding:0}}></td></tr>
                <tr><td>Loan to Value (ARV)</td><td className="num" style={{fontWeight:600}}>{pct(ltvARV, 2)}</td></tr>
                <tr><td>Loan to Value (As-Is)</td><td className="num" style={{fontWeight:600}}>{pct(ltvAsIs, 2)}</td></tr>
                <tr><td>Loan to Cost (LTC)</td><td className="num" style={{fontWeight:600}}>{pct(ltc, 2)}</td></tr>
              </tbody>
            </table>
          </Card>

          {/* Loan Summary card */}
          <Card title="Loan Summary">
            <div style={{display:'flex', flexDirection:'column', gap: 16}}>
              <div style={{display:'flex', justifyContent:'space-between', alignItems:'center', padding:'12px 16px', background:'var(--bg-sunken)', borderRadius:8}}>
                <span style={{fontSize:13, color:'var(--text-muted)'}}>Total Loan Amount</span>
                <span className="mono" style={{fontSize:18, fontWeight:700, color:'var(--accent)'}}>{money(loanAmount)}</span>
              </div>
              <div style={{display:'flex', justifyContent:'space-between', alignItems:'center', padding:'12px 16px', background:'var(--bg-sunken)', borderRadius:8}}>
                <span style={{fontSize:13, color:'var(--text-muted)'}}>Monthly Payment (IO)</span>
                <span className="mono" style={{fontSize:18, fontWeight:700}}>{money(monthlyInterest, 2)}</span>
              </div>
              <div style={{display:'flex', justifyContent:'space-between', alignItems:'center', padding:'12px 16px', background:'var(--bg-sunken)', borderRadius:8}}>
                <span style={{fontSize:13, color:'var(--text-muted)'}}>Total Holding Costs ({s.holding}mo)</span>
                <span className="mono" style={{fontSize:18, fontWeight:700, color:'var(--c-amber)'}}>{money(totalInterest)}</span>
              </div>
              <div style={{display:'flex', justifyContent:'space-between', alignItems:'center', padding:'12px 16px', background:'var(--bg-sunken)', borderRadius:8}}>
                <span style={{fontSize:13, color:'var(--text-muted)'}}>Total Cost Basis</span>
                <span className="mono" style={{fontSize:18, fontWeight:700, color:'var(--accent)'}}>{money(totalCost)}</span>
              </div>
            </div>
          </Card>

          {/* Closing Costs card */}
          <Card title="Closing Costs">
            <table className="tbl">
              <tbody>
                <tr><td>Down Payment</td><td className="num">{money(downPayment)}</td></tr>
                <tr><td>Origination Fee</td><td className="num">{money(points)}</td></tr>
                <tr><td>Underwriting & Attorney</td><td className="num" style={{color:'var(--accent)'}}>{money(s.underwriting + s.attorney)}</td></tr>
                <tr><td>Reserves ({s.reserves}mo)</td><td className="num">{money(reserves)}</td></tr>
                <tr><td>Other Costs</td><td className="num">{money(s.other)}</td></tr>
                <tr><td colSpan={2} style={{borderTop:'1px solid var(--border)', height:4, padding:0}}></td></tr>
                <tr style={{fontWeight:800}}><td>Cash Due at Closing</td><td className="num">{money(cashAtClose)}</td></tr>
                <tr><td style={{color:'var(--text-muted)', fontSize:12}}>Total Liquid Required</td><td className="num" style={{color:'var(--text-muted)', fontSize:12}}>{money(totalLiquid)}</td></tr>
              </tbody>
            </table>
          </Card>

          {/* Project Economics chart */}
          <Card title="Project Economics">
            <BarChart data={[
              {label: 'Purchase', value: s.purchase, color: 'var(--accent)'},
              {label: 'Rehab', value: s.rehab, color: 'var(--c-amber)'},
              {label: 'Closing', value: closingCosts, color: 'var(--c-violet)'},
              {label: 'Holding', value: totalInterest, color: 'var(--c-teal)'},
              {label: 'Profit', value: netProfit, color: netProfit > 0 ? 'var(--good)' : 'var(--bad)'},
            ]} height={260} horizontal />
          </Card>
        </div>

        {/* Selling costs & summary row */}
        <Card title="Deal Summary">
          <div className="g2-half" style={{display:'grid', gridTemplateColumns:'repeat(4, 1fr)', gap: 12}}>
            <div className="kpi" style={{padding: 14}}>
              <div className="kpi-label">Selling Costs ({s.sellingCostPct}%)</div>
              <div className="kpi-value">{money(sellingCosts)}</div>
            </div>
            <div className="kpi" style={{padding: 14}}>
              <div className="kpi-label">Total Project Cost</div>
              <div className="kpi-value">{money(totalCost)}</div>
            </div>
            <div className="kpi" style={{padding: 14, background: netProfit > 0 ? 'rgba(16,185,129,0.08)' : 'rgba(239,68,68,0.08)'}}>
              <div className="kpi-label">Net Profit</div>
              <div className="kpi-value" style={{color: netProfit > 0 ? 'var(--good)' : 'var(--bad)'}}>{money(netProfit)}</div>
              <div className="kpi-sub">{pct(roi, 1)} ROI</div>
            </div>
            <div className="kpi" style={{padding: 14}}>
              <div className="kpi-label">ARV</div>
              <div className="kpi-value">{money(s.arv)}</div>
            </div>
          </div>
        </Card>
      </div>
    </div>
  );
}

Object.assign(window, { HELOCCalc, ExtraPaymentCalc, ClosingCostsCalc, FixFlipCalc });
/* ========== Calculators set 3: Rent vs Buy, Buydown, Amortization, DSCR ========== */

/* -------- 9. Rent vs Buy -------- */
function RentVsBuyCalc() {
  const [s, setS] = useCalcState('rentbuy', {
    rent: 2000, rentIncrease: 3, rentersIns: 200,
    homePrice: 400000, downPayment: 80000, downPct: 20,
    rate: 6.5, term: 30, taxMo: 400, insMo: 150, hoaMo: 100, closing: 8000,
    appreciation: 3, horizon: 10, investReturn: 7, maintMo: 250, renterInvest: 0,
  });
  const set = k => v => setS(p => {
    const n = {...p, [k]: v};
    if (k === 'downPayment') n.downPct = p.homePrice ? (v/p.homePrice)*100 : 0;
    if (k === 'downPct') n.downPayment = p.homePrice * (v/100);
    if (k === 'homePrice') n.downPayment = v * (p.downPct/100);
    return n;
  });
  const [tab, setTab] = useState('Rent');

  const loan = s.homePrice - s.downPayment;
  const pi = calcPI(loan, s.rate, s.term);
  const buyMonthly = pi + s.taxMo + s.insMo + s.hoaMo + s.maintMo;

  // Year-by-year projection
  const projection = useMemo(() => {
    const am = buildAmortization(loan, s.rate, s.term);
    const out = [];
    let rentCum = 0, rentInsCum = 0, rentInvest = s.downPayment + s.closing;
    let buyCum = s.downPayment + s.closing, maintCum = 0;
    let currRent = s.rent;
    const mr = s.investReturn / 100 / 12; // monthly investment return
    for (let y = 0; y <= s.horizon; y++) {
      // Rent side — competitor uses base * (1+increase)^year for each year, accumulates 0..horizon inclusive
      const yearRent = s.rent * Math.pow(1 + s.rentIncrease / 100, y);
      const annualRent = yearRent * 12;
      rentCum += annualRent;
      rentInsCum += s.rentersIns;
      if (y > 0) {
        // Renter invests: monthly compounding of savings differential + fixed monthly investment
        const prevYearRent = s.rent * Math.pow(1 + s.rentIncrease / 100, y - 1);
        const monthlyRenterIns = s.rentersIns / 12;
        const monthlySavingsDiff = pi + s.taxMo + s.insMo + s.hoaMo + s.maintMo - prevYearRent - monthlyRenterIns;
        for (let mi = 0; mi < 12; mi++) {
          rentInvest = rentInvest * (1 + mr) + Math.max(0, monthlySavingsDiff) + s.renterInvest;
        }
      }
      // Buy side
      const idx = Math.min(am.rows.length - 1, y * 12 - 1);
      const balance = idx >= 0 ? am.rows[idx].balance : loan;
      const homeValue = s.homePrice * Math.pow(1 + s.appreciation/100, y);
      const equity = homeValue - balance;
      if (y > 0) {
        buyCum += (pi + s.taxMo + s.insMo + s.hoaMo) * 12;
        maintCum += s.maintMo * 12;
      }
      const rentNet = rentInvest;
      const buyNet = equity;
      out.push({ y, rentNet, buyNet, rentCum: rentCum + rentInsCum, buyCum: buyCum + maintCum, equity, homeValue, rentInvest, rentInsCum });
    }
    return out;
  }, [s, loan, pi]);

  const final = projection[projection.length - 1];
  const winner = final.buyNet > final.rentNet ? 'buy' : 'rent';
  const diff = Math.abs(final.buyNet - final.rentNet);

  const series = [
    {color: 'var(--c-rose)', label: 'Renting', data: projection.map(p => ({x: p.y, y: p.rentNet}))},
    {color: 'var(--accent)', label: 'Buying', data: projection.map(p => ({x: p.y, y: p.buyNet}))},
  ];
  const cumSeries = [
    {color: 'var(--c-rose)', label: 'Rent costs', data: projection.map(p => ({x: p.y, y: p.rentCum}))},
    {color: 'var(--accent)', label: 'Buy costs', data: projection.map(p => ({x: p.y, y: p.buyCum}))},
  ];

  return (
    <div className="split fade-in">
      <div>
        <div className="seg seg-full" style={{marginBottom: 14}}>
          <button className={"seg-btn" + (tab === 'Rent' ? ' active' : '')} onClick={() => setTab('Rent')}>Rent</button>
          <button className={"seg-btn" + (tab === 'Buy' ? ' active' : '')} onClick={() => setTab('Buy')}>Buy</button>
          <button className={"seg-btn" + (tab === 'Other' ? ' active' : '')} onClick={() => setTab('Other')}>Other</button>
        </div>
        {tab === 'Rent' && (
          <Card title="Rent Details">
            <Field label="Monthly Rent"><NumInput prefix="$" value={s.rent} onChange={set('rent')} /></Field>
            <Field label="Annual Rent Increase"><NumInput suffix="%" value={s.rentIncrease} onChange={set('rentIncrease')} /></Field>
            <Field label="Renters Insurance / yr"><NumInput prefix="$" value={s.rentersIns} onChange={set('rentersIns')} /></Field>
          </Card>
        )}
        {tab === 'Buy' && (
          <Card title="Buy Details">
            <Field label="Home Price"><NumInput prefix="$" value={s.homePrice} onChange={set('homePrice')} /></Field>
            <div className="field-row">
              <Field label="Down Payment"><NumInput prefix="$" value={Math.round(s.downPayment)} onChange={set('downPayment')} /></Field>
              <Field label="Down %"><NumInput suffix="%" value={Number(s.downPct.toFixed(2))} onChange={set('downPct')} /></Field>
            </div>
            <div className="field-row">
              <Field label="Interest Rate"><NumInput suffix="%" value={s.rate} onChange={set('rate')} /></Field>
              <Field label="Term"><NumInput suffix="yr" value={s.term} onChange={set('term')} /></Field>
            </div>
            <div className="field-row">
              <Field label="Tax / mo"><NumInput prefix="$" value={s.taxMo} onChange={set('taxMo')} /></Field>
              <Field label="Insurance / mo"><NumInput prefix="$" value={s.insMo} onChange={set('insMo')} /></Field>
            </div>
            <div className="field-row">
              <Field label="HOA / mo"><NumInput prefix="$" value={s.hoaMo} onChange={set('hoaMo')} /></Field>
              <Field label="Closing Costs"><NumInput prefix="$" value={s.closing} onChange={set('closing')} /></Field>
            </div>
          </Card>
        )}
        {tab === 'Other' && (
          <Card title="Assumptions">
            <Field label="Appreciation / yr"><NumInput suffix="%" value={s.appreciation} onChange={set('appreciation')} /></Field>
            <Field label="Time Horizon"><NumInput suffix="yr" value={s.horizon} onChange={set('horizon')} /></Field>
            <Field label="Investment Return"><NumInput suffix="%" value={s.investReturn} onChange={set('investReturn')} /></Field>
            <Field label="Maintenance / mo"><NumInput prefix="$" value={s.maintMo} onChange={set('maintMo')} /></Field>
            <Field label="Renter Monthly Investment" hint="Difference vs buying invested"><NumInput prefix="$" value={s.renterInvest} onChange={set('renterInvest')} /></Field>
          </Card>
        )}
      </div>

      <div className="results-grid">
        <Tip><strong>How This Calculator Works</strong><br/>
<strong>Renter's Net Worth:</strong> Assumes the renter invests the down payment and closing costs upfront. Each month, any savings from lower housing costs (compared to buying) are also invested. All investments grow at the specified investment return rate.<br/>
<strong>Buyer's Net Worth:</strong> Tracks home equity as the property appreciates and the mortgage balance decreases over time.</Tip>

        <Card style={{background: winner === 'buy' ? 'linear-gradient(135deg, var(--accent-soft), oklch(from var(--accent) calc(l + 0.4) c h / 0.2))' : 'linear-gradient(135deg, oklch(from var(--c-rose) calc(l + 0.35) calc(c * 0.4) h), oklch(from var(--c-rose) calc(l + 0.4) calc(c * 0.3) h))'}}>
          <div style={{fontSize: 13, color:'var(--text-muted)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 8}}>The Bottom Line</div>
          <div style={{fontSize: 24, fontWeight: 700, lineHeight: 1.3}}>
            <strong style={{color: winner === 'buy' ? 'var(--accent)' : 'var(--c-rose)'}}>{winner === 'buy' ? 'Buying' : 'Renting'}</strong> wins by <span className="mono">{money(diff)}</span> over {s.horizon} years.
          </div>
        </Card>

        <div className="kpi-grid">
          <KPI label="Buy Monthly" value={money(buyMonthly, 0)} sub="P&I + tax + ins + HOA + maint" />
          <KPI label="Rent Today" value={money(s.rent, 0)} />
          <KPI label="Buy Net Position" value={money(final.buyNet)} sub={`@ ${s.horizon}y`} />
          <KPI label="Rent Net Position" value={money(final.rentNet)} sub={`@ ${s.horizon}y`} />
        </div>

        <Card title="Net Worth Comparison">
          <LineChart series={series} height={260} xFormat={v => 'Yr ' + v} yFormat={v => '$' + (v/1000).toFixed(0) + 'k'} />
          <div className="legend">
            <span className="legend-dot" style={{'--c': 'var(--c-rose)'}}>Renting (with invested savings)</span>
            <span className="legend-dot" style={{'--c': 'var(--accent)'}}>Buying (home equity)</span>
          </div>
        </Card>

        <Card title="Cumulative Costs">
          <LineChart series={cumSeries} height={220} xFormat={v => 'Yr ' + v} yFormat={v => '$' + (v/1000).toFixed(0) + 'k'} />
          <div className="legend">
            <span className="legend-dot" style={{'--c': 'var(--c-rose)'}}>Rent + insurance costs</span>
            <span className="legend-dot" style={{'--c': 'var(--accent)'}}>Buy + maintenance costs</span>
          </div>
        </Card>

        <Card title="Summary">
          <div className="g2" style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap: 24}}>
            {/* Renting column */}
            <div>
              <h4 style={{fontSize: 15, fontWeight: 700, marginBottom: 14, color: 'var(--text)'}}>Renting</h4>
              <div style={{display:'flex', flexDirection:'column', gap: 10}}>
                <div style={{display:'flex', justifyContent:'space-between', padding:'10px 14px', borderLeft:'3px solid var(--border)', borderRadius: 4}}>
                  <span style={{fontSize: 13, color:'var(--text-muted)'}}>Monthly Cost</span>
                  <span className="mono" style={{fontWeight: 700}}>{money(s.rent + s.rentersIns/12, 2)}</span>
                </div>
                <div style={{display:'flex', justifyContent:'space-between', padding:'10px 14px', borderLeft:'3px solid var(--c-rose)', borderRadius: 4}}>
                  <span style={{fontSize: 13, color:'var(--text-muted)'}}>Total Rent Paid</span>
                  <span className="mono" style={{fontWeight: 700, color:'var(--c-rose)'}}>{money(final.rentCum)}</span>
                </div>
                <div style={{display:'flex', justifyContent:'space-between', padding:'10px 14px', borderLeft:'3px solid var(--c-amber)', background:'rgba(245,158,11,0.06)', borderRadius: 4}}>
                  <span style={{fontSize: 13, color:'var(--text-muted)'}}>Capital Kept (Down Payment)</span>
                  <span className="mono" style={{fontWeight: 700}}>{money(s.downPayment)}</span>
                </div>
                <div style={{display:'flex', justifyContent:'space-between', padding:'10px 14px', borderLeft:'3px solid var(--c-amber)', background:'rgba(245,158,11,0.06)', borderRadius: 4}}>
                  <span style={{fontSize: 13, color:'var(--text-muted)'}}>Capital Kept (Closing Costs)</span>
                  <span className="mono" style={{fontWeight: 700}}>{money(s.closing)}</span>
                </div>
                <div style={{display:'flex', justifyContent:'space-between', padding:'10px 14px', borderLeft:'3px solid var(--accent)', background:'rgba(99,102,241,0.08)', borderRadius: 4}}>
                  <span style={{fontSize: 13, fontWeight: 600}}>Net Worth (Investments)</span>
                  <span className="mono" style={{fontWeight: 700, color:'var(--accent)'}}>{money(final.rentNet)}</span>
                </div>
              </div>
            </div>
            {/* Buying column */}
            <div>
              <h4 style={{fontSize: 15, fontWeight: 700, marginBottom: 14, color: 'var(--text)'}}>Buying</h4>
              <div style={{display:'flex', flexDirection:'column', gap: 10}}>
                <div style={{display:'flex', justifyContent:'space-between', padding:'10px 14px', borderLeft:'3px solid var(--border)', borderRadius: 4}}>
                  <span style={{fontSize: 13, color:'var(--text-muted)'}}>Monthly Cost</span>
                  <span className="mono" style={{fontWeight: 700}}>{money(buyMonthly, 2)}</span>
                </div>
                <div style={{display:'flex', justifyContent:'space-between', padding:'10px 14px', borderLeft:'3px solid var(--border)', borderRadius: 4}}>
                  <span style={{fontSize: 13, color:'var(--text-muted)'}}>Down Payment</span>
                  <span className="mono" style={{fontWeight: 700}}>{money(s.downPayment)}</span>
                </div>
                <div style={{display:'flex', justifyContent:'space-between', padding:'10px 14px', borderLeft:'3px solid var(--c-amber)', borderRadius: 4}}>
                  <span style={{fontSize: 13, color:'var(--text-muted)'}}>Closing Costs (sunk)</span>
                  <span className="mono" style={{fontWeight: 700, color:'var(--c-amber)'}}>{money(s.closing)}</span>
                </div>
                <div style={{display:'flex', justifyContent:'space-between', padding:'10px 14px', borderLeft:'3px solid var(--c-rose)', borderRadius: 4}}>
                  <span style={{fontSize: 13, color:'var(--text-muted)'}}>Total Paid</span>
                  <span className="mono" style={{fontWeight: 700, color:'var(--c-rose)'}}>{money(final.buyCum)}</span>
                </div>
                <div style={{display:'flex', justifyContent:'space-between', padding:'10px 14px', borderLeft:'3px solid var(--accent)', borderRadius: 4}}>
                  <span style={{fontSize: 13, color:'var(--text-muted)'}}>Home Value</span>
                  <span className="mono" style={{fontWeight: 700, color:'var(--accent)'}}>{money(final.homeValue)}</span>
                </div>
                <div style={{display:'flex', justifyContent:'space-between', padding:'10px 14px', borderLeft:'3px solid var(--good)', background:'rgba(16,185,129,0.08)', borderRadius: 4}}>
                  <span style={{fontSize: 13, fontWeight: 600}}>Net Worth (Equity)</span>
                  <span className="mono" style={{fontWeight: 700, color:'var(--good)'}}>{money(final.buyNet)}</span>
                </div>
              </div>
            </div>
          </div>
        </Card>
      </div>
    </div>
  );
}

/* -------- 10. Temporary Buydown -------- */
function BuydownCalc() {
  const [s, setS] = useCalcState('buydown', {
    loan: 400000, noteRate: 7, term: 30, type: '2-1',
  });
  const set = k => v => setS(p => ({...p, [k]: v}));

  const buydownMap = {
    '3-2-1': [3, 2, 1],
    '2-1':   [2, 1],
    '1-0':   [1],
  };
  const reductions = buydownMap[s.type];
  const noteP = calcPI(s.loan, s.noteRate, s.term);

  const schedule = reductions.map((red, i) => {
    const yearRate = s.noteRate - red;
    const yearPay = calcPI(s.loan, yearRate, s.term);
    const savings = noteP - yearPay;
    return { year: i + 1, rate: yearRate, payment: yearPay, savings, annual: savings * 12 };
  });
  const buydownCost = schedule.reduce((sum, r) => sum + r.annual, 0);
  const cumSavings = [];
  let acc = 0;
  for (let m = 1; m <= 36; m++) {
    const yr = Math.ceil(m / 12);
    const row = schedule[yr - 1];
    if (row) acc += row.savings;
    cumSavings.push({x: m, y: acc});
  }

  return (
    <div className="split fade-in">
      <Card title="Loan Details">
        <Field label="Loan Amount"><NumInput prefix="$" value={s.loan} onChange={set('loan')} /></Field>
        <Field label="Note Rate" hint="Permanent rate"><NumInput suffix="%" value={s.noteRate} onChange={set('noteRate')} /></Field>
        <Field label="Term (Years)"><NumInput value={s.term} onChange={set('term')} /></Field>
        <Field label="Buydown Type">
          <Segmented full value={s.type} onChange={set('type')} options={[
            {label:'3-2-1', value:'3-2-1'}, {label:'2-1', value:'2-1'}, {label:'1-0', value:'1-0'}
          ]} />
        </Field>
        <div className="kpi-grid" style={{marginTop: 16}}>
          <KPI accent label="Buydown Cost" value={money(buydownCost, 0)} sub="Paid upfront" />
          <KPI label="Permanent Payment" value={money(noteP, 2)} sub={`After year ${reductions.length}`} />
        </div>
      </Card>

      <div className="results-grid">
        <Tip><strong>How it works.</strong> A {s.type} buydown reduces the rate by [{reductions.join(', ')}]% over the first {reductions.length} year{reductions.length>1?'s':''}, then jumps to the full note rate. The cost is typically paid upfront by the seller, builder, or buyer.</Tip>

        <Card title="Year-by-Year Schedule">
          <table className="tbl">
            <thead><tr><th>Year</th><th style={{textAlign:'right'}}>Rate</th><th style={{textAlign:'right'}}>Monthly Payment</th><th style={{textAlign:'right'}}>Monthly Savings</th><th style={{textAlign:'right'}}>Annual Savings</th></tr></thead>
            <tbody>
              {schedule.map(r => (
                <tr key={r.year}>
                  <td><strong>Year {r.year}</strong></td>
                  <td className="num">{pct(r.rate, 3)}</td>
                  <td className="num">{money(r.payment, 2)}</td>
                  <td className="num" style={{color:'var(--good)', fontWeight: 600}}>{money(r.savings, 2)}</td>
                  <td className="num" style={{color:'var(--good)', fontWeight: 600}}>{money(r.annual, 0)}</td>
                </tr>
              ))}
              <tr style={{fontWeight: 700}}>
                <td>Year {reductions.length + 1}+</td>
                <td className="num">{pct(s.noteRate, 3)}</td>
                <td className="num">{money(noteP, 2)}</td>
                <td className="num">—</td><td className="num">—</td>
              </tr>
            </tbody>
          </table>
        </Card>

        <div className="g2" style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap: 20}}>
          <Card title="Payment Comparison">
            <BarChart height={220}
              data={schedule.map((r, i) => ({label: `Yr ${r.year}`, value: r.payment, color: CHART_COLORS[i]}))
                .concat([{label: 'Yr ' + (reductions.length+1) + '+', value: noteP, color: 'var(--c-rose)'}])} />
          </Card>
          <Card title="Cumulative Savings">
            <LineChart series={[{color:'var(--good)', label:'Cumulative', data: cumSavings}]}
              height={220} xFormat={v => 'M' + v} yFormat={v => money(v)} />
          </Card>
        </div>

        <Card title="How Buydowns Work">
          <div style={{fontSize: 13, lineHeight: 1.7, color:'var(--text-muted)'}}>
            <p style={{marginBottom: 12}}>A temporary buydown reduces the interest rate for the first few years of a mortgage, resulting in lower initial payments.</p>
            <ul style={{paddingLeft: 20, margin: 0}}>
              <li style={{marginBottom: 8}}><strong style={{color:'var(--text)'}}>2-1 Buydown:</strong> Rate is 2% lower in year 1, 1% lower in year 2, then the full note rate from year 3 onward.</li>
              <li style={{marginBottom: 8}}><strong style={{color:'var(--text)'}}>3-2-1 Buydown:</strong> Rate is 3% lower in year 1, 2% lower in year 2, 1% lower in year 3, then full rate.</li>
              <li style={{marginBottom: 8}}><strong style={{color:'var(--text)'}}>1-0 Buydown:</strong> Rate is 1% lower in year 1 only, then the full note rate from year 2 onward.</li>
              <li style={{marginBottom: 8}}><strong style={{color:'var(--text)'}}>Who pays?</strong> The buydown cost is typically paid upfront by the seller, builder, or buyer as a lump sum at closing.</li>
              <li><strong style={{color:'var(--text)'}}>Best for:</strong> Borrowers who expect income to increase, or when sellers offer concessions to close a deal.</li>
            </ul>
          </div>
        </Card>
      </div>
    </div>
  );
}

/* -------- 11. Amortization -------- */
function AmortizationCalc() {
  const [s, setS] = useCalcState('amort', {
    loan: 750000, rate: 6.0, term: 30, preparedFor: '', address: '',
  });
  const [hoveredBar, setHoveredBar] = useState(null);
  const set = k => v => setS(p => ({...p, [k]: v}));

  const am = useMemo(() => buildAmortization(s.loan, s.rate, s.term), [s.loan, s.rate, s.term]);
  const yearly = useMemo(() => {
    const out = [];
    for (let y = 1; y <= s.term; y++) {
      const start = (y-1)*12, end = y*12;
      const slice = am.rows.slice(start, end);
      if (!slice.length) break;
      const principal = slice.reduce((s2, r) => s2 + r.principal, 0);
      const interest = slice.reduce((s2, r) => s2 + r.interest, 0);
      const last = slice[slice.length - 1];
      out.push({ year: y, payment: am.pi * 12, principal, interest, balance: last.balance });
    }
    return out;
  }, [am, s.term]);

  const stackedData = useMemo(() => {
    return yearly.map(y => ({
      label: y.year,
      principal: y.principal,
      interest: y.interest,
    }));
  }, [yearly]);

  return (
    <div className="split fade-in">
      <div>
        <Card title="Client Details" style={{marginBottom: 16}}>
          <Field label="Prepared For">
            <input type="text" value={s.preparedFor} onChange={e => set('preparedFor')(e.target.value)} placeholder="Client name" />
          </Field>
          <Field label="Property Address">
            <input type="text" value={s.address} onChange={e => set('address')(e.target.value)} placeholder="123 Main St" />
          </Field>
        </Card>
        <Card title="Loan Details">
          <Field label={`Loan Amount: ${money(s.loan)}`}>
            <NumInput prefix="$" value={s.loan} onChange={set('loan')} />
            <input type="range" className="range-slider" min={50000} max={2000000} step={5000} value={s.loan} onChange={e => set('loan')(Number(e.target.value))} />
          </Field>
          <Field label={`Interest Rate: ${pct(s.rate, 3)}`}>
            <NumInput suffix="%" value={s.rate} onChange={set('rate')} />
            <input type="range" className="range-slider" min={1} max={15} step={0.125} value={s.rate} onChange={e => set('rate')(Number(e.target.value))} />
          </Field>
          <Field label="Term (Years)"><NumInput value={s.term} onChange={set('term')} /></Field>
          <div className="kpi-grid" style={{marginTop: 14}}>
            <KPI accent label="Monthly" value={money(am.pi, 2)} />
            <KPI label="Total Interest" value={money(am.totalInterest)} />
            <KPI label="Total Cost" value={money(s.loan + am.totalInterest)} />
          </div>
        </Card>
      </div>

      <div className="results-grid">
        <Card title="Principal vs Interest Per Year" sub="Watch how each payment shifts toward principal">
          <ChartContainer height={280}
            tooltip={hoveredBar ? {x: hoveredBar.x, y: hoveredBar.y, content: (
              <div>
                <div style={{fontSize:11, opacity:0.7}}>Year {hoveredBar.d.label}</div>
                <div style={{color:'var(--accent)', fontWeight:600}}>Principal: {money(hoveredBar.d.principal, 0)}</div>
                <div style={{color:'var(--c-amber)', fontWeight:600}}>Interest: {money(hoveredBar.d.interest, 0)}</div>
                <div style={{fontWeight:700, fontFamily:'JetBrains Mono', marginTop:2}}>Total: {money(hoveredBar.d.principal + hoveredBar.d.interest, 0)}</div>
              </div>
            )} : null}
            onLeave={() => setHoveredBar(null)}>
            {({w, h, padding}) => {
              const innerW = w - padding.left - padding.right;
              const innerH = h - padding.top - padding.bottom;
              if (!stackedData.length) return null;
              const maxVal = Math.max(...stackedData.map(d => d.principal + d.interest), 1);
              const bw = innerW / stackedData.length;
              const labelEvery = Math.max(1, Math.ceil(stackedData.length / 8));
              return (
                <g>
                  {[0, 0.25, 0.5, 0.75, 1].map((t, i) => {
                    const v = maxVal * (1 - t);
                    const y = padding.top + innerH * t;
                    return (
                      <g key={i}>
                        <line x1={padding.left} x2={w - padding.right} y1={y} y2={y} stroke="var(--border)" strokeDasharray="2,3" />
                        <text x={padding.left - 8} y={y + 4} fontSize="10.5" textAnchor="end" fill="var(--text-subtle)" fontFamily="JetBrains Mono">{'$' + (v/1000).toFixed(0) + 'k'}</text>
                      </g>
                    );
                  })}
                  {stackedData.map((d, i) => {
                    const x = padding.left + bw * i + bw * 0.1;
                    const barW = bw * 0.8;
                    const intH = (d.interest / maxVal) * innerH;
                    const prinH = (d.principal / maxVal) * innerH;
                    const intY = padding.top + innerH - intH - prinH;
                    const prinY = padding.top + innerH - prinH;
                    return (
                      <g key={i} style={{cursor:'pointer'}}
                        onMouseEnter={() => setHoveredBar({i, x: x + barW/2, y: intY, d})}
                        onMouseLeave={() => setHoveredBar(null)}>
                        <rect x={x} y={padding.top} width={barW} height={innerH} fill="transparent" />
                        <rect x={x} y={intY} width={barW} height={intH} fill="var(--c-amber)" rx="2" style={{pointerEvents:'none'}} />
                        <rect x={x} y={prinY} width={barW} height={prinH} fill="var(--accent)" rx="2" style={{pointerEvents:'none'}} />
                        {d.label % labelEvery === 0 && (
                          <text x={x + barW/2} y={h - 8} fontSize="10" textAnchor="middle" fill="var(--text-muted)" fontFamily="JetBrains Mono">{d.label}</text>
                        )}
                      </g>
                    );
                  })}
                </g>
              );
            }}
          </ChartContainer>
          <div className="legend">
            <span className="legend-dot" style={{'--c': 'var(--accent)'}}>Principal</span>
            <span className="legend-dot" style={{'--c': 'var(--c-amber)'}}>Interest</span>
          </div>
        </Card>

        <Card title="Yearly Amortization Schedule">
          <table className="tbl" style={{width:'100%'}}>
            <thead><tr><th>Year</th><th style={{textAlign:'right'}}>Payment</th><th style={{textAlign:'right'}}>Principal</th><th style={{textAlign:'right'}}>Interest</th><th style={{textAlign:'right'}}>Balance</th></tr></thead>
            <tbody>
              {yearly.map(y => {
                const crossover = y.principal >= y.interest;
                const prevCrossover = y.year > 1 && yearly[y.year - 2] && yearly[y.year - 2].principal < yearly[y.year - 2].interest;
                const highlight = crossover && prevCrossover;
                return (
                  <tr key={y.year} style={highlight ? {background:'var(--accent-soft)', fontWeight:700} : {}}>
                    <td><strong>{y.year}</strong></td>
                    <td className="num">{money(y.payment, 0)}</td>
                    <td className="num" style={{color:'var(--accent)'}}>{money(y.principal, 0)}</td>
                    <td className="num" style={{color:'var(--c-amber)'}}>{money(y.interest, 0)}</td>
                    <td className="num">{money(y.balance, 0)}</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </Card>
      </div>
    </div>
  );
}

/* -------- 12. DSCR -------- */
function DSCRCalc() {
  const [s, setS] = useCalcState('dscr', {
    address: '', purchase: 450000, downPct: 20, rate: 6.875, term: 30, interestOnly: false,
    rent: 3500, taxYr: 5400, insYr: 1200, hoaMo: 0, mgmtPct: 0,
  });
  const set = k => v => setS(p => {
    const n = {...p, [k]: v};
    if (k === 'downPayment') n.downPct = p.purchase ? (v/p.purchase)*100 : 0;
    if (k === 'downPct') {} // handled below
    return n;
  });

  const downPay = s.purchase * (s.downPct / 100);
  const loan = s.purchase - downPay;
  const pi = s.interestOnly ? loan * (s.rate/100)/12 : calcPI(loan, s.rate, s.term);
  const taxMo = s.taxYr / 12;
  const insMo = s.insYr / 12;
  const mgmt = s.rent * (s.mgmtPct / 100);
  const pitia = pi + taxMo + insMo + s.hoaMo;
  const totalExpenses = pitia + mgmt;
  const dscr = pitia > 0 ? s.rent / pitia : 0;
  const monthlyCashFlow = s.rent - totalExpenses;
  const noi = (s.rent - taxMo - insMo - s.hoaMo - mgmt) * 12;
  const capRate = s.purchase ? (noi / s.purchase) * 100 : 0;

  const dscrColor = dscr >= 1.25 ? 'var(--good)' : dscr >= 1.0 ? 'var(--warn)' : 'var(--bad)';
  const dscrLabel = dscr >= 1.5 ? 'Excellent' : dscr >= 1.25 ? 'Good - Most Lenders Accept' : dscr >= 1.0 ? 'Acceptable - Break Even' : dscr >= 0.75 ? 'Below Break Even' : 'Poor - High Risk';

  return (
    <div className="split fade-in">
      <div>
        <Card title="Loan Details" style={{marginBottom: 16}}>
          <Field label="Property Address">
            <input type="text" value={s.address} onChange={e => set('address')(e.target.value)} placeholder="Property address" />
          </Field>
          <Field label="Purchase Price"><NumInput prefix="$" value={s.purchase} onChange={set('purchase')} /></Field>
          <div className="field-row">
            <Field label="Down %"><NumInput suffix="%" value={s.downPct} onChange={set('downPct')} /></Field>
            <Field label="Down $"><NumInput prefix="$" value={Math.round(downPay)} onChange={v => set('downPct')(s.purchase ? (v/s.purchase)*100 : 0)} /></Field>
          </div>
          <Field label={`Loan Amount: ${money(loan)}`}><div className="kpi" style={{padding: 8}}><span className="mono" style={{fontSize: 16, fontWeight: 700}}>{money(loan)}</span></div></Field>
          <div className="field-row">
            <Field label="Rate"><NumInput suffix="%" value={s.rate} onChange={set('rate')} /></Field>
            <Field label="Term (yr)"><NumInput value={s.term} onChange={set('term')} /></Field>
          </div>
          <Toggle value={s.interestOnly} onChange={set('interestOnly')}>Interest Only</Toggle>
        </Card>
        <Card title="Income & Expenses">
          <Field label="Gross Monthly Rent"><NumInput prefix="$" value={s.rent} onChange={set('rent')} /></Field>
          <div className="field-row">
            <Field label="Annual Taxes"><NumInput prefix="$" value={s.taxYr} onChange={set('taxYr')} /></Field>
            <Field label="Annual Insurance"><NumInput prefix="$" value={s.insYr} onChange={set('insYr')} /></Field>
          </div>
          <div className="field-row">
            <Field label="Monthly HOA"><NumInput prefix="$" value={s.hoaMo} onChange={set('hoaMo')} /></Field>
            <Field label="Management %"><NumInput suffix="%" value={s.mgmtPct} onChange={set('mgmtPct')} /></Field>
          </div>
        </Card>
      </div>

      <div className="results-grid">
        <Card>
          <div style={{textAlign:'center', padding: '8px 0'}}>
            <div style={{fontSize: 12, color:'var(--text-muted)', fontWeight: 600, textTransform:'uppercase', letterSpacing:'0.05em'}}>DSCR Score</div>
            <svg viewBox="0 0 200 120" width="260" style={{margin:'8px auto', display:'block'}}>
              <defs>
                <linearGradient id="dscr-gauge-grad" x1="0%" y1="0%" x2="100%" y2="0%">
                  <stop offset="0%" style={{stopColor:'#ef4444'}} />
                  <stop offset="40%" style={{stopColor:'#f59e0b'}} />
                  <stop offset="70%" style={{stopColor:'#10b981'}} />
                  <stop offset="100%" style={{stopColor:'#059669'}} />
                </linearGradient>
              </defs>
              <path d="M 20 100 A 80 80 0 0 1 180 100" stroke="var(--border)" strokeWidth="12" fill="none" strokeLinecap="round" />
              <path d="M 20 100 A 80 80 0 0 1 180 100" stroke="url(#dscr-gauge-grad)" strokeWidth="12" fill="none" strokeLinecap="round"
                strokeDasharray="251" strokeDashoffset={251 * (1 - Math.min(dscr / 2.5, 1))}
                style={{transition: 'stroke-dashoffset 0.6s ease'}} />
              <circle cx="100" cy="100" r="5" fill="var(--text)" />
              {(() => {
                const ang = (180 - Math.min(dscr / 2.5, 1) * 180) * Math.PI / 180;
                const nx = 100 + 70 * Math.cos(ang);
                const ny = 100 - 70 * Math.sin(ang);
                return <line x1="100" y1="100" x2={nx} y2={ny} stroke="var(--text)" strokeWidth="2.5" strokeLinecap="round" />;
              })()}
              <text x="18" y="115" fontSize="8" fill="var(--text-muted)" textAnchor="middle">0</text>
              <text x="38" y="68" fontSize="8" fill="var(--text-muted)" textAnchor="middle">0.75</text>
              <text x="62" y="40" fontSize="8" fill="var(--text-muted)" textAnchor="middle">1.0</text>
              <text x="100" y="28" fontSize="8" fill="var(--text-muted)" textAnchor="middle">1.25</text>
              <text x="138" y="40" fontSize="8" fill="var(--text-muted)" textAnchor="middle">1.5</text>
              <text x="162" y="68" fontSize="8" fill="var(--text-muted)" textAnchor="middle">2.0</text>
              <text x="182" y="115" fontSize="8" fill="var(--text-muted)" textAnchor="middle">2.5+</text>
            </svg>
            <div className="mono" style={{fontSize: 48, fontWeight: 700, color: dscrColor, lineHeight: 1, marginTop: -10}}>{dscr.toFixed(2)}</div>
            <span className="pill" style={{background: dscrColor, color: 'white', borderColor: 'transparent', marginTop: 6, display:'inline-block'}}>{dscrLabel}</span>
            <div style={{fontSize: 12, color:'var(--text-muted)', marginTop: 8}}>Target: &gt; 1.0 (Breakeven) {"\u00b7"} &gt; 1.25 (Ideal)</div>
          </div>
        </Card>

        <div className="kpi-grid">
          <KPI good={monthlyCashFlow > 0} bad={monthlyCashFlow < 0} label="Monthly Cash Flow" value={(monthlyCashFlow >= 0 ? '+' : '') + money(monthlyCashFlow, 0)} sub="After all expenses" />
          <KPI label="Annual Cash Flow" value={money(monthlyCashFlow * 12, 0)} />
          <KPI label="Net Operating Income" value={money(noi, 0)} sub="Pre-debt service" />
          <KPI label="Cap Rate" value={pct(capRate, 2)} sub="NOI / purchase" />
        </div>

        <div className="g2" style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap: 20}}>
          <Card title="Financial Breakdown">
            <table className="tbl">
              <tbody>
                <tr><td>Gross Rent</td><td className="num" style={{color:'var(--good)'}}>+{money(s.rent, 0)}</td></tr>
                <tr><td>Principal & Interest</td><td className="num">−{money(pi, 2)}</td></tr>
                <tr><td>Property Tax</td><td className="num">−{money(taxMo, 2)}</td></tr>
                <tr><td>Insurance</td><td className="num">−{money(insMo, 2)}</td></tr>
                <tr><td>HOA</td><td className="num">−{money(s.hoaMo, 2)}</td></tr>
                <tr><td>Management</td><td className="num">−{money(mgmt, 2)}</td></tr>
                <tr style={{fontWeight: 700, borderTop: '2px solid var(--border-strong)'}}>
                  <td>PITIA</td><td className="num">{money(pitia, 2)}</td>
                </tr>
              </tbody>
            </table>
          </Card>
          <Card title="Expense Composition">
            <Donut size={170} thickness={26} centerLabel="monthly" centerValue={money(totalExpenses, 0)}
              data={[
                {label: 'P&I', value: pi, color: 'var(--accent)'},
                {label: 'Tax', value: taxMo, color: 'var(--c-violet)'},
                {label: 'Insurance', value: insMo, color: 'var(--c-teal)'},
                {label: 'HOA', value: s.hoaMo, color: 'var(--c-amber)'},
                {label: 'Mgmt', value: mgmt, color: 'var(--c-rose)'},
              ]} />
          </Card>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { RentVsBuyCalc, BuydownCalc, AmortizationCalc, DSCRCalc });
/* ========== App shell: sidebar, header, dashboard, router ========== */

const NAV_ITEMS = [
  { group: 'Overview', items: [
    { id: 'dashboard', label: 'Dashboard', icon: 'home' },
    { id: 'saved', label: 'Saved Presentations', icon: 'bookmark' },
  ]},
  { group: 'Tools', items: [
    { id: 'mortgage', label: 'Mortgage', icon: 'house', component: 'MortgageCalc' },
    { id: 'comparison', label: 'Loan Comparison', icon: 'compare', component: 'ComparisonCalc' },
    { id: 'refinance', label: 'Refinance', icon: 'refresh', component: 'RefinanceCalc' },
    { id: 'preapproval', label: 'Pre-Approval', icon: 'badge', component: 'PreApprovalCalc' },
    { id: 'heloc', label: 'HELOC', icon: 'bank', component: 'HELOCCalc' },
    { id: 'extra', label: 'Extra Payment', icon: 'plus', component: 'ExtraPaymentCalc' },
    { id: 'closing', label: 'Closing Costs', icon: 'receipt', component: 'ClosingCostsCalc' },
    { id: 'flip', label: 'Fix & Flip', icon: 'tools', component: 'FixFlipCalc' },
    { id: 'rentbuy', label: 'Rent vs Buy', icon: 'trend', component: 'RentVsBuyCalc' },
    { id: 'buydown', label: 'Temp Buydown', icon: 'chart', component: 'BuydownCalc' },
    { id: 'amort', label: 'Amortization', icon: 'table', component: 'AmortizationCalc' },
    { id: 'dscr', label: 'DSCR', icon: 'dscr', component: 'DSCRCalc' },
  ]},
  { group: 'Account', items: [
    { id: 'settings', label: 'Settings', icon: 'settings' },
  ]},
];
const ALL_TOOLS = NAV_ITEMS[1].items;

const TOOL_DESCRIPTIONS = {
  mortgage: 'Monthly payment breakdown',
  comparison: 'Compare multiple scenarios',
  refinance: 'Evaluate refinance savings',
  preapproval: 'Estimate buying power',
  heloc: 'Home equity line of credit',
  extra: 'Pay off loan faster',
  closing: 'Estimate total closing costs',
  flip: 'Investment property analysis',
  rentbuy: 'Compare renting and buying',
  buydown: 'Rate buydown analysis',
  amort: 'Full amortization schedule',
  dscr: 'Debt service coverage ratio',
};

const TILE_COLORS = {
  mortgage: 'linear-gradient(135deg, var(--accent), var(--c-violet))',
  comparison: 'linear-gradient(135deg, var(--c-violet), var(--c-rose))',
  refinance: 'linear-gradient(135deg, var(--c-teal), var(--c-blue))',
  preapproval: 'linear-gradient(135deg, var(--c-amber), var(--c-rose))',
  heloc: 'linear-gradient(135deg, var(--c-emerald), var(--c-teal))',
  extra: 'linear-gradient(135deg, var(--c-blue), var(--accent))',
  closing: 'linear-gradient(135deg, var(--c-rose), var(--c-amber))',
  flip: 'linear-gradient(135deg, var(--c-amber), var(--c-emerald))',
  rentbuy: 'linear-gradient(135deg, var(--c-violet), var(--accent))',
  buydown: 'linear-gradient(135deg, var(--c-emerald), var(--accent))',
  amort: 'linear-gradient(135deg, var(--c-teal), var(--c-violet))',
  dscr: 'linear-gradient(135deg, var(--c-rose), var(--c-violet))',
};

function Sidebar({ active, setActive, collapsed, setCollapsed, mobileOpen, user, onSignOut, access }) {
  const initial = (user && (user.displayName || user.email) || '?').trim().charAt(0).toUpperCase();
  const displayName = user && (user.displayName || (user.email || '').split('@')[0]) || '';
  const [portalBusy, setPortalBusy] = useState(false);

  const handleManageSub = async () => {
    const fb = window.__firebase;
    if (!fb || !user) return;
    setPortalBusy(true);
    try {
      const url = await fb.getPortalUrl(user.uid);
      window.location.assign(url);
    } catch (e) {
      console.error('Portal link failed', e);
      alert('Could not open subscription management. Please try again.');
      setPortalBusy(false);
    }
  };

  const trialBadge = access && access.reason === 'trial' ? (
    <div onClick={() => setActive('settings')} style={{
      margin: '0 12px 8px', padding: '8px 12px',
      background: 'var(--accent-soft)', border: '1px solid var(--border)',
      borderRadius: 'var(--radius-sm)', fontSize: 11, fontWeight: 600,
      color: 'var(--accent)', textAlign: 'center', cursor: 'pointer',
      transition: 'background 0.15s'
    }}
    onMouseEnter={e => e.currentTarget.style.background = 'var(--accent-soft)'}
    onMouseLeave={e => e.currentTarget.style.background = 'var(--accent-soft)'}
    >
      {access.trialDaysLeft} day{access.trialDaysLeft !== 1 ? 's' : ''} left in free trial
    </div>
  ) : null;

  return (
    <aside className={'sidebar' + (mobileOpen ? ' mobile-open' : '')}>
      <div className="sidebar-brand">
        <div className="logo">L</div>
        <div className="brand-text-wrap">
          <div className="brand-text">Loan Officer</div>
          <div className="brand-sub">Tools Suite</div>
        </div>
        <button className="sidebar-collapse" onClick={() => setCollapsed(c => !c)} title={collapsed ? 'Expand' : 'Collapse'}>
          <Icon name={collapsed ? 'chevR' : 'menu'} size={14} />
        </button>
      </div>
      {trialBadge}
      <nav className="sidebar-nav">
        {NAV_ITEMS.map((sec, i) => (
          <Fragment key={i}>
            <div className="sidebar-section-label">{sec.group}</div>
            {sec.items.map(it => (
              <div key={it.id}
                className={"nav-item" + (active === it.id ? ' active' : '')}
                onClick={() => setActive(it.id)}
                data-label={it.label}>
                <span className="icon"><Icon name={it.icon} size={17} /></span>
                <span className="label">{it.label}</span>
              </div>
            ))}
          </Fragment>
        ))}
      </nav>
      {user && (
        <div className="sb-user" title={user.email || ''}>
          <div className="sb-user-avatar">
            {user.photoURL ? <img src={user.photoURL} alt="" /> : initial}
          </div>
          <div className="sb-user-info">
            <div className="sb-user-name">{displayName}</div>
            <div className="sb-user-mail">{user.email}</div>
          </div>
          <button className="sb-user-out" onClick={onSignOut} title="Sign out">
            <Icon name="log-out" size={15} />
          </button>
        </div>
      )}
      {user && access && access.reason === 'subscribed' && (
        <div style={{ padding: '0 12px 12px' }}>
          <button onClick={handleManageSub} disabled={portalBusy} style={{
            width: '100%', padding: '8px 12px', background: 'transparent',
            border: '1px solid var(--border)', borderRadius: 'var(--radius-sm)',
            color: 'var(--text-muted)', fontSize: 11, fontWeight: 600,
            cursor: 'pointer', fontFamily: 'inherit', transition: 'all 0.15s'
          }}
          onMouseEnter={e => { e.target.style.borderColor = 'var(--border-strong)'; e.target.style.color = 'var(--text)'; }}
          onMouseLeave={e => { e.target.style.borderColor = 'var(--border)'; e.target.style.color = 'var(--text-muted)'; }}
          >
            {portalBusy ? 'Opening…' : 'Manage Subscription'}
          </button>
        </div>
      )}
    </aside>
  );
}

function Header({ active, setActive, theme, setTheme, onSave, onPresent, savedCount, onMenuToggle }) {
  const tool = ALL_TOOLS.find(t => t.id === active);
  const isCalc = !!tool;
  const title = isCalc ? tool.label + ' Calculator' :
    active === 'dashboard' ? 'Dashboard' :
    active === 'saved' ? 'Saved Presentations' :
    active === 'settings' ? 'Settings' : 'Loan Officer Tools';
  const sub = isCalc ? TOOL_DESCRIPTIONS[active] :
    active === 'dashboard' ? 'Your mortgage toolkit overview' :
    active === 'saved' ? `${savedCount} saved item${savedCount === 1 ? '' : 's'}` :
    active === 'settings' ? 'Customize your branding and appearance' : '';

  return (
    <header className="header">
      {onMenuToggle && (
        <button className="btn btn-icon btn-ghost mobile-menu-btn" onClick={onMenuToggle} title="Menu">
          <Icon name="menu" size={18} />
        </button>
      )}
      <div className="header-title">
        <h1>{title}</h1>
        <div className="sub">{sub}</div>
      </div>
      <div className="header-actions">
        <button className="btn btn-icon btn-ghost" onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')} title="Toggle theme">
          <Icon name={theme === 'dark' ? 'sun' : 'moon'} size={16} />
        </button>
        {isCalc && (
          <Fragment>
            <button className="btn" onClick={onSave}><Icon name="save" size={14}/> <span className="label-text">Save</span></button>
            <button className="btn btn-primary" onClick={onPresent}><Icon name="play" size={14}/> <span className="label-text">Present</span></button>
          </Fragment>
        )}
      </div>
    </header>
  );
}

function useHousingWireNews() {
  const [news, setNews] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  useEffect(() => {
    const fetchNews = async () => {
      try {
        const res = await fetch('https://api.rss2json.com/v1/api.json?rss_url=' + encodeURIComponent('https://www.housingwire.com/feed/'));
        const data = await res.json();
        if (data.status === 'ok' && data.items) {
          setNews(data.items.slice(0, 8).map(item => ({
            title: item.title,
            link: item.link,
            pubDate: item.pubDate,
            source: 'HousingWire',
            author: item.author || '',
            categories: item.categories || []
          })));
        } else { setError('Failed to load news'); }
      } catch (e) { setError('Failed to fetch news'); }
      setLoading(false);
    };
    fetchNews();
    const interval = setInterval(fetchNews, 10 * 60 * 1000);
    return () => clearInterval(interval);
  }, []);
  return { news, loading, error };
}

function timeAgo(dateStr) {
  const now = new Date();
  const then = new Date(dateStr);
  const diff = Math.floor((now - then) / 1000);
  if (diff < 60) return 'just now';
  if (diff < 3600) return Math.floor(diff / 60) + ' min ago';
  if (diff < 86400) return Math.floor(diff / 3600) + 'h ago';
  return Math.floor(diff / 86400) + 'd ago';
}

function RatesWidget() {
  const ref = useRef(null);
  const [scale, setScale] = useState(1);
  const [containerW, setContainerW] = useState(654);
  const [mobile, setMobile] = useState(window.innerWidth <= 768);
  useEffect(() => {
    const mq = window.matchMedia('(max-width: 768px)');
    const handler = e => setMobile(e.matches);
    mq.addEventListener('change', handler);
    return () => mq.removeEventListener('change', handler);
  }, []);
  const DESKTOP_W = 818;
  const desktopScale = 1.5;
  const desktopNativeW = Math.round(DESKTOP_W / desktopScale);
  const mobileScale = 1.5;
  const mobileNativeW = Math.round(containerW / mobileScale);
  const mobileNativeH = Math.round(800 / mobileScale);
  const barH = 30;
  const desktopIframeH = 340;
  const iframeH = mobile ? 800 : desktopIframeH;
  const iframeSrc = mobile
    ? '//widgets.mortgagenewsdaily.com/widget/f/rates?t=expanded&sn=true&sc=true&c=336699&u=&cbu=&w=' + (mobileNativeW - 4) + '&h=' + mobileNativeH
    : 'https://widgets.mortgagenewsdaily.com/widget/f/rates?t=large&sn=true&c=336699&u=&cbu=&w=' + (desktopNativeW - 4) + '&h=340';
  useEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver(es => {
      const w = es[0].contentRect.width;
      setContainerW(w);
      setScale(Math.min(1, w / DESKTOP_W));
    });
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);
  const innerH = iframeH + barH * 2;
  return (
    <div className="rates-card" ref={ref} style={{display:'flex', flexDirection:'column'}}>
      <div style={{overflow:'hidden', borderRadius:'var(--radius-lg)', border:'1px solid var(--border)', boxShadow:'var(--shadow-sm)', flex: mobile ? undefined : 1, height: mobile ? innerH : undefined, display: mobile ? undefined : 'flex', flexDirection: mobile ? undefined : 'column'}}>
        {mobile ? (
          <div style={{width: containerW}}>
            <div style={{textAlign:'center', padding:'8px 0', background:'#336699', color:'#fff', fontSize:13, fontWeight:600, height: barH, boxSizing:'border-box', display:'flex', alignItems:'center', justifyContent:'center'}}>
              <a href="https://www.mortgagenewsdaily.com/mortgage-rates" target="_blank" rel="noopener noreferrer" style={{color:'#fff', textDecoration:'none'}}>Mortgage Rates Today</a>
            </div>
            <div style={{width: containerW, height: iframeH, overflow:'hidden'}}>
              <iframe
                src={iframeSrc}
                width={mobileNativeW} height={mobileNativeH} frameBorder="0" scrolling="no"
                style={{display:'block', border:'none', borderLeft:'2px solid #336699', borderRight:'2px solid #336699', boxSizing:'border-box', width:mobileNativeW, height:mobileNativeH, transformOrigin:'top left', transform:`scale(${mobileScale})`}}
                title="Mortgage Rates Widget"
              />
            </div>
            <div style={{textAlign:'center', padding:'8px 0', background:'#336699', color:'#fff', fontSize:12, fontWeight:500, height: barH, boxSizing:'border-box', display:'flex', alignItems:'center', justifyContent:'center'}}>
              View More <a href="https://www.mortgagenewsdaily.com/mortgage-rates" target="_blank" rel="noopener noreferrer" style={{color:'#fff', textDecoration:'none', marginLeft: 4}}>Mortgage Rates</a>
            </div>
          </div>
        ) : (
        <div style={{width: DESKTOP_W, transformOrigin:'top left', transform:`scale(${scale})`, display:'flex', flexDirection:'column', height: `${100 / scale}%`}}>
          <div style={{textAlign:'center', padding:'8px 0', background:'#336699', color:'#fff', fontSize:13, fontWeight:600, height: barH, boxSizing:'border-box', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0}}>
            <a href="https://www.mortgagenewsdaily.com/mortgage-rates" target="_blank" rel="noopener noreferrer" style={{color:'#fff', textDecoration:'none'}}>Mortgage Rates Today</a>
          </div>
          <div style={{flex:1, overflow:'hidden'}}>
            <iframe
              src={iframeSrc}
              width={desktopNativeW} frameBorder="0" scrolling="no"
              style={{display:'block', border:'none', borderLeft:'2px solid #336699', borderRight:'2px solid #336699', boxSizing:'border-box', width:desktopNativeW, height: `${100 / desktopScale}%`, transformOrigin:'top left', transform:`scale(${desktopScale})`}}
              title="Mortgage Rates Widget"
            />
          </div>
          <div style={{textAlign:'center', padding:'8px 0', background:'#336699', color:'#fff', fontSize:12, fontWeight:500, height: barH, boxSizing:'border-box', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0}}>
            View More <a href="https://www.mortgagenewsdaily.com/mortgage-rates" target="_blank" rel="noopener noreferrer" style={{color:'#fff', textDecoration:'none', marginLeft: 4}}>Mortgage Rates</a>
          </div>
        </div>
        )}
      </div>
    </div>
  );
}

function Dashboard({ setActive, rates }) {
  const [flash, setFlash] = useState({ loan: 500000, rate: 6.5 });
  const flash30 = calcPI(flash.loan, flash.rate, 30);
  const flash15 = calcPI(flash.loan, flash.rate, 15);
  const { news, loading: newsLoading, error: newsError } = useHousingWireNews();
  return (
    <div className="fade-in">
      <div className="dash-top-grid" style={{display:'grid', gridTemplateColumns:'auto 1fr', gap: 20, marginBottom: 24, alignItems:'stretch', gridAutoRows:'minmax(480px, auto)'}}>
        <RatesWidget />
        <div style={{display:'flex', flexDirection:'column', gap: 20}}>
          <Card title="Flash Mode" sub="Quick Calculator" style={{flex: 1}}>
            <Field label="Loan Amount"><NumInput prefix="$" value={flash.loan} onChange={v => setFlash(f => ({...f, loan: v}))} /></Field>
            <Field label="Interest Rate (%)"><NumInput suffix="%" value={flash.rate} onChange={v => setFlash(f => ({...f, rate: v}))} /></Field>
            <div className="kpi-grid" style={{gridTemplateColumns:'1fr', marginTop: 8}}>
              <KPI label="30-Year P&I" value={money(flash30, 2)} />
              <KPI label="15-Year P&I" value={money(flash15, 2)} />
            </div>
          </Card>
        </div>
      </div>

      <div style={{marginBottom: 24}}>
        <Card title="Mortgage Market News"
          sub="Live headlines from HousingWire"
          action={<span className="pill good">● Live</span>}>
          {newsLoading ? (
            <div style={{textAlign:'center', padding: 24, color:'var(--text-muted)'}}>
              <div style={{marginBottom: 8}}>Loading latest headlines...</div>
              <div className="mono" style={{fontSize: 11}}>Fetching HousingWire RSS</div>
            </div>
          ) : newsError ? (
            <div style={{textAlign:'center', padding: 24, color:'var(--bad)'}}>{newsError}</div>
          ) : (
            <div style={{display:'flex', flexDirection:'column', gap: 14}}>
              {news.map((n, i) => (
                <a key={i} href={n.link} target="_blank" rel="noopener noreferrer"
                  style={{display:'flex', alignItems:'flex-start', gap: 12, borderLeft:'2px solid var(--accent)', paddingLeft: 12, textDecoration:'none', color:'inherit', transition:'all 120ms', borderRadius:'0 var(--radius-sm) var(--radius-sm) 0', padding:'8px 12px 8px 12px', marginLeft: -1}}
                  onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover)'}
                  onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
                  <div style={{flex:1}}>
                    <div style={{fontSize: 13, fontWeight: 600, lineHeight: 1.4, marginBottom: 4}}>{n.title}</div>
                    <div style={{fontSize: 11, color:'var(--text-muted)', display:'flex', alignItems:'center', gap: 8}}>
                      <span style={{fontWeight: 700, letterSpacing:'0.03em', color:'var(--accent)', fontSize: 10, textTransform:'uppercase'}}>{n.source}</span>
                      <span>·</span>
                      <span>{timeAgo(n.pubDate)}</span>
                      {n.categories.length > 0 && (
                        <Fragment>
                          <span>·</span>
                          <span style={{fontSize: 10, opacity: 0.7}}>{n.categories.slice(0, 2).join(', ')}</span>
                        </Fragment>
                      )}
                    </div>
                  </div>
                  <div style={{color:'var(--text-subtle)', flexShrink:0, marginTop: 2}}>
                    <Icon name="third-party-link" size={14} />
                  </div>
                </a>
              ))}
            </div>
          )}
        </Card>
      </div>

      <div style={{marginBottom: 4, fontSize: 13, fontWeight: 700, color: 'var(--text-muted)', textTransform:'uppercase', letterSpacing:'0.05em'}}>Calculator Quick-Launch</div>
      <div style={{marginBottom: 12, fontSize: 12, color: 'var(--text-subtle)'}}>Access your complete suite of financial tools</div>
      <div className="tile-grid">
        {ALL_TOOLS.map(t => (
          <button key={t.id} className="tile" onClick={() => setActive(t.id)}>
            <div className="tile-icon" style={{background: TILE_COLORS[t.id]}}>
              <Icon name={t.icon} size={20} />
            </div>
            <div>
              <div className="tile-title">{t.label}</div>
              <div className="tile-sub">{TOOL_DESCRIPTIONS[t.id]}</div>
            </div>
          </button>
        ))}
      </div>
    </div>
  );
}

function PresentationView({ toolId, toolObj, client, scenario, note, branding = {}, onClose }) {
  const Comp = window[toolObj.component];
  const date = new Date().toLocaleDateString('en-US', { year:'numeric', month:'long', day:'numeric' });
  const initials = client ? client.split(' ').map(w => w[0]).join('').toUpperCase().slice(0,2) : '?';
  // Has any branding worth showing? If yes, render the branded header layout.
  const hasBranding = !!(branding && (branding.companyName || branding.officerName || branding.jobTitle ||
    branding.email || branding.phone || branding.nmls || branding.headshot || branding.logo));
  const officerInitials = (branding.officerName || '').split(' ').map(w => w[0]).join('').toUpperCase().slice(0, 2) || '?';
  const generatedBy = branding.companyName ? `Generated by ${branding.companyName}` : 'Generated by Loan Officer Tools';

  const handleShare = () => {
    const state = window.__calcState[toolId];
    if (!state) return;
    const b = Object.fromEntries(Object.entries(branding || {}).filter(([,v]) => v));
    const data = { t: toolId, c: client, s: scenario, st: state, b };
    const encoded = btoa(unescape(encodeURIComponent(JSON.stringify(data))));
    const url = window.location.origin + window.location.pathname + '#present=' + encoded;
    navigator.clipboard.writeText(url).then(() => {
      alert('Link copied to clipboard!');
    }).catch(() => {
      prompt('Copy this link:', url);
    });
  };

  const handlePDF = () => {
    const body = document.querySelector('.present-body');
    if (!body) { window.print(); return; }
    // Temporarily fix SVGs for print
    // Only scale chart SVGs, not small inline icons (which are typically <= 64px)
    const svgs = [...body.querySelectorAll('.chart-wrap svg[width]')]
      .concat([...body.querySelectorAll('svg[width]')].filter(s => {
        const w = parseFloat(s.getAttribute('width'));
        return w && w > 64 && !s.closest('.chart-wrap');
      }));
    const originals = [];
    svgs.forEach(svg => {
      originals.push({ el: svg, w: svg.getAttribute('width'), h: svg.getAttribute('height') });
      const w = parseFloat(svg.getAttribute('width'));
      const h = parseFloat(svg.getAttribute('height'));
      if (w && h) {
        svg.setAttribute('viewBox', svg.getAttribute('viewBox') || `0 0 ${w} ${h}`);
        svg.removeAttribute('width');
        svg.removeAttribute('height');
        svg.style.width = '100%';
        svg.style.height = 'auto';
      }
    });
    window.print();
    // Restore after print
    setTimeout(() => {
      originals.forEach(o => {
        if (o.w) o.el.setAttribute('width', o.w);
        if (o.h) o.el.setAttribute('height', o.h);
        o.el.style.width = '';
        o.el.style.height = '';
      });
    }, 500);
  };

  return (
    <div className="present-overlay fade-in">
      <div className="present-bar">
        <h2>
          <div style={{width:32, height:32, borderRadius:8, background:TILE_COLORS[toolId] || 'var(--accent)', display:'grid', placeItems:'center', color:'white'}}>
            <Icon name={toolObj.icon} size={16} />
          </div>
          {toolObj.label}
        </h2>
        <div style={{display:'flex', gap:8, alignItems:'center'}}>
          <button className="btn btn-ghost btn-sm" onClick={handleShare}><Icon name="third-party-link" size={14}/> Share</button>
          <button className="btn btn-ghost btn-sm" onClick={handlePDF}><Icon name="download" size={14}/> Export PDF</button>
          <button className="btn btn-ghost btn-sm" onClick={onClose}><Icon name="x" size={14}/> Close</button>
        </div>
      </div>
      <div className="present-body">
        {hasBranding ? (
          <div className="present-client present-client-branded">
            <div className="present-branding">
              <div className="present-branding-identity">
                {branding.headshot
                  ? <img src={branding.headshot} alt="" className="present-headshot" />
                  : <div className="present-client-avatar present-headshot present-headshot-fallback">{officerInitials}</div>}
                <div>
                  {branding.officerName && <div className="present-branding-name">{branding.officerName}</div>}
                  {branding.jobTitle && <div className="present-branding-title">{branding.jobTitle}</div>}
                  {branding.nmls && <div className="present-branding-nmls">NMLS# {branding.nmls}</div>}
                </div>
              </div>
              <div className="present-branding-contact">
                {branding.phone && <div><Icon name="phone" size={14} /> <span>{branding.phone}</span></div>}
                {branding.email && <div><Icon name="mail" size={14} /> <span>{branding.email}</span></div>}
              </div>
              {branding.logo
                ? <img src={branding.logo} alt={branding.companyName || 'Company logo'} className="present-logo" />
                : <div />}
            </div>
            <div className="present-prepared-row">
              <div>Prepared for: <strong>{client || 'Client'}</strong>{scenario ? <span style={{marginLeft: 10, color:'var(--text-subtle)'}}>· {scenario}</span> : null}</div>
              <div>{date}</div>
            </div>
            {note && <div style={{padding:'0 24px 14px', fontSize:12, color:'var(--text-muted)'}}>{note}</div>}
          </div>
        ) : (
          <div className="present-client">
            <div style={{display:'flex', flexDirection:'column', gap:4}}>
              <div className="present-client-name">
                <div className="present-client-avatar">{initials}</div>
                <div>
                  <div>{client || 'Client'}</div>
                  {scenario && <div style={{fontSize:13, fontWeight:500, color:'var(--text-muted)', marginTop:2}}>{scenario}</div>}
                </div>
              </div>
              {note && <div style={{fontSize:12, color:'var(--text-muted)', marginTop:4, marginLeft:58}}>{note}</div>}
            </div>
            <div style={{textAlign:'right', color:'var(--text-muted)', fontSize:13}}>
              <div style={{fontWeight:600}}>Prepared for: <span style={{color:'var(--text)'}}>{client || 'Client'}</span></div>
              <div style={{marginTop:2}}>{date}</div>
            </div>
          </div>
        )}
        {Comp && <Comp key={'present-' + toolId} />}
        <div className="present-disclaimer">
          <strong>Disclaimer:</strong> Your actual rate, payment, and costs could be higher; obtain an official Loan Estimate before choosing a loan. This is not a commitment to lend, and all figures are estimates based on provided information and current market rates which are subject to change. All calculations are for informational purposes only and do not constitute financial advice.
        </div>
        <div className="present-footer">
          <span>{generatedBy}</span>
          <span>{date}</span>
        </div>
      </div>
    </div>
  );
}

function SaveDialog({ tool, onSave, onCancel, mode }) {
  const [client, setClient] = useState('');
  const [scenario, setScenario] = useState(tool ? tool.label : '');
  const [note, setNote] = useState('');
  const isPresent = mode === 'present';
  const inputStyle = {width:'100%', padding:'8px 12px', border:'1px solid var(--border)', borderRadius:'var(--radius-sm)', fontSize:13, background:'var(--bg-elev)', color:'var(--text)'};
  return (
    <div style={{position:'fixed', inset:0, background:'rgba(0,0,0,0.4)', display:'grid', placeItems:'center', zIndex:100}} onClick={onCancel}>
      <div className="card" style={{width:400, maxWidth:'90vw', padding:24}} onClick={e => e.stopPropagation()}>
        <h3 className="card-title" style={{marginBottom:16}}>{isPresent ? 'Create Presentation' : 'Save Presentation'}</h3>
        <div style={{display:'flex', flexDirection:'column', gap:12}}>
          <div>
            <label style={{fontSize:12, fontWeight:600, color:'var(--text-muted)', marginBottom:4, display:'block'}}>Client Name</label>
            <input style={inputStyle} placeholder="e.g. John Smith" value={client} onInput={e => setClient(e.target.value)} autoFocus />
          </div>
          <div>
            <label style={{fontSize:12, fontWeight:600, color:'var(--text-muted)', marginBottom:4, display:'block'}}>Scenario Name</label>
            <input style={inputStyle} placeholder="e.g. Primary Residence Purchase" value={scenario} onInput={e => setScenario(e.target.value)} />
          </div>
          <div>
            <label style={{fontSize:12, fontWeight:600, color:'var(--text-muted)', marginBottom:4, display:'block'}}>Note <span style={{fontWeight:400, opacity:0.6}}>(optional)</span></label>
            <textarea style={{...inputStyle, minHeight:60, resize:'vertical', fontFamily:'inherit'}} placeholder="Any additional notes..." value={note} onInput={e => setNote(e.target.value)} />
          </div>
        </div>
        <div style={{display:'flex', gap:8, justifyContent:'flex-end', marginTop:20}}>
          <button className="btn btn-ghost" onClick={onCancel}>Cancel</button>
          <button className="btn btn-primary" onClick={() => onSave({ client, scenario, note })}>{isPresent ? 'Present' : 'Save'}</button>
        </div>
      </div>
    </div>
  );
}

function SavedView({ saved, setActive, onRemove, onLoad, onUpdate }) {
  const [editing, setEditing] = useState(null);
  if (!saved.length) {
    return (
      <div className="card fade-in" style={{textAlign:'center', padding: 60}}>
        <div style={{width: 56, height: 56, borderRadius: 14, background: 'var(--accent-soft)', display:'grid', placeItems:'center', margin: '0 auto 14px', color:'var(--accent)'}}>
          <Icon name="bookmark" size={26} />
        </div>
        <h3 className="card-title" style={{fontSize: 17}}>No saved presentations yet</h3>
        <p style={{color:'var(--text-muted)', fontSize: 13, margin: '8px 0 16px'}}>Save a calculation to see it here.</p>
        <button className="btn btn-primary" onClick={() => setActive('dashboard')}>Browse calculators</button>
      </div>
    );
  }
  const inputStyle = {width:'100%', padding:'6px 10px', border:'1px solid var(--border)', borderRadius:'var(--radius-sm)', fontSize:12, background:'var(--bg-elev)', color:'var(--text)'};
  return (
    <div className="fade-in" style={{display:'grid', gridTemplateColumns:'repeat(auto-fill, minmax(300px, 1fr))', gap: 16}}>
      {saved.map((it, i) => {
        const tool = ALL_TOOLS.find(t => t.id === it.tool);
        const isEditing = editing === i;
        return (
          <div key={i} className="card" style={{padding: 16}}>
            <div className="row-between" style={{marginBottom: 10}}>
              <div style={{display:'flex', alignItems:'center', gap: 10}}>
                <div className="tile-icon" style={{background: TILE_COLORS[it.tool] || 'var(--accent)', width:36, height:36, borderRadius:10}}>
                  <Icon name={tool ? tool.icon : 'calc'} size={18} />
                </div>
                <div>
                  <div style={{fontSize:11, color:'var(--text-muted)', fontWeight:600, textTransform:'uppercase', letterSpacing:'0.04em'}}>{tool ? tool.label : it.tool}</div>
                  <div style={{fontSize:11, color:'var(--text-subtle)'}}>{new Date(it.savedAt).toLocaleDateString()} {new Date(it.savedAt).toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'})}</div>
                </div>
              </div>
              <div style={{display:'flex', gap:4}}>
                <button className="btn btn-ghost btn-sm" title="Edit" onClick={() => setEditing(isEditing ? null : i)}><Icon name="edit" size={13}/></button>
                <button className="btn btn-ghost btn-sm" title="Delete" onClick={() => { onRemove(i); if (isEditing) setEditing(null); }}><Icon name="x" size={13}/></button>
              </div>
            </div>
            {isEditing ? (
              <div style={{display:'flex', flexDirection:'column', gap:8, marginBottom:10}}>
                <div>
                  <label style={{fontSize:11, fontWeight:600, color:'var(--text-muted)', marginBottom:2, display:'block'}}>Client Name</label>
                  <input style={inputStyle} value={it.client || ''} onInput={e => onUpdate(i, {client: e.target.value})} />
                </div>
                <div>
                  <label style={{fontSize:11, fontWeight:600, color:'var(--text-muted)', marginBottom:2, display:'block'}}>Scenario Name</label>
                  <input style={inputStyle} value={it.scenario || ''} onInput={e => onUpdate(i, {scenario: e.target.value})} />
                </div>
                <div>
                  <label style={{fontSize:11, fontWeight:600, color:'var(--text-muted)', marginBottom:2, display:'block'}}>Note</label>
                  <textarea style={{...inputStyle, minHeight:50, resize:'vertical', fontFamily:'inherit'}} value={it.note || ''} onInput={e => onUpdate(i, {note: e.target.value})} />
                </div>
                <button className="btn btn-sm" onClick={() => setEditing(null)} style={{alignSelf:'flex-end'}}>Done</button>
              </div>
            ) : (
              <div style={{marginBottom: 10}}>
                {it.client && <div style={{fontSize:14, fontWeight:700, marginBottom:2}}>{it.client}</div>}
                <div style={{fontSize:13, fontWeight:600, color:'var(--text)'}}>{it.scenario || it.name}</div>
                {it.note && <div style={{fontSize:12, color:'var(--text-muted)', marginTop:4, lineHeight:1.4}}>{it.note}</div>}
              </div>
            )}
            {it.shareUrl && (
              <div style={{marginBottom:10, display:'flex', alignItems:'center', gap:6, background:'var(--bg-sunken)', borderRadius:'var(--radius-sm)', padding:'6px 10px'}}>
                <Icon name="third-party-link" size={12} />
                <input readOnly value={it.shareUrl} style={{flex:1, border:'none', background:'transparent', fontSize:11, color:'var(--text-muted)', fontFamily:'JetBrains Mono, monospace', outline:'none', minWidth:0}} onClick={e => e.target.select()} />
                <button className="btn btn-ghost btn-sm" style={{padding:'2px 6px', fontSize:10}} onClick={() => { navigator.clipboard.writeText(it.shareUrl); }}>Copy</button>
                <button className="btn btn-ghost btn-sm" style={{padding:'2px 6px', fontSize:10, color:'var(--bad)'}} onClick={() => onUpdate(i, { shareUrl: null })}>Delete</button>
              </div>
            )}
            <div style={{display:'flex', gap:8}}>
              <button className="btn btn-sm btn-primary" style={{flex:1}} onClick={() => onLoad(it)}>Open</button>
              {!it.shareUrl && <button className="btn btn-sm" style={{flex:1}} onClick={() => {
                const b = Object.fromEntries(Object.entries(window.__lotBrandingRef || {}).filter(([,v]) => v));
                const data = { t: it.tool, c: it.client || '', s: it.scenario || '', st: it.state, b };
                const encoded = btoa(unescape(encodeURIComponent(JSON.stringify(data))));
                const url = window.location.origin + window.location.pathname + '#present=' + encoded;
                onUpdate(i, { shareUrl: url });
              }}>Generate Link</button>}
            </div>
          </div>
        );
      })}
    </div>
  );
}

function SettingsView({ branding, onSave, user, access }) {
  // Local form state — only commit to global branding on Save Settings.
  const [form, setForm] = useState(branding || {});
  const [savedFlash, setSavedFlash] = useState(false);
  const [error, setError] = useState(null);
  // Sync local form when global branding changes externally (e.g. another tab).
  useEffect(() => { setForm(branding || {}); }, [branding]);

  const set = (k) => (v) => { setForm(p => ({...p, [k]: v})); setSavedFlash(false); setError(null); };
  const setEvent = (k) => (e) => set(k)(e.target.value);

  const handleImage = (k, maxDim, mime) => async (e) => {
    const file = e.target.files && e.target.files[0];
    e.target.value = '';
    if (!file) return;
    try {
      const data = await window.imageToDataUrl(file, maxDim, mime);
      set(k)(data);
    } catch (err) {
      console.error(err);
      setError('Could not load that image. Try a different file.');
    }
  };
  const removeImage = (k) => () => set(k)(null);

  const handleSave = () => {
    const ok = onSave(form);
    if (ok === false) {
      setError('Could not save — image may be too large for browser storage. Try a smaller image.');
      return;
    }
    setSavedFlash(true);
    setTimeout(() => setSavedFlash(false), 2200);
  };

  const inputStyle = {width:'100%', padding:'10px 12px', border:'1px solid var(--border)', borderRadius:'var(--radius-sm)', fontSize:13, background:'var(--bg-elev)', color:'var(--text)', boxSizing:'border-box'};
  const labelStyle = {fontSize:13, fontWeight:600, color:'var(--text)', marginBottom:6, display:'block'};
  const hintStyle = {fontSize:11, color:'var(--text-muted)', marginTop:5};

  return (
    <div className="fade-in" style={{maxWidth: 920, display:'flex', flexDirection:'column', gap: 24}}>
      <Card
        title={<span style={{display:'inline-flex', alignItems:'center', gap:8}}><Icon name="settings" size={16} /> Branding</span>}
        sub="Customize your company branding for shared presentations">
        <div style={{display:'grid', gridTemplateColumns:'1fr', gap: 18}}>
          <div>
            <label style={labelStyle}>Company Name</label>
            <input style={inputStyle} value={form.companyName || ''} onInput={setEvent('companyName')} placeholder="ABC Mortgage" />
          </div>
          <div>
            <label style={labelStyle}>Loan Officer Name</label>
            <input style={inputStyle} value={form.officerName || ''} onInput={setEvent('officerName')} placeholder="Jane Smith" />
          </div>
          <div>
            <label style={labelStyle}>Job Title</label>
            <input style={inputStyle} value={form.jobTitle || ''} onInput={setEvent('jobTitle')} placeholder="Senior Mortgage Advisor" />
          </div>
          <div className="settings-row" style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap: 14}}>
            <div>
              <label style={labelStyle}>Contact Email</label>
              <input type="email" style={inputStyle} value={form.email || ''} onInput={setEvent('email')} placeholder="you@example.com" />
            </div>
            <div>
              <label style={labelStyle}>Contact Phone</label>
              <input type="tel" style={inputStyle} value={form.phone || ''} onInput={setEvent('phone')} placeholder="(555) 123-4567" />
            </div>
          </div>
          <div>
            <label style={labelStyle}>NMLS ID</label>
            <input style={inputStyle} value={form.nmls || ''} onInput={setEvent('nmls')} placeholder="123456" />
            <div style={hintStyle}>Your National Mortgage Licensing System ID</div>
          </div>
          <div>
            <label style={labelStyle}>Headshot Photo</label>
            <div style={{display:'flex', alignItems:'center', gap: 14, flexWrap:'wrap'}}>
              {form.headshot
                ? <img src={form.headshot} alt="headshot" style={{width: 56, height: 56, borderRadius:'50%', objectFit:'cover', border:'2px solid var(--border)'}}/>
                : <div style={{width: 56, height: 56, borderRadius:'50%', background:'var(--bg-sunken)', display:'grid', placeItems:'center', color:'var(--text-subtle)'}}><Icon name="badge" size={20} /></div>}
              <label className="btn" style={{cursor:'pointer'}}>
                <Icon name="upload" size={14}/> {form.headshot ? 'Replace Headshot' : 'Upload Headshot'}
                <input type="file" accept="image/*" style={{display:'none'}} onChange={handleImage('headshot', 400, 'image/jpeg')} />
              </label>
              {form.headshot && (
                <button className="btn btn-ghost btn-sm" style={{color:'var(--bad)'}} onClick={removeImage('headshot')}>
                  <Icon name="x" size={12}/> Remove
                </button>
              )}
            </div>
            <div style={hintStyle}>JPEG/PNG; resized to 400px max.</div>
          </div>
          <div>
            <label style={labelStyle}>Company Logo</label>
            <div style={{padding:'18px', border:'1px solid var(--border)', borderRadius:'var(--radius-md)', background:'var(--bg-sunken)', display:'flex', alignItems:'center', gap: 14, flexWrap:'wrap'}}>
              {form.logo
                ? <img src={form.logo} alt="logo" style={{maxHeight: 48, maxWidth: 220, objectFit:'contain'}}/>
                : <div style={{fontSize: 12, color:'var(--text-muted)'}}>No logo uploaded</div>}
              <label className="btn" style={{marginLeft:'auto', cursor:'pointer'}}>
                <Icon name="upload" size={14}/> {form.logo ? 'Replace Logo' : 'Upload Logo'}
                <input type="file" accept="image/*" style={{display:'none'}} onChange={handleImage('logo', 600, 'image/png')} />
              </label>
              {form.logo && (
                <button className="btn btn-ghost btn-sm" style={{color:'var(--bad)'}} onClick={removeImage('logo')}>
                  <Icon name="x" size={12}/> Remove
                </button>
              )}
            </div>
            <div style={hintStyle}>PNG (transparent backgrounds OK); resized to 600px max.</div>
          </div>
        </div>
        <div style={{display:'flex', justifyContent:'flex-end', alignItems:'center', gap: 12, marginTop: 24, flexWrap:'wrap'}}>
          {error && <span style={{fontSize: 12, color:'var(--bad)'}}>{error}</span>}
          {savedFlash && <span style={{fontSize: 12, color:'var(--good)', fontWeight: 600}}>✓ Saved</span>}
          <button className="btn btn-primary" onClick={handleSave}>Save Settings</button>
        </div>
      </Card>

      {user && <SubscriptionCard user={user} access={access} />}
    </div>
  );
}

/* ============== Subscription management card (Settings tab) ============== */
function SubscriptionCard({ user, access }) {
  const [portalBusy, setPortalBusy] = useState(false);
  const [checkoutBusy, setCheckoutBusy] = useState(false);
  const [err, setErr] = useState(null);
  const fb = window.__firebase;

  const handlePortal = async () => {
    setErr(null); setPortalBusy(true);
    try {
      const url = await fb.getPortalUrl(user.uid);
      window.location.assign(url);
    } catch (e) {
      console.error('Portal link failed', e);
      setErr('Could not open subscription management. Please try again.');
      setPortalBusy(false);
    }
  };

  const handleSubscribe = async () => {
    setErr(null); setCheckoutBusy(true);
    try {
      const url = await fb.createCheckoutSession(user.uid);
      window.location.assign(url);
    } catch (e) {
      console.error('Checkout failed', e);
      setErr(e.message || 'Failed to start checkout. Please try again.');
      setCheckoutBusy(false);
    }
  };

  const reason = access?.reason;
  const isTrial = reason === 'trial';
  const isSubscribed = reason === 'subscribed';

  const statusLabel = isTrial ? 'Free Trial' : isSubscribed ? 'Pro Plan' : 'No Active Plan';
  const statusColor = isTrial ? 'var(--warn)' : isSubscribed ? 'var(--good)' : 'var(--text-muted)';

  return (
    <Card
      title={<span style={{display:'inline-flex', alignItems:'center', gap:8}}><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="1" y="4" width="22" height="16" rx="2" ry="2"/><line x1="1" y1="10" x2="23" y2="10"/></svg> Subscription</span>}
      sub="Manage your plan and billing">
      <div style={{display:'flex', alignItems:'center', gap: 16, padding: '16px 0 8px', flexWrap:'wrap'}}>
        <div style={{flex:1, minWidth: 200}}>
          <div style={{fontSize: 13, color: 'var(--text-muted)', marginBottom: 4}}>Current Plan</div>
          <div style={{fontSize: 18, fontWeight: 700, color: 'var(--text)', display:'flex', alignItems:'center', gap: 8}}>
            {statusLabel}
            <span style={{
              fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '.05em',
              padding: '3px 8px', borderRadius: 99, background: statusColor, color: '#fff'
            }}>
              {isTrial ? `${access.trialDaysLeft} day${access.trialDaysLeft !== 1 ? 's' : ''} left` : isSubscribed ? 'Active' : 'Expired'}
            </span>
          </div>
          {isSubscribed && (
            <div style={{fontSize: 12, color: 'var(--text-muted)', marginTop: 4}}>$14.99/month</div>
          )}
          {isTrial && (
            <div style={{fontSize: 12, color: 'var(--text-muted)', marginTop: 4}}>
              Upgrade to Pro to keep access after your trial ends — $14.99/month
            </div>
          )}
        </div>
        <div style={{display:'flex', gap: 8, flexWrap:'wrap'}}>
          {isSubscribed && (
            <button className="btn" onClick={handlePortal} disabled={portalBusy}>
              {portalBusy ? 'Opening…' : 'Manage Billing'}
            </button>
          )}
          {isTrial && (
            <button className="btn btn-primary" onClick={handleSubscribe} disabled={checkoutBusy}>
              {checkoutBusy ? 'Redirecting…' : 'Upgrade to Pro — $14.99/mo'}
            </button>
          )}
        </div>
      </div>
      {err && <div style={{fontSize: 12, color: 'var(--bad)', marginTop: 8}}>{err}</div>}
      <div style={{fontSize: 11, color: 'var(--text-subtle)', marginTop: 12, paddingTop: 12, borderTop: '1px solid var(--border)'}}>
        Account: {user.email} &nbsp;·&nbsp; Payments secured by Stripe
      </div>
    </Card>
  );
}

/* ============== Auth screen ============== */
function AuthScreen() {
  const [mode, setMode] = useState('signin'); // 'signin' | 'signup' | 'reset'
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [confirm, setConfirm] = useState('');
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState(null);
  const [success, setSuccess] = useState(null);

  const fb = window.__firebase;
  const ready = !!fb;

  const friendly = (e) => {
    const code = (e && e.code) || '';
    if (code === 'auth/invalid-email') return 'That email address looks invalid.';
    if (code === 'auth/missing-password' || code === 'auth/weak-password') return 'Password must be at least 6 characters.';
    if (code === 'auth/email-already-in-use') return 'An account already exists for that email — try Sign in instead.';
    if (code === 'auth/invalid-credential' || code === 'auth/wrong-password' || code === 'auth/user-not-found') return 'Email or password is incorrect.';
    if (code === 'auth/popup-closed-by-user') return 'Sign-in was canceled.';
    if (code === 'auth/popup-blocked') return 'Your browser blocked the Google sign-in popup. Please allow popups and try again.';
    if (code === 'auth/network-request-failed') return 'Network error — check your connection.';
    return (e && e.message) || 'Something went wrong. Try again.';
  };

  const handleGoogle = async () => {
    setErr(null); setBusy(true);
    try { await fb.signInGoogle(); } catch (e) { setErr(friendly(e)); } finally { setBusy(false); }
  };

  const handleSignIn = async (ev) => {
    ev.preventDefault(); setErr(null);
    if (!email || !password) { setErr('Email and password are required.'); return; }
    setBusy(true);
    try { await fb.signInEmail(email.trim(), password); }
    catch (e) { setErr(friendly(e)); } finally { setBusy(false); }
  };

  const handleSignUp = async (ev) => {
    ev.preventDefault(); setErr(null);
    if (!email || !password) { setErr('Email and password are required.'); return; }
    if (password.length < 8) { setErr('Password must be at least 8 characters.'); return; }
    if (password !== confirm) { setErr('Passwords do not match.'); return; }
    setBusy(true);
    try { await fb.signUpEmail(email.trim(), password); }
    catch (e) { setErr(friendly(e)); } finally { setBusy(false); }
  };

  const handleReset = async (ev) => {
    ev.preventDefault(); setErr(null); setSuccess(null);
    if (!email) { setErr('Enter the email for your account.'); return; }
    setBusy(true);
    try { await fb.resetPassword(email.trim()); setSuccess('Password reset email sent. Check your inbox.'); }
    catch (e) { setErr(friendly(e)); } finally { setBusy(false); }
  };

  return (
    <div className="auth-shell fade-in">
      <div className="auth-card">
        {mode !== 'signin' && (
          <button className="auth-back" onClick={() => { setMode('signin'); setErr(null); setSuccess(null); }}>
            <Icon name="arrow-left" size={14} /> Back to sign in
          </button>
        )}
        <div className="auth-logo-wrap">
          <div className="auth-logo">
            <Icon name="calc" size={32} color="var(--accent)" />
          </div>
        </div>
        <h1 className="auth-title">
          {mode === 'signin' && 'Welcome to Loan Officer Tools'}
          {mode === 'signup' && 'Create your account'}
          {mode === 'reset' && 'Reset your password'}
        </h1>
        <div className="auth-sub">
          {mode === 'signin' && 'Sign in to continue'}
          {mode === 'signup' && 'Sign up to start using the tools'}
          {mode === 'reset' && "We'll email you a reset link"}
        </div>

        {err && <div className="auth-error">{err}</div>}
        {success && <div className="auth-success">{success}</div>}

        {(mode === 'signin' || mode === 'signup') && (
          <button className="auth-google" onClick={handleGoogle} disabled={!ready || busy}>
            <Icon name="google" size={18} /> Continue with Google
          </button>
        )}

        {(mode === 'signin' || mode === 'signup') && <div className="auth-divider">OR</div>}

        {mode === 'signin' && (
          <form onSubmit={handleSignIn}>
            <div className="auth-field">
              <label className="auth-label">Email</label>
              <div className="auth-input-wrap">
                <Icon name="mail" size={14} />
                <input className="auth-input" type="email" autoComplete="email" placeholder="you@example.com" value={email} onChange={(e) => setEmail(e.target.value)} />
              </div>
            </div>
            <div className="auth-field">
              <label className="auth-label">Password</label>
              <div className="auth-input-wrap">
                <Icon name="lock" size={14} />
                <input className="auth-input" type="password" autoComplete="current-password" placeholder="••••••••" value={password} onChange={(e) => setPassword(e.target.value)} />
              </div>
            </div>
            <button className="auth-submit" type="submit" disabled={!ready || busy}>{busy ? 'Signing in…' : 'Sign in'}</button>
            <div className="auth-foot">
              <span className="auth-link" onClick={() => { setMode('reset'); setErr(null); setSuccess(null); }}>Forgot password?</span>
              <span>Need an account? <span className="auth-link" onClick={() => { setMode('signup'); setErr(null); }}>Sign up</span></span>
            </div>
          </form>
        )}

        {mode === 'signup' && (
          <form onSubmit={handleSignUp}>
            <div className="auth-field">
              <label className="auth-label">Email</label>
              <div className="auth-input-wrap">
                <Icon name="mail" size={14} />
                <input className="auth-input" type="email" autoComplete="email" placeholder="you@example.com" value={email} onChange={(e) => setEmail(e.target.value)} />
              </div>
            </div>
            <div className="auth-field">
              <label className="auth-label">Password</label>
              <div className="auth-input-wrap">
                <Icon name="lock" size={14} />
                <input className="auth-input" type="password" autoComplete="new-password" placeholder="Min. 8 characters" value={password} onChange={(e) => setPassword(e.target.value)} />
              </div>
            </div>
            <div className="auth-field">
              <label className="auth-label">Confirm Password</label>
              <div className="auth-input-wrap">
                <Icon name="lock" size={14} />
                <input className="auth-input" type="password" autoComplete="new-password" placeholder="Re-enter password" value={confirm} onChange={(e) => setConfirm(e.target.value)} />
              </div>
            </div>
            <button className="auth-submit" type="submit" disabled={!ready || busy}>{busy ? 'Creating…' : 'Create account'}</button>
          </form>
        )}

        {mode === 'reset' && (
          <form onSubmit={handleReset}>
            <div className="auth-field">
              <label className="auth-label">Email</label>
              <div className="auth-input-wrap">
                <Icon name="mail" size={14} />
                <input className="auth-input" type="email" autoComplete="email" placeholder="you@example.com" value={email} onChange={(e) => setEmail(e.target.value)} />
              </div>
            </div>
            <button className="auth-submit" type="submit" disabled={!ready || busy}>{busy ? 'Sending…' : 'Send reset email'}</button>
          </form>
        )}
      </div>
    </div>
  );
}
window.AuthScreen = AuthScreen;

/* ============== Paywall screen ============== */
function PaywallScreen({ user, onSignOut }) {
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState(null);
  const fb = window.__firebase;

  const handleSubscribe = async () => {
    setErr(null); setBusy(true);
    try {
      const url = await fb.createCheckoutSession(user.uid);
      window.location.assign(url);
    } catch (e) {
      console.error('Checkout failed', e);
      setErr(e.message || 'Failed to start checkout. Please try again.');
      setBusy(false);
    }
  };

  return (
    <div className="auth-shell fade-in">
      <div className="auth-card" style={{ textAlign: 'center' }}>
        <div className="auth-logo-wrap">
          <div className="auth-logo">
            <svg width="38" height="38" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><rect x="1" y="4" width="22" height="16" rx="2" ry="2"/><line x1="1" y1="10" x2="23" y2="10"/></svg>
          </div>
        </div>
        <h1 className="auth-title">Your free trial has ended</h1>
        <div className="auth-sub" style={{ marginBottom: 20 }}>
          Subscribe to continue using Loan Officer Tools Suite with unlimited access to all calculators, presentations, and client tools.
        </div>

        <div style={{
          background: 'var(--bg-sunken)', border: '1px solid var(--border)',
          borderRadius: 'var(--radius-md)', padding: '24px 20px', marginBottom: 20
        }}>
          <div style={{ fontSize: 13, color: 'var(--text-muted)', marginBottom: 4, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '.04em' }}>Pro Plan</div>
          <div style={{ fontSize: 36, fontWeight: 800, color: 'var(--text)', lineHeight: 1.1 }}>
            $14.99<span style={{ fontSize: 15, fontWeight: 500, color: 'var(--text-muted)' }}>/mo</span>
          </div>
          <div style={{ fontSize: 13, color: 'var(--text-subtle)', marginTop: 8, lineHeight: 1.5 }}>
            All calculators &amp; tools &bull; Unlimited presentations<br/>
            Client-ready branding &bull; Cloud sync &bull; Cancel anytime
          </div>
        </div>

        {err && <div className="auth-error">{err}</div>}

        <button className="auth-submit" onClick={handleSubscribe} disabled={busy} style={{ fontSize: 15, padding: '14px 20px' }}>
          {busy ? 'Redirecting to checkout…' : 'Subscribe — $14.99/mo'}
        </button>

        <div style={{ marginTop: 16, fontSize: 12, color: 'var(--text-subtle)' }}>
          Secure payment via Stripe. Cancel anytime from your account.
        </div>

        <div style={{ marginTop: 20, paddingTop: 16, borderTop: '1px solid var(--border)', fontSize: 13 }}>
          <span style={{ color: 'var(--text-muted)' }}>Signed in as {user.email}</span>
          <span style={{ margin: '0 8px', color: 'var(--border-strong)' }}>·</span>
          <span className="auth-link" onClick={onSignOut}>Sign out</span>
        </div>
      </div>
    </div>
  );
}
window.PaywallScreen = PaywallScreen;

/* ============== Subscription gate (wraps App, inside AuthGate) ============== */
function SubscriptionGate({ user, children }) {
  const [access, setAccess] = useState(null); // null = loading
  const fb = window.__firebase;

  useEffect(() => {
    if (!user || !fb) return;
    let cancelled = false;

    // Initial check
    fb.checkAccess(user.uid).then(result => {
      if (!cancelled) setAccess(result);
    }).catch(e => {
      console.warn('access check failed', e);
      // On error, grant access (fail open) so the app doesn't lock out users due to Firestore glitches
      if (!cancelled) setAccess({ access: true, reason: 'error-fallback' });
    });

    // Real-time listener: if a subscription activates while on the paywall, auto-unlock
    const unsub = fb.onSubscriptionChange(user.uid, (subs) => {
      if (subs.length > 0 && !cancelled) {
        setAccess({ access: true, reason: 'subscribed', subscription: subs[0] });
      }
    });

    return () => { cancelled = true; unsub(); };
  }, [user?.uid]);

  if (!access) return <div className="auth-loading">Checking subscription…</div>;
  if (!access.access) return <PaywallScreen user={user} onSignOut={() => fb.signOutUser()} />;
  return children(access);
}
window.SubscriptionGate = SubscriptionGate;

/* ============== Auth gate wrapper around App ============== */
function AuthGate({ children }) {
  const [state, setState] = useState({ status: 'loading', user: null });
  useEffect(() => {
    let unsub = null;
    const wire = () => {
      const fb = window.__firebase;
      if (!fb) { setState({ status: 'loading', user: null }); return false; }
      unsub = fb.onAuth(async (user) => {
        if (user) {
          try { await fb.syncDown(user.uid); } catch (e) { console.warn('cloud syncDown failed', e); }
          setState({ status: 'authed', user });
        } else {
          setState({ status: 'anon', user: null });
        }
      });
      return true;
    };
    if (!wire()) {
      const handler = () => wire();
      window.addEventListener('firebase-ready', handler);
      return () => { window.removeEventListener('firebase-ready', handler); if (unsub) unsub(); };
    }
    return () => { if (unsub) unsub(); };
  }, []);
  if (state.status === 'loading') return <div className="auth-loading">Loading…</div>;
  // Allow shared presentation links without login
  if (state.status === 'anon' && window.location.hash.startsWith('#present=')) return children(null, null);
  if (state.status === 'anon') return <AuthScreen />;
  return children(state.user);
}
window.AuthGate = AuthGate;

function App({ user, access }) {
  const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
    "theme": "light"
  }/*EDITMODE-END*/;
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);

  const [active, setActive] = useState('dashboard');
  const [collapsed, setCollapsed] = useState(false);
  const [mobileOpen, setMobileOpen] = useState(false);
  // After AuthGate.syncDown(), localStorage reflects the cloud — re-read fresh.
  const [saved, setSaved] = useState(() => loadSaved());
  const [branding, setBranding] = useState(() => loadBranding());
  // Expose branding ref for SavedView's Generate Link (avoids prop drilling)
  useEffect(() => { window.__lotBrandingRef = branding; }, [branding]);
  // Re-stamp all saved shareUrls with current branding (migrates old links)
  useEffect(() => {
    const b = Object.fromEntries(Object.entries(branding || {}).filter(([,v]) => v));
    if (!Object.keys(b).length) return;
    let changed = false;
    const next = saved.map(it => {
      if (!it.shareUrl || !it.state) return it;
      const data = { t: it.tool, c: it.client || '', s: it.scenario || '', st: it.state, b };
      const encoded = btoa(unescape(encodeURIComponent(JSON.stringify(data))));
      const url = window.location.origin + window.location.pathname + '#present=' + encoded;
      if (url !== it.shareUrl) { changed = true; return { ...it, shareUrl: url }; }
      return it;
    });
    if (changed) { setSaved(next); persistSaved(next); }
  }, [branding]);
  const handleSignOut = async () => {
    try {
      await window.__firebase.signOutUser();
      // Clear local cache so next sign-in starts clean (cloud is source of truth).
      try { localStorage.removeItem(SAVED_KEY); localStorage.removeItem(BRANDING_KEY); } catch {}
    } catch (e) { console.warn(e); }
  };
  const handleSaveBranding = (form) => {
    const ok = persistBranding(form);
    if (ok) { setBranding(form); showToast('Branding saved'); }
    return ok;
  };
  const [isMobile, setIsMobile] = useState(() => typeof window !== 'undefined' && window.innerWidth <= 768);
  useEffect(() => {
    const check = () => setIsMobile(window.innerWidth <= 768);
    window.addEventListener('resize', check);
    return () => window.removeEventListener('resize', check);
  }, []);
  const [toast, setToast] = useState(null);
  const [loadKey, setLoadKey] = useState(0);

  const rates = [
    {label: '30-Yr Fixed', rate: 6.842, delta: -0.045},
    {label: '15-Yr Fixed', rate: 6.115, delta: -0.030},
    {label: '5/1 ARM', rate: 6.553, delta: 0.020},
    {label: '30-Yr Jumbo', rate: 7.021, delta: -0.035},
  ];

  useEffect(() => {
    document.documentElement.setAttribute('data-theme', tweaks.theme || 'light');
  }, [tweaks.theme]);

  const showToast = (msg) => {
    setToast(msg);
    setTimeout(() => setToast(null), 2200);
  };

  const [saveDialog, setSaveDialog] = useState(null);

  const handleSave = () => {
    const t = ALL_TOOLS.find(t => t.id === active);
    if (!t) return;
    const state = window.__calcState[active];
    if (!state) { showToast('Nothing to save'); return; }
    setSaveDialog({ toolId: active, toolObj: t, state: JSON.parse(JSON.stringify(state)) });
  };
  const completeSave = ({ client, scenario, note }) => {
    const d = saveDialog;
    if (!d) return;
    const entry = { tool: d.toolId, name: `${d.toolObj.label} — ${new Date().toLocaleDateString()}`, client, scenario, note, savedAt: Date.now(), state: d.state };
    setSaved(prev => { const next = [entry, ...prev]; persistSaved(next); return next; });
    setSaveDialog(null);
    showToast('Saved to presentations');
  };
  const handleUpdate = (idx, fields) => {
    setSaved(prev => {
      const next = prev.map((it, i) => i === idx ? {...it, ...fields} : it);
      persistSaved(next);
      return next;
    });
  };
  const [presenting, setPresenting] = useState(null);

  const handlePresent = () => {
    const t = ALL_TOOLS.find(t => t.id === active);
    if (!t) return;
    const state = window.__calcState[active];
    if (!state) { showToast('Nothing to present'); return; }
    setSaveDialog({ toolId: active, toolObj: t, state: JSON.parse(JSON.stringify(state)), mode: 'present' });
  };

  const completePresent = ({ client, scenario, note }) => {
    const d = saveDialog;
    if (!d) return;
    const b = Object.fromEntries(Object.entries(branding || {}).filter(([,v]) => v));
    const data = { t: d.toolId, c: client, s: scenario, st: d.state, b };
    const encoded = btoa(unescape(encodeURIComponent(JSON.stringify(data))));
    const shareUrl = window.location.origin + window.location.pathname + '#present=' + encoded;
    const entry = { tool: d.toolId, name: `${d.toolObj.label} — ${new Date().toLocaleDateString()}`, client, scenario, note, savedAt: Date.now(), state: d.state, shareUrl };
    setSaved(prev => { const next = [entry, ...prev]; persistSaved(next); return next; });
    window.__calcLoadState[d.toolId] = d.state;
    setPresenting({ toolId: d.toolId, toolObj: d.toolObj, client, scenario, note });
    setSaveDialog(null);
  };

  // Check URL hash for shared presentation on mount
  // Store hash-embedded branding separately so logout can't wipe it
  const [hashBranding, setHashBranding] = useState(null);
  useEffect(() => {
    const hash = window.location.hash;
    if (hash.startsWith('#present=')) {
      try {
        const data = JSON.parse(decodeURIComponent(escape(atob(hash.slice(9)))));
        const t = ALL_TOOLS.find(t => t.id === data.t);
        if (t && data.st) {
          window.__calcLoadState[data.t] = data.st;
          if (data.b && Object.keys(data.b).length) {
            setBranding(data.b);
            setHashBranding(data.b);
          }
          setPresenting({ toolId: data.t, toolObj: t, client: data.c || '', scenario: data.s || '', note: data.n || '' });
        }
      } catch {}
    }
  }, []);

  const tool = ALL_TOOLS.find(t => t.id === active);
  const Comp = tool ? window[tool.component] : null;

  return (
    <Fragment>
      <div className={'mobile-overlay' + (mobileOpen ? ' visible' : '')} onClick={() => setMobileOpen(false)} />
      <div className="app" data-collapsed={collapsed}>
        <Sidebar active={active} setActive={(id) => { setActive(id); setMobileOpen(false); }} collapsed={collapsed} setCollapsed={setCollapsed} mobileOpen={mobileOpen} user={user} onSignOut={handleSignOut} access={access} />
        <div className="main">
          <Header active={active} setActive={setActive}
            theme={tweaks.theme || 'light'} setTheme={(t) => setTweak('theme', t)}
            onSave={handleSave} onPresent={handlePresent}
            savedCount={saved.length}
            onMenuToggle={isMobile ? () => setMobileOpen(o => !o) : null} />
          <div className="content">
            {active === 'dashboard' && <Dashboard setActive={setActive} rates={rates} />}
            {active === 'saved' && <SavedView saved={saved} setActive={setActive} onRemove={(i) => setSaved(prev => { const next = prev.filter((_, idx) => idx !== i); persistSaved(next); return next; })} onLoad={(item) => { window.__calcLoadState[item.tool] = item.state; setLoadKey(k => k + 1); setActive(item.tool); }} onUpdate={handleUpdate} />}
            {active === 'settings' && <SettingsView branding={branding} onSave={handleSaveBranding} user={user} access={access} />}
            {Comp && <Comp key={active + '-' + loadKey} />}
          </div>
        </div>
      </div>

      <TweaksPanel title="Tweaks">
        <TweakSection title="Appearance">
          <TweakRadio label="Theme" value={tweaks.theme} onChange={(v) => setTweak('theme', v)}
            options={[{label:'Light', value:'light'}, {label:'Dark', value:'dark'}]} />
        </TweakSection>
      </TweaksPanel>

      {saveDialog && <SaveDialog tool={saveDialog.toolObj} mode={saveDialog.mode} onCancel={() => setSaveDialog(null)} onSave={saveDialog.mode === 'present' ? completePresent : completeSave} />}
      {presenting && <PresentationView toolId={presenting.toolId} toolObj={presenting.toolObj} client={presenting.client} scenario={presenting.scenario} note={presenting.note} branding={hashBranding || branding} onClose={() => { setPresenting(null); window.location.hash = ''; }} />}

      {toast && (
        <div style={{position:'fixed', bottom: 24, left: '50%', transform:'translateX(-50%)',
          background: 'var(--text)', color: 'var(--bg-elev)',
          padding: '10px 18px', borderRadius: 999, fontSize: 13, fontWeight: 600,
          boxShadow: 'var(--shadow-lg)', zIndex: 1000}}>
          {toast}
        </div>
      )}
    </Fragment>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(
  <AuthGate>{(user) =>
    user
      ? <SubscriptionGate user={user}>{(access) => <App user={user} access={access} />}</SubscriptionGate>
      : <App user={null} access={null} />
  }</AuthGate>
);
