/* ===============================================================
   AGENTICS.CREDIT — SHARED LIB v2
   Atoms, mock data, utilities, hooks
   =============================================================== */

const { useState, useEffect, useRef, useMemo, useCallback } = React;

/* ---------- Tiers ---------- */
const TIERS = {
  ELITE: { key: "ELITE", label: "ELITE",       short: "Elite",       color: "#1D9E75", className: "elite", apr: 15, rev: 20, ltv: 70, util: 78, backtest: 28.7, sharpe: 1.41, defaultRate: 0.0, range: [780, 850] },
  QUAL:  { key: "QUAL",  label: "QUALIFIED",   short: "Qualified",   color: "#3B82F6", className: "qual",  apr: 22, rev: 25, ltv: 60, util: 65, backtest: 34.2, sharpe: 1.18, defaultRate: 0.4, range: [640, 779] },
  EMRG:  { key: "EMRG",  label: "EMERGING",    short: "Emerging",    color: "#D97706", className: "emrg",  apr: 35, rev: 30, ltv: 50, util: 45, backtest: 41.6, sharpe: 0.94, defaultRate: 1.2, range: [540, 639] },
  SPEC:  { key: "SPEC",  label: "SPECULATIVE", short: "Speculative", color: "#DC2626", className: "spec",  apr: 60, rev: 35, ltv: 35, util: 32, backtest: 56.8, sharpe: 0.71, defaultRate: 4.8, range: [400, 539] },
};
const TIER_LIST = [TIERS.ELITE, TIERS.QUAL, TIERS.EMRG, TIERS.SPEC];

function tierForScore(s) {
  if (s >= 780) return TIERS.ELITE;
  if (s >= 640) return TIERS.QUAL;
  if (s >= 540) return TIERS.EMRG;
  if (s >= 400) return TIERS.SPEC;
  return { key: "NONE", label: "UNTIERED", short: "Untiered", color: "var(--muted)", className: "untiered", apr: 0, rev: 0, ltv: 0 };
}

/* ---------- Mock identity ---------- */
const MOCK_ADDRESS = "0x4f8e2a9b1c3d7e6f8a9b2c3d4e5f6a7b";
const MOCK_SHORT = "0x4f8e...a7b";

/* ---------- Score history ---------- */
const MOCK_SCORE_HISTORY = (() => {
  const out = [];
  for (let i = 0; i <= 90; i++) {
    let v;
    if (i < 5) v = 300 + Math.round(i * 8);
    else if (i < 25) v = 340 + Math.round(i * 4);
    else if (i < 50) v = 440 + Math.round(i * 3);
    else if (i < 75) v = 590 + Math.round((i - 50) * 4);
    else v = 690 + Math.round((i - 75) * 3.5);
    out.push({ x: i, y: v });
  }
  return out;
})();

const MOCK_SIGNALS_LIVE = [
  { label: "PROFITABILITY", v: 82 },
  { label: "DRAWDOWN",      v: 94 },
  { label: "CONSISTENCY",   v: 67 },
  { label: "LONGEVITY",     v: 81 },
  { label: "WIN_RATE",      v: 71 },
];

const MOCK_SIGNALS_DEMO = [
  { label: "PROFITABILITY", v: 78 },
  { label: "DRAWDOWN",      v: 82 },
  { label: "CONSISTENCY",   v: 58 },
  { label: "LONGEVITY",     v: 48 },
  { label: "WIN_RATE",      v: 67 },
];

/* ---------- Mock loan data (open-ended credit line) ----------
   Open-ended model: no term, no APY.
   Fees: 1% origination on new draws + 50/50 profit share daily.
   Lender principal can be recalled with vault lockup.                   */
const MOCK_LOAN = {
  creditLine: 500_000,    // approved credit line
  drawn:      387_000,    // currently deployed
  available:  113_000,    // headroom on the line
  ltv: 34.2,
  ltvLimit: 70,
  vault: "lcELITE",
  openedAt: "Mar 4 2026",
  daysOpen: 61,
};

/* ---------- Daily settlements (open-ended waterfall) ----------
   Per-day flow:
     pnl                  — agent's gross daily PnL on the line
     borrowFee            — accrued daily portion of 1% annualised on drawn balance
     profitShare          — 50% of positive-PnL days to vault (0 on losing days)
     netToAgent           — pnl − borrowFee − profitShare                              */
const MOCK_SETTLEMENTS = [
  { day: 61, date: "May 3",  pnl:  4230, borrowFee: -106, profitShare: -2115, netToAgent:  2009 },
  { day: 60, date: "May 2",  pnl:  2100, borrowFee: -106, profitShare: -1050, netToAgent:   944 },
  { day: 59, date: "May 1",  pnl:  -890, borrowFee: -106, profitShare:     0, netToAgent:  -996 },
  { day: 58, date: "Apr 30", pnl:  6440, borrowFee: -106, profitShare: -3220, netToAgent:  3114 },
  { day: 57, date: "Apr 29", pnl:  1200, borrowFee: -106, profitShare:  -600, netToAgent:   494 },
  { day: 56, date: "Apr 28", pnl:  3800, borrowFee: -106, profitShare: -1900, netToAgent:  1794 },
  { day: 55, date: "Apr 27", pnl:  2950, borrowFee: -106, profitShare: -1475, netToAgent:  1369 },
  { day: 54, date: "Apr 26", pnl: -1240, borrowFee: -106, profitShare:     0, netToAgent: -1346 },
  { day: 53, date: "Apr 25", pnl:  5800, borrowFee: -106, profitShare: -2900, netToAgent:  2794 },
  { day: 52, date: "Apr 24", pnl:  3200, borrowFee: -106, profitShare: -1600, netToAgent:  1494 },
  { day: 51, date: "Apr 23", pnl:   880, borrowFee: -106, profitShare:  -440, netToAgent:   334 },
  { day: 50, date: "Apr 22", pnl:  4100, borrowFee: -106, profitShare: -2050, netToAgent:  1944 },
];

/* ---------- P&L curve (cumulative, since loan opened) ---------- */
const MOCK_PNL_CURVE = (() => {
  const out = [];
  let v = 0;
  for (let i = 0; i <= 61; i++) {
    const d = (Math.sin(i * 0.42) * 1800) + (i * 480) + (Math.random() * 600 - 200);
    v = v + d * 0.06;
    out.push({ x: i, y: Math.max(0, v) });
  }
  return out;
})();

/* ---------- Fee history (origination + daily profit-share + borrow fee) ---------- */
const MOCK_FEE_HISTORY = [
  { date: "May 3 · 23:59",  type: "PROFIT_SHARE", desc: "50% of +$4,230 PnL",     amt:  -2115 },
  { date: "May 3 · 23:59",  type: "BORROW_FEE",   desc: "1% APR · $387K drawn",   amt:   -106 },
  { date: "May 2 · 23:59",  type: "PROFIT_SHARE", desc: "50% of +$2,100 PnL",     amt:  -1050 },
  { date: "May 2 · 23:59",  type: "BORROW_FEE",   desc: "1% APR · $387K drawn",   amt:   -106 },
  { date: "May 1 · 23:59",  type: "BORROW_FEE",   desc: "1% APR · $387K drawn",   amt:   -106 },
  { date: "Apr 30 · 23:59", type: "PROFIT_SHARE", desc: "50% of +$6,440 PnL",     amt:  -3220 },
  { date: "Apr 30 · 23:59", type: "BORROW_FEE",   desc: "1% APR · $387K drawn",   amt:   -106 },
  { date: "Apr 22 · 14:08", type: "ORIGINATION",  desc: "1% on $50K draw",        amt:   -500 },
  { date: "Mar 4 · 09:32",  type: "ORIGINATION",  desc: "1% on $337K initial",    amt:  -3370 },
];

/* ---------- Vault list ---------- */
const VAULT_LIST = [
  { ...TIERS.ELITE, deposited: 8_240_000, totalCap: 10_580_000, available: 2_340_000, maxLoan: 500_000 },
  { ...TIERS.QUAL,  deposited: 7_620_000, totalCap: 11_720_000, available: 4_100_000, maxLoan: 250_000 },
  { ...TIERS.EMRG,  deposited: 5_580_000, totalCap: 12_380_000, available: 6_800_000, maxLoan: 100_000 },
  { ...TIERS.SPEC,  deposited: 4_320_000, totalCap: 13_520_000, available: 9_200_000, maxLoan: 50_000 },
];

/* ---------- Leaderboard ---------- */
const MOCK_LEADERBOARD = [
  { rank: 1,  addr: "0x7f3a...e1c2", score: 841, tier: TIERS.ELITE, day: 94, type: "LIVE", loan: 387000 },
  { rank: 2,  addr: "0x2b9c...4d8f", score: 798, tier: TIERS.ELITE, day: 71, type: "LIVE", loan: 298000 },
  { rank: 3,  addr: "0x4e1f...92ab", score: 756, tier: TIERS.ELITE, day: 88, type: "LIVE", loan: 241000 },
  { rank: 4,  addr: "0x9a2d...77c1", score: 734, tier: TIERS.ELITE, day: 67, type: "DEMO", loan: null },
  { rank: 5,  addr: "0x1c8e...3e44", score: 698, tier: TIERS.QUAL,  day: 52, type: "DEMO", loan: 162000 },
  { rank: 6,  addr: "0x6d29...ba01", score: 689, tier: TIERS.QUAL,  day: 58, type: "LIVE", loan: 184000 },
  { rank: 7,  addr: "0xb841...0f7e", score: 681, tier: TIERS.QUAL,  day: 49, type: "LIVE", loan: 178000 },
  { rank: 8,  addr: "0x39f0...2c5a", score: 672, tier: TIERS.QUAL,  day: 61, type: "DEMO", loan: null },
  { rank: 9,  addr: "0x5e74...8810", score: 660, tier: TIERS.QUAL,  day: 44, type: "LIVE", loan: 165000 },
  { rank: 10, addr: "0xa11d...0c3b", score: 654, tier: TIERS.QUAL,  day: 39, type: "DEMO", loan: null },
  { rank: 11, addr: "0xe028...44d2", score: 641, tier: TIERS.QUAL,  day: 56, type: "LIVE", loan: 150000 },
  { rank: 47, addr: "YOU",           score: 623, tier: TIERS.QUAL,  day: 34, type: "DEMO", loan: 142000, you: true },
];

/* ---------- Demo agent state ---------- */
const MOCK_DEMO = {
  agentName: "My Momentum Strategy",
  day: 23, totalDays: 90,
  pnl: 4230, pnlPct: 8.4,
  winRate: 67.3,
  drawdown: 4.2,
  sharpe: 1.24,
  trades: 89,
  score: 587,
  creditEarned: 1240,
  creditPurchased: 500,
  projectedLoanAt60: 187000,
};

const MOCK_RECENT_TRADES = [
  { time: "14:23", market: "BTC-PERP", side: "BUY", size: 2.3, entry: 67240, exit: 68890, pnl:  230, credit: 23 },
  { time: "13:41", market: "ETH-PERP", side: "BUY", size: 2.3, entry:  3240, exit:  3198, pnl:  -62, credit:  0 },
  { time: "11:02", market: "BTC-PERP", side: "BUY", size: 2.3, entry: 66890, exit: 68120, pnl:  180, credit: 18 },
  { time: "09:48", market: "SOL-PERP", side: "BUY", size: 2.3, entry:   142, exit:   146, pnl:   95, credit: 10 },
  { time: "08:17", market: "BTC-PERP", side: "SELL", size: 2.3, entry: 68420, exit: 67980, pnl: 110, credit: 11 },
  { time: "06:33", market: "ETH-PERP", side: "BUY", size: 2.3, entry:  3180, exit:  3225, pnl:   72, credit:  7 },
];

/* ---------- $CREDIT rewards (lender side) ---------- */
const MOCK_REWARDS = {
  accruing:  1240,
  vesting:   3890,
  claimable:  450,
  vestingDay: 94, vestingTotal: 180,
  rate: 0.014,  // per second
};

const MOCK_VAULT_HISTORY = (() => {
  const out = [];
  let v = 1.0000;
  for (let i = 0; i <= 90; i++) {
    v = v + (0.00045 + Math.sin(i * 0.4) * 0.0002 + Math.random() * 0.00012);
    out.push({ x: i, y: v });
  }
  return out;
})();

/* ---------- Active withdrawals ---------- */
const MOCK_WITHDRAWALS = [
  { vault: "ELITE", shares: 5000, usdc: 5078, unlocksAt: Date.now() + (2 * 3600 + 34 * 60 + 12) * 1000, status: "READY_SOON" },
  { vault: "QUAL",  shares: 2000, usdc: 2050, unlocksAt: Date.now() + (6 * 24 * 3600 + 14 * 3600 + 22 * 60) * 1000, status: "WAITING" },
];

/* ---------- Strategy builder data ---------- */
const STRATEGY_BLOCKS = {
  triggers: [
    { id: "trg-ma",   name: "Price crosses MA",  desc: "Moving average crossover" },
    { id: "trg-rsi",  name: "RSI reaches level", desc: "Overbought or oversold" },
    { id: "trg-vol",  name: "Volume spike",      desc: "X times average" },
    { id: "trg-pct",  name: "Price % move",      desc: "Up or down X% in Y hours" },
    { id: "trg-time", name: "Time trigger",      desc: "Every X hours or specific time" },
    { id: "trg-fund", name: "Funding rate",      desc: "Goes positive or negative" },
    { id: "trg-ath",  name: "New all-time high", desc: "Asset hits new ATH or ATL" },
  ],
  conditions: [
    { id: "cnd-trend",  name: "Trend direction",   desc: "Above or below MA" },
    { id: "cnd-atr",    name: "Volatility filter", desc: "ATR threshold" },
    { id: "cnd-time",   name: "Time of day",       desc: "Trading hours filter" },
    { id: "cnd-dd",     name: "Drawdown limit",    desc: "Stop if down X% today" },
    { id: "cnd-pos",    name: "Position exists",   desc: "Only if in or not in position" },
    { id: "cnd-streak", name: "Win/loss streak",   desc: "After X consecutive wins/losses" },
    { id: "cnd-regime", name: "Market regime",     desc: "Trending vs ranging detection" },
  ],
  actions: [
    { id: "act-buy",   name: "Market buy",       desc: "X% of portfolio" },
    { id: "act-sell",  name: "Market sell",      desc: "X% of portfolio" },
    { id: "act-limit", name: "Limit order",      desc: "At price offset from current" },
    { id: "act-sl",    name: "Stop loss",        desc: "X% below entry" },
    { id: "act-tp",    name: "Take profit",      desc: "X% above entry" },
    { id: "act-trail", name: "Trailing stop",    desc: "Follows price, triggers on reversal" },
    { id: "act-close", name: "Close all",        desc: "Close all open positions" },
    { id: "act-scale", name: "Scale in",         desc: "Split into smaller orders" },
  ],
};

const STRATEGY_TEMPLATES = [
  { id: "tpl-mom",   name: "MOMENTUM FOLLOWER",  scoreD90: 734, loan: 287000, winRate: 71.3, blocks: ["trg-ma", "cnd-trend", "act-buy", "act-sl", "act-tp"] },
  { id: "tpl-mr",    name: "MEAN REVERSION",     scoreD90: 698, loan: 220000, winRate: 64.8, blocks: ["trg-rsi", "cnd-atr", "act-buy", "act-tp"] },
  { id: "tpl-mm",    name: "MARKET MAKER",       scoreD90: 712, loan: 250000, winRate: 78.2, blocks: ["trg-time", "cnd-regime", "act-limit", "act-sl"] },
  { id: "tpl-scalp", name: "TREND SCALPER",      scoreD90: 681, loan: 195000, winRate: 69.5, blocks: ["trg-vol", "cnd-trend", "act-buy", "act-trail"] },
];

/* ---------- API key + SDK snippets ---------- */
const API_KEY_PREVIEW = "ac_live_4f8e2a9b1c3d7e6f8a9b2c3d4e5f6a7b";

const SNIPPETS = {
  python: `from agentics import AgenticsClient

client = AgenticsClient(
    api_key="${API_KEY_PREVIEW}",
    wallet_address="0xYOUR_AGENT_WALLET"
)

# Your agent is scored automatically on first call
score = client.get_score()
print(f"Score: {score.value} | Tier: {score.tier}")

# Get a loan offer
offer = client.get_loan_offer()
if offer.eligible:
    loan = client.accept_loan(offer)
    print(f"Funded: \${loan.principal} USDC")`,

  javascript: `import { AgenticsClient } from '@agentics/credit';

const client = new AgenticsClient({
  apiKey: '${API_KEY_PREVIEW}',
  walletAddress: '0xYOUR_AGENT_WALLET'
});

const score = await client.getScore();
console.log(\`Score: \${score.value} | Tier: \${score.tier}\`);

const offer = await client.getLoanOffer();
if (offer.eligible) {
  const loan = await client.acceptLoan(offer);
  console.log(\`Funded: $\${loan.principal} USDC\`);
}`,

  typescript: `import { AgenticsClient, type LoanOffer } from '@agentics/credit';

const client = new AgenticsClient({
  apiKey: '${API_KEY_PREVIEW}',
  walletAddress: '0xYOUR_AGENT_WALLET' as \`0x\${string}\`,
});

const score = await client.getScore();
console.log(\`Score: \${score.value} | Tier: \${score.tier}\`);

const offer: LoanOffer = await client.getLoanOffer();
if (offer.eligible) {
  const loan = await client.acceptLoan(offer);
  console.log(\`Funded: $\${loan.principal} USDC\`);
}`,

  langchain: `from langchain.tools import tool
from agentics import AgenticsClient

client = AgenticsClient(api_key="${API_KEY_PREVIEW}")

@tool
def check_credit_score(wallet_address: str) -> dict:
    """Check credit score for an AI agent on Agentics.credit"""
    return client.get_score(wallet_address)

@tool
def request_loan(amount_usdc: float) -> dict:
    """Request an undercollateralised loan from Agentics.credit"""
    return client.request_loan(amount_usdc)

tools = [check_credit_score, request_loan]`,

  crewai: `from crewai import Agent
from agentics.crewai import AgenticsToolkit

toolkit = AgenticsToolkit(
    api_key="${API_KEY_PREVIEW}",
    wallet_address="0xYOUR_AGENT_WALLET"
)

trader = Agent(
    role="DeFi Trader",
    goal="Compound capital on Hyperliquid",
    tools=toolkit.tools(),
    backstory="Operates within an Agentics credit line."
)`,

  eliza: `// characters/trader.character.json
{
  "name": "Hyperliquid Trader",
  "plugins": ["@agentics/eliza-plugin"],
  "settings": {
    "agentics": {
      "apiKey": "${API_KEY_PREVIEW}",
      "walletAddress": "0xYOUR_AGENT_WALLET",
      "autoBorrow": true,
      "maxLtv": 0.7
    }
  }
}`,
};

const MCP_CONFIG = `{
  "mcpServers": {
    "agentics": {
      "url": "https://mcp.agentics.credit",
      "apiKey": "${API_KEY_PREVIEW}"
    }
  }
}`;

/* ===============================================================
   FORMATTERS
   =============================================================== */
const fmtUSD  = (n) => "$" + Number(n).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const fmtUSDc = (n) => "$" + Math.round(Number(n)).toLocaleString();
const fmtUSDk = (n) => {
  const v = Number(n);
  if (Math.abs(v) >= 1_000_000) return "$" + (v / 1_000_000).toFixed(2) + "M";
  if (Math.abs(v) >= 1_000)     return "$" + Math.round(v / 1000) + "K";
  return "$" + Math.round(v);
};
const fmtTok  = (n) => Number(n).toLocaleString(undefined, { maximumFractionDigits: 2 });
const fmtPct  = (n, d = 1) => Number(n).toFixed(d) + "%";
const fmtCountdown = (ms) => {
  if (ms <= 0) return "READY";
  const s = Math.floor(ms / 1000);
  const d = Math.floor(s / 86400);
  const h = Math.floor((s % 86400) / 3600);
  const m = Math.floor((s % 3600) / 60);
  const sec = s % 60;
  if (d > 0) return `${d}d ${h}h ${m}m`;
  if (h > 0) return `${h}h ${m}m ${sec.toString().padStart(2,"0")}s`;
  return `${m}m ${sec.toString().padStart(2,"0")}s`;
};

/* ===============================================================
   THEME HOOK
   =============================================================== */
function useTheme() {
  const [theme, setTheme] = useState(() => {
    try {
      const stored = localStorage.getItem("ac-theme");
      if (stored === "light" || stored === "dark") return stored;
    } catch (_) {}
    return document.documentElement.getAttribute("data-theme") || "dark";
  });
  useEffect(() => {
    document.documentElement.setAttribute("data-theme", theme);
    try { localStorage.setItem("ac-theme", theme); } catch (_) {}
  }, [theme]);
  return [theme, setTheme];
}

function ThemeToggle({ theme, onToggle }) {
  return (
    <button
      className="nav-link"
      onClick={onToggle}
      aria-label="Toggle theme"
      title="Toggle light/dark"
      style={{ background:"none", border:"1px solid transparent", cursor:"pointer", fontFamily:"inherit", padding:"8px 10px", lineHeight:1 }}
    >
      {theme === "dark" ? "☾" : "☀"}
    </button>
  );
}

/* ===============================================================
   ATOMS
   =============================================================== */

function AgenticsLogo({ size = 28 }) {
  // Use the exact SVG from chrome.js for parity with marketing site
  const html = (typeof window !== "undefined" && window.AC && window.AC.logoSVG)
    ? window.AC.logoSVG(size, false)
    : "";
  return (
    <a href="../index.html" className="logo-lockup">
      <span style={{ display:"inline-flex", width:size, height:size }} dangerouslySetInnerHTML={{ __html: html }}/>
      <span style={{ display:"flex", alignItems:"baseline", gap:2, lineHeight:1 }}>
        <span className="name">AGENTICS</span><span className="tld">.CREDIT</span>
      </span>
    </a>
  );
}

function HeroBars() {
  const heights = [22, 45, 70, 90, 105, 110, 105, 90, 70, 45, 22];
  return (
    <div className="bars">
      {heights.map((h, i) => (
        <div key={i} className="bar" style={{ height: `${(h / 110) * 100}%` }}/>
      ))}
    </div>
  );
}

function TierBadge({ tier, big }) {
  return (
    <span className={`tier-pill ${tier.className}`} style={{ fontSize: big ? 11 : 10, padding: big ? "5px 12px" : "4px 10px" }}>
      ▲ {tier.label}
    </span>
  );
}

function Card({ title, right, children, glow }) {
  return (
    <div className={`card ${glow ? "glow" : ""}`}>
      {title && (
        <div className="card-head">
          <span className="title">// {title}</span>
          {right && <span>{right}</span>}
        </div>
      )}
      <div className="card-body">{children}</div>
    </div>
  );
}

function CountUp({ to, dur = 1200, fmt = (n) => Math.round(n).toString(), className }) {
  // Initialize to target so the value is always correct on first paint,
  // even if rAF is throttled (background tabs, iframes, etc).
  const [val, setVal] = useState(to);
  const startRef = useRef(null);
  const prevToRef = useRef(to);
  useEffect(() => {
    // Only animate when the target actually changes after mount.
    if (prevToRef.current === to) return;
    const from = prevToRef.current;
    prevToRef.current = to;
    let raf;
    let timeout;
    startRef.current = null;
    const step = (t) => {
      if (!startRef.current) startRef.current = t;
      const p = Math.min(1, (t - startRef.current) / dur);
      const eased = 1 - Math.pow(1 - p, 3);
      setVal(from + (to - from) * eased);
      if (p < 1) raf = requestAnimationFrame(step);
    };
    raf = requestAnimationFrame(step);
    // Fallback: if rAF doesn't fire (throttled iframe), snap to target.
    timeout = setTimeout(() => setVal(to), dur + 250);
    return () => { cancelAnimationFrame(raf); clearTimeout(timeout); };
  }, [to, dur]);
  return <span className={className}>{fmt(val)}</span>;
}

function ScoreDial({ score, max = 850, min = 300, size = 220, label }) {
  const pct = Math.max(0, Math.min(1, (score - min) / (max - min)));
  const r = (size / 2) - 12;
  const cx = size / 2, cy = size / 2;
  const C = 2 * Math.PI * r;
  const start = -Math.PI * 0.78;
  const end   =  Math.PI * 0.78;
  const sweep = end - start;
  const trackArc = (sweep / (2 * Math.PI)) * C;
  const fillArc  = trackArc * pct;

  return (
    <div className="score-dial-wrap" style={{ width: size, height: size }}>
      <svg width={size} height={size} style={{ display: "block", transform: "rotate(-90deg)" }}>
        <circle cx={cx} cy={cy} r={r}
                fill="none" stroke="var(--bg-3)" strokeWidth="10"
                strokeDasharray={`${trackArc} ${C - trackArc}`}
                strokeDashoffset={-((C - trackArc) / 2 - (C / 4))}
                strokeLinecap="round"/>
        <circle cx={cx} cy={cy} r={r}
                fill="none" stroke="var(--accent)" strokeWidth="10"
                strokeDasharray={`${fillArc} ${C - fillArc}`}
                strokeDashoffset={-((C - trackArc) / 2 - (C / 4))}
                strokeLinecap="round"
                style={{ transition: "stroke-dasharray 1.2s cubic-bezier(0.16, 1, 0.3, 1)", filter: "drop-shadow(0 0 6px var(--accent-glow))" }}/>
      </svg>
      <div className="num">
        <span className="big"><CountUp to={score}/></span>
        <span className="small">{label || "AGENT_SCORE"}</span>
      </div>
    </div>
  );
}

function ProgressBar({ value, max = 100, color = "var(--accent)", height = 6 }) {
  const pct = Math.max(0, Math.min(100, (value / max) * 100));
  return (
    <div className="bar" style={{ height, background: "var(--bg-3)", borderRadius: 3, overflow: "hidden" }}>
      <div className="fill" style={{ width: `${pct}%`, height: "100%", background: color, transition: "width 800ms cubic-bezier(0.4, 0, 0.2, 1)" }}/>
    </div>
  );
}

/* ===============================================================
   CHARTS — hand-rolled SVG
   =============================================================== */

function AreaChart({ data, height = 160, yMin, yMax, color = "var(--accent)" }) {
  const w = 600;
  const ys = data.map(d => d.y);
  const lo = yMin ?? Math.min(...ys);
  const hi = yMax ?? Math.max(...ys);
  const xs = data.map(d => d.x);
  const xlo = Math.min(...xs), xhi = Math.max(...xs);
  const xRange = xhi - xlo || 1;
  const pad = 8;
  const px = (x) => pad + ((x - xlo) / xRange) * (w - pad * 2);
  const py = (y) => height - pad - ((y - lo) / (hi - lo || 1)) * (height - pad * 2);
  const path = data.map((d, i) => `${i === 0 ? "M" : "L"} ${px(d.x).toFixed(2)} ${py(d.y).toFixed(2)}`).join(" ");
  const area = path + ` L ${px(xhi).toFixed(2)} ${height - pad} L ${px(xlo).toFixed(2)} ${height - pad} Z`;
  const id = "g_" + Math.random().toString(36).slice(2, 8);
  return (
    <svg viewBox={`0 0 ${w} ${height}`} width="100%" height={height} preserveAspectRatio="none" style={{ display: "block" }}>
      <defs>
        <linearGradient id={id} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={color} stopOpacity="0.35"/>
          <stop offset="100%" stopColor={color} stopOpacity="0"/>
        </linearGradient>
      </defs>
      {[0.25, 0.5, 0.75].map((p, i) => (
        <line key={i} x1="0" y1={height * p} x2={w} y2={height * p} stroke="rgba(255,255,255,0.05)" strokeWidth="1"/>
      ))}
      <path d={area} fill={`url(#${id})`}/>
      <path d={path} fill="none" stroke={color} strokeWidth="1.5"/>
    </svg>
  );
}

/* ===============================================================
   MOCK WALLET CONNECT
   =============================================================== */

const WALLETS = [
  { id: "metamask",  name: "MetaMask",     glyph: "M", color: "#F6851B" },
  { id: "rainbow",   name: "Rainbow",      glyph: "R", color: "#0095FF" },
  { id: "coinbase",  name: "Coinbase",     glyph: "C", color: "#0052FF" },
  { id: "wc",        name: "WalletConnect",glyph: "W", color: "#3B99FC" },
];

function ConnectButton({ account, onConnect, onDisconnect }) {
  const [open, setOpen] = useState(false);
  if (account) {
    return (
      <button
        className="nav-link cta"
        onClick={() => onDisconnect()}
        style={{ cursor:"pointer", fontFamily:"inherit", display:"inline-flex", alignItems:"center", gap:8 }}
        title="Disconnect"
      >
        <span style={{ display:"inline-flex", width:14, height:14, alignItems:"center", justifyContent:"center", background:account.color, color:"#000", fontSize:9, fontWeight:700, borderRadius:2 }}>{account.glyph}</span>
        [{MOCK_SHORT}]
      </button>
    );
  }
  return (
    <>
      <button
        className="nav-link cta"
        onClick={() => setOpen(true)}
        style={{ cursor:"pointer", fontFamily:"inherit" }}
      >[CONNECT_WALLET]</button>
      {open && (
        <div className="modal-backdrop" onClick={() => setOpen(false)}>
          <div className="modal" style={{ width: 440, padding: 20 }} onClick={(e) => e.stopPropagation()}>
            <div style={{ fontFamily: "'DM Mono', monospace", fontSize: 11, letterSpacing: 1.5, color: "var(--accent)", marginBottom: 4 }}>// CONNECT_WALLET</div>
            <div style={{ fontSize: 17, fontWeight: 600, marginBottom: 16 }}>Connect to Agentics.credit</div>
            <div className="wallets-grid">
              {WALLETS.map(w => (
                <button key={w.id} className="wallet-opt" onClick={() => { onConnect(w); setOpen(false); }}>
                  <span className="glyph" style={{ background: w.color }}>{w.glyph}</span>
                  <span>{w.name}</span>
                </button>
              ))}
            </div>
            <div style={{ marginTop: 16, fontSize: 11, color: "var(--muted)", textAlign: "center" }}>
              No wallet? <a href="#">Get one →</a>
            </div>
          </div>
        </div>
      )}
    </>
  );
}

/* ===============================================================
   TOP NAV (shared across all logged-in views)
   =============================================================== */

function TopNav({ account, onConnect, onDisconnect, role, tab, onTab, theme, onToggleTheme }) {
  let tabs = [];
  if (role === "agent") tabs = [
    { id: "dashboard", label: "MY_AGENTS" },
    { id: "strategy",  label: "STRATEGY_BUILDER" },
    { id: "leaderboard", label: "LEADERBOARD" },
    { id: "developer", label: "DEVELOPER" },
  ];
  else if (role === "lender") tabs = [
    { id: "dashboard", label: "MY_VAULTS" },
    { id: "leaderboard", label: "LEADERBOARD" },
  ];
  else if (role === "developer") tabs = [
    { id: "developer", label: "DEVELOPER" },
    { id: "dashboard", label: "MY_AGENTS" },
    { id: "strategy",  label: "STRATEGY_BUILDER" },
    { id: "leaderboard", label: "LEADERBOARD" },
  ];

  return (
    <nav className="term-nav">
      <AgenticsLogo size={28}/>
      <div className="nav-links">
        {tabs.map(t => (
          <button
            key={t.id}
            className={"nav-link" + (tab === t.id ? " active" : "")}
            onClick={() => onTab(t.id)}
            style={{ background:"none", cursor:"pointer", fontFamily:"inherit" }}
          >[{t.label}]</button>
        ))}
        <span className="nav-link" style={{ display:"inline-flex", alignItems:"center", gap:6, padding:"8px 12px", border:"1px solid var(--border)" }}>
          <span className="led"></span>BASE_SEPOLIA
        </span>
        <ThemeToggle theme={theme} onToggle={onToggleTheme}/>
        <ConnectButton account={account} onConnect={onConnect} onDisconnect={onDisconnect}/>
      </div>
    </nav>
  );
}

/* ===============================================================
   TOAST SYSTEM
   =============================================================== */

const ToastCtx = React.createContext(null);

function ToastProvider({ children }) {
  const [toasts, setToasts] = useState([]);
  const push = useCallback((t) => {
    const id = Math.random().toString(36).slice(2);
    setToasts(prev => [...prev, { id, ...t }]);
    setTimeout(() => setToasts(prev => prev.filter(x => x.id !== id)), t.duration || 4000);
  }, []);
  return (
    <ToastCtx.Provider value={{ push }}>
      {children}
      <div className="toast-stack">
        {toasts.map(t => (
          <div key={t.id} className="toast">
            <div className="title">{t.title}</div>
            {t.meta && <div className="meta">{t.meta}</div>}
          </div>
        ))}
      </div>
    </ToastCtx.Provider>
  );
}
const useToast = () => React.useContext(ToastCtx);

/* ===============================================================
   EXPORTS
   =============================================================== */
Object.assign(window, {
  // core
  useState, useEffect, useRef, useMemo, useCallback,
  // data
  TIERS, TIER_LIST, MOCK_ADDRESS, MOCK_SHORT,
  MOCK_SCORE_HISTORY, MOCK_SIGNALS_LIVE, MOCK_SIGNALS_DEMO,
  MOCK_LOAN, MOCK_SETTLEMENTS, MOCK_PNL_CURVE, MOCK_FEE_HISTORY, VAULT_LIST, MOCK_LEADERBOARD,
  MOCK_DEMO, MOCK_RECENT_TRADES, MOCK_REWARDS, MOCK_VAULT_HISTORY, MOCK_WITHDRAWALS,
  STRATEGY_BLOCKS, STRATEGY_TEMPLATES,
  API_KEY_PREVIEW, SNIPPETS, MCP_CONFIG, WALLETS,
  // helpers
  tierForScore, fmtUSD, fmtUSDc, fmtUSDk, fmtTok, fmtPct, fmtCountdown,
  // hooks
  useTheme, useToast,
  // components
  AgenticsLogo, HeroBars, TierBadge, Card, CountUp, ScoreDial, ProgressBar,
  AreaChart, ConnectButton, TopNav, ThemeToggle, ToastProvider,
});
