'use client'

import { useState, useEffect, useCallback } from 'react'
import { useLocale } from 'next-intl'
import { useAccount, useConnect, useSignMessage } from 'wagmi'
import {
  getUserPublic,
  getUser,
  getTelegramStatus,
  setUserSettings,
  setHlApiKey,
  setManualWindow,
  setAutoTrade,
  subscribe,
  type UserSettings,
} from '@/lib/api'
import { useDashboardStore } from '@/store/dashboard'
import {
  signRequest,
  getOrCreateViewEnvelope,
  getCachedViewEnvelope,
} from '@/lib/signedRequest'
import { walletErrorLabel } from '@/lib/walletError'
import { getFirstReadyConnector, walletConnectErrorLabel } from '@/lib/walletConnect'
import { confirmSign } from '@/components/wallet/SignConfirmSheet'

const DEFAULT_SETTINGS: UserSettings = {
  leverage: 3, position_size_usd: 20, take_profit_pct: 2, stop_loss_pct: 1.5,
  min_confidence: 70, daily_budget_usd: 15, sys2_leverage: 2,
  sys2_mode: 'standard',
  active_from: null, active_until: null,
  trump_enabled: false, macro_enabled: false,
}

// The backend treats active_from/active_until as a DAILY RECURRING time window
// (it extracts only .time() and compares with the current UTC time). Storing a
// full datetime-local value was misleading — the date part was silently ignored
// so users thought they were setting a date range when they were setting a
// time-of-day schedule. We now use type="time" and store as a fixed-epoch ISO
// so the backend's .time() extraction gives the right HH:MM:SS.
function isoToLocalInput(iso: string | null): string {
  if (!iso) return ''
  const d = new Date(iso)
  if (isNaN(d.getTime())) return ''
  const pad = (n: number) => String(n).padStart(2, '0')
  // Return just HH:MM for the time input
  return `${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}`
}
function localInputToIso(v: string): string | null {
  if (!v) return null
  // v is HH:MM from <input type="time">. Store as 2000-01-01T{HH:MM}:00Z so
  // the backend's .time() extraction returns the correct UTC time.
  const [hh, mm] = v.split(':')
  if (!hh || !mm) return null
  return `2000-01-01T${hh.padStart(2,'0')}:${mm.padStart(2,'0')}:00Z`
}

export default function BotConfigPanel() {
  useLocale() // keep next-intl context hydrated even though i18n is shelved
  const { address, isConnected } = useAccount()
  const { connectAsync, connectors } = useConnect()
  const { signMessageAsync } = useSignMessage()
  const {
    isSubscribed, hlApiKeySet, hlApiKeyMasked,
    setSubscribed, setBotReadiness, setHlApiKeySet, setPaperMode: setPaperModeStore,
  } = useDashboardStore()

  const [settings, setSettings] = useState<UserSettings>(DEFAULT_SETTINGS)
  const [tpConfigured, setTpConfigured]         = useState(false)
  const [slConfigured, setSlConfigured]         = useState(false)
  const [useBudget, setUseBudget]               = useState(false)
  const [useSchedule, setUseSchedule]           = useState(false)
  const [fromLocal, setFromLocal]               = useState('')
  const [untilLocal, setUntilLocal]             = useState('')
  const [dirty, setDirty]                       = useState(false)
  const [saveState, setSaveState]               = useState<'idle'|'signing'|'saving'|'ok'|'err'>('idle')
  const [saveErr, setSaveErr]                   = useState('')
  const [apiKey, setApiKey]                     = useState('')
  const [keyState, setKeyState]                 = useState<'idle'|'signing'|'saving'|'ok'|'err'>('idle')
  const [keyErr, setKeyErr]                     = useState('')
  const [subState, setSubState]                 = useState<'idle'|'signing'|'saving'|'err'>('idle')
  const [subErr, setSubErr]                     = useState('')
  const [subPaperChoice, setSubPaperChoice]     = useState<'paper' | 'live'>('paper')
  const [manualUntil, setManualUntil]           = useState<string | null>(null)
  const [mwState, setMwState]                   = useState<'idle'|'signing'|'saving'|'err'>('idle')
  const [mwErr, setMwErr]                       = useState('')
  const [mwTick, setMwTick]                     = useState(0)
  const [paperMode, setPaperMode]               = useState(false)
  const [autoTrade, setAutoTrade_]              = useState(false)
  const [atState, setAtState]                   = useState<'idle'|'signing'|'saving'|'err'>('idle')
  const [showAdvanced, setShowAdvanced]         = useState(false)
  const [mounted, setMounted]                   = useState(false)
  const [loadState, setLoadState]               = useState<'idle'|'loading'|'loaded'|'err'>('idle')
  const [loadErr, setLoadErr]                   = useState('')
  const [connectErr, setConnectErr]             = useState('')
  // Telegram binding state. Macro Vibes (System-2) is manage-only via the
  // Telegram /adopt command, so a Macro-only user who hasn't bound Telegram
  // cannot actually hand any position to the bot — readiness must reflect that.
  // The unauthenticated status call returns only { bound } (no chat_id), which
  // is all we need here.
  const [tgBound, setTgBound]                   = useState<boolean | null>(null)

  useEffect(() => { setMounted(true) }, [])

  useEffect(() => {
    if (!address) { setTgBound(null); return }
    let cancelled = false
    getTelegramStatus(address.toLowerCase())
      .then(s => { if (!cancelled) setTgBound(!!s.bound) })
      .catch(() => { if (!cancelled) setTgBound(null) })
    return () => { cancelled = true }
  }, [address])

  // B33: when the connected wallet changes, immediately wipe all private
  // settings so the previous wallet's config never leaks into the new one.
  useEffect(() => {
    setSettings(DEFAULT_SETTINGS)
    setPaperMode(false)
    setAutoTrade_(false)
    setTpConfigured(false)
    setSlConfigured(false)
    setUseBudget(false)
    setUseSchedule(false)
    setFromLocal('')
    setUntilLocal('')
    setManualUntil(null)
    setApiKey('')
    setDirty(false)
    setSaveState('idle'); setSaveErr('')
    setKeyState('idle');  setKeyErr('')
    setSubState('idle');  setSubErr('')
    setLoadState('idle'); setLoadErr('')
    setConnectErr('')
  }, [address])

  function applyUserPayload(u: {
    active: boolean
    hl_api_key_set: boolean
    hl_api_key_masked: string | null
    paper_mode?: boolean
    auto_trade?: boolean
    settings: UserSettings
    manual_window_until?: string | null
  }) {
    setSubscribed(u.active)
    setPaperMode(!!u.paper_mode)
    setPaperModeStore(!!u.paper_mode)   // sync to global store for B40/B41
    setAutoTrade_(!!u.auto_trade)
    setHlApiKeySet(u.hl_api_key_set, u.hl_api_key_masked ?? undefined)
    setManualUntil(u.manual_window_until ?? null)
    if (u.settings) {
      const s = u.settings
      setTpConfigured(s.take_profit_pct != null)
      setSlConfigured(s.stop_loss_pct != null)
      const hasBudget = s.daily_budget_usd != null
      setUseBudget(hasBudget)
      setBotReadiness(u.hl_api_key_set ? 'saved' : 'unknown')
      setSettings({
        ...s,
        take_profit_pct:  s.take_profit_pct  ?? 2,
        stop_loss_pct:    s.stop_loss_pct    ?? 1.5,
        daily_budget_usd: s.daily_budget_usd ?? 15,
        trump_enabled:    s.trump_enabled    ?? false,
        macro_enabled:    s.macro_enabled    ?? false,
      })
      const hasSched = s.active_from != null && s.active_until != null
      setUseSchedule(hasSched)
      setFromLocal(isoToLocalInput(s.active_from))
      setUntilLocal(isoToLocalInput(s.active_until))
    }
  }

  useEffect(() => {
    if (!mounted || !isConnected || !address) return
    let cancelled = false
    ;(async () => {
      const pub = await getUserPublic(address.toLowerCase()).catch(() => null)
      if (!pub || cancelled) return
      setSubscribed(pub.active)
      setHlApiKeySet(pub.hl_api_key_set)
      setBotReadiness(pub.hl_api_key_set ? 'saved' : 'unknown')
      setAutoTrade_(!!pub.auto_trade)
      if (!pub.active) return
      const cached = getCachedViewEnvelope('view_user', address)
      if (!cached) return
      const u = await getUser(address, cached).catch(() => null)
      if (!u || cancelled) return
      applyUserPayload(u)
      setLoadState('loaded')
    })()
    return () => { cancelled = true }
  }, [address, isConnected, mounted]) // eslint-disable-line react-hooks/exhaustive-deps

  async function handleLoadSettings() {
    if (!address) return
    setLoadErr(''); setLoadState('loading')
    try {
      // Read-only identity check — go straight to MetaMask, no pre-confirm sheet.
      // getOrCreateViewEnvelope caches the result for 4 min so repeat visits are free.
      const env = await getOrCreateViewEnvelope({ action: 'view_user', wallet: address, signMessageAsync })
      const u = await getUser(address, env)
      applyUserPayload(u)
      setLoadState('loaded')
    } catch (e: unknown) {
      setLoadErr(walletErrorLabel(e, 'Cancelled', 120))
      setLoadState('err')
    }
  }

  async function refreshUserState(wallet: string) {
    const pub = await getUserPublic(wallet.toLowerCase())
    setSubscribed(pub.active)
    setHlApiKeySet(pub.hl_api_key_set)
    setBotReadiness(pub.hl_api_key_set ? 'saved' : 'unknown')
    setAutoTrade_(!!pub.auto_trade)
    return pub
  }

  async function flipAutoTrade(on: boolean) {
    if (!address || atState !== 'idle') return
    const ok = await confirmSign({
      label: on ? 'Enable Auto-Trade' : 'Disable Auto-Trade',
      description: on
        ? 'Bot will open real positions on Hyperliquid when qualifying Trump signals fire. Stop-loss and de-risking remain active.'
        : 'Bot stops opening new trades. Risk controls on existing positions keep running.',
      danger: on,
    })
    if (!ok) return
    setAtState('signing')
    try {
      const env = await signRequest({ action: 'set_auto_trade', wallet: address, body: { enabled: on }, signMessageAsync })
      setAtState('saving')
      const r = await setAutoTrade(env, on)
      setAutoTrade_(r.auto_trade)
      setAtState('idle')
    } catch (e: unknown) {
      console.error('set_auto_trade failed', e)
      setAtState('err')
      setTimeout(() => setAtState('idle'), 3000)
    }
  }

  function updateSettings(patch: Partial<UserSettings>) {
    setSettings(s => ({ ...s, ...patch }))
    setDirty(true)
  }

  async function handleSubscribe() {
    if (!address) return
    const paperMode = subPaperChoice === 'paper'
    const ok = await confirmSign({
      label: paperMode ? 'Start in paper mode' : 'Start live trading',
      description: paperMode
        ? 'Trades are simulated end-to-end — no funds are at risk. You can switch to live at any time from the settings panel.'
        : 'Start live trading. You\'ll need to add a Hyperliquid API key (next step) before the bot can open any real position.',
      danger: !paperMode,
    })
    if (!ok) return
    setSubErr(''); setSubState('signing')
    try {
      const signedBody = paperMode ? { paper_mode: true } : null
      const env = await signRequest({ action: 'subscribe', wallet: address, body: signedBody, signMessageAsync })
      setSubState('saving')
      await subscribe(env, { paper_mode: paperMode })
      setSubscribed(true)
      setPaperMode(paperMode)
      void refreshUserState(address)
      setSubState('idle')
      setTpConfigured(false); setSlConfigured(false)
      setUseBudget(false); setLoadState('loaded')
    } catch (e: unknown) {
      setSubErr(walletErrorLabel(e, 'Cancelled', 100))
      setSubState('err')
    }
  }

  async function handleSaveSettings() {
    if (!address) return
    const ok = await confirmSign({
      label: 'Save trading settings',
      description: 'Applied to all new trades going forward. Open positions are not affected.',
    })
    if (!ok) return
    setSaveErr(''); setSaveState('signing')
    try {
      let schedFrom: string | null = null
      let schedUntil: string | null = null
      if (useSchedule) {
        schedFrom  = localInputToIso(fromLocal)
        schedUntil = localInputToIso(untilLocal)
        if (!schedFrom || !schedUntil) { setSaveErr('Pick both a start and end time'); setSaveState('err'); return }
        // Cross-midnight windows (e.g. 22:00–02:00) are VALID — the backend's
        // _is_in_active_window treats af_t > au_t as a wrap-around window
        // (now >= from OR now <= until). Only reject when start === end, which
        // would describe a zero-length (or full-day-ambiguous) window.
        if (new Date(schedUntil).getTime() === new Date(schedFrom).getTime()) { setSaveErr('Start and end can’t be the same time'); setSaveState('err'); return }
      }
      const tp = settings.take_profit_pct, sl = settings.stop_loss_pct, bd = settings.daily_budget_usd
      if (trumpOn) {
        if (tp == null || tp < 0.1 || tp > 50) { setSaveErr('Take profit must be 0.1–50%'); setSaveState('err'); return }
        if (sl == null || sl < 0.1 || sl > 50) { setSaveErr('Stop loss must be 0.1–50%');  setSaveState('err'); return }
      }
      if (useBudget && (bd == null || bd <= 0 || bd > 100000)) { setSaveErr('Daily budget must be > $0'); setSaveState('err'); return }
      const payload: UserSettings = { ...settings, daily_budget_usd: useBudget ? bd : null, active_from: schedFrom, active_until: schedUntil }
      const env = await signRequest({ action: 'set_settings', wallet: address, body: payload, signMessageAsync })
      setSaveState('saving')
      await setUserSettings(env, payload)
      setSettings(payload)
      setTpConfigured(tp != null); setSlConfigured(sl != null)
      setBotReadiness('saved'); setDirty(false)
      setSaveState('ok')
      setTimeout(() => setSaveState('idle'), 2500)
    } catch (e: unknown) {
      setSaveErr(walletErrorLabel(e, 'Cancelled', 100))
      setSaveState('err')
    }
  }

  async function handleSaveKey() {
    if (!address || !apiKey.trim()) return
    const trimmed = apiKey.trim()
    if (!trimmed.startsWith('0x') || trimmed.length !== 66) {
      setKeyErr('Must be 0x + 64 hex chars'); setKeyState('err'); return
    }
    const ok = await confirmSign({
      label: 'Link Hyperliquid API key',
      description: 'Stored encrypted on the server. Used by the bot to open and close positions only — no withdrawal access.',
      danger: true,
    })
    if (!ok) return
    setKeyErr(''); setKeyState('signing')
    try {
      const env = await signRequest({ action: 'set_hl_api_key', wallet: address, body: { api_key: trimmed }, signMessageAsync })
      setKeyState('saving')
      const res = await setHlApiKey(env, trimmed)
      setHlApiKeySet(true, res.masked_key)
      setBotReadiness('saved')
      void refreshUserState(address)
      setApiKey(''); setKeyState('ok')
    } catch (e: unknown) {
      setKeyErr(walletErrorLabel(e, 'Cancelled', 100))
      setKeyState('err')
    }
  }

  const handleConnectWallet = useCallback(async () => {
    setConnectErr('')
    try {
      const connector = await getFirstReadyConnector(connectors)
      if (!connector) { setConnectErr('No wallet connector is available right now.'); return }
      await connectAsync({ connector })
    } catch (err: unknown) {
      setConnectErr(walletConnectErrorLabel(err))
    }
  }, [connectAsync, connectors])

  async function handleManualWindow(hours: number) {
    if (!address) return
    const ok = await confirmSign(hours === 0
      ? {
          label: 'Cancel override window',
          description: 'The bot will stop accepting new trades from this override. Any open position risk controls (stop-loss, de-risk) remain active.',
        }
      : {
          label: `Open ${hours}h override window`,
          description: `The bot will accept qualifying signals for the next ${hours} hour${hours > 1 ? 's' : ''}, regardless of your trading schedule. Use this for high-conviction catalysts (e.g. CPI, FOMC). Stop-loss is always active.`,
          danger: true,
        }
    )
    if (!ok) return
    setMwErr(''); setMwState('signing')
    try {
      const env = await signRequest({ action: 'set_manual_window', wallet: address, body: { hours }, signMessageAsync })
      setMwState('saving')
      const r = await setManualWindow(env, hours)
      setManualUntil(r.manual_window_until); setMwState('idle')
    } catch (e: unknown) {
      setMwErr(walletErrorLabel(e, 'Cancelled', 100))
      setMwState('err')
    }
  }

  async function handleUpgradeToLive() {
    if (!address) return
    const ok = await confirmSign({
      label: 'Switch to live trading',
      description: 'Switches to live trading. Auto-Trade is turned OFF automatically — re-enable it explicitly. You still need a Hyperliquid API key before the bot can trade.',
      danger: true,
    })
    if (!ok) return
    setSubErr(''); setSubState('signing')
    try {
      // paper_mode=false → signedBody=null (same as original live subscribe)
      const env = await signRequest({ action: 'subscribe', wallet: address, body: null, signMessageAsync })
      setSubState('saving')
      await subscribe(env, { paper_mode: false })
      setPaperMode(false)
      void refreshUserState(address)
      setSubState('idle')
    } catch (e: unknown) {
      setSubErr(walletErrorLabel(e, 'Cancelled', 100))
      setSubState('err')
    }
  }

  useEffect(() => {
    if (!manualUntil) return
    const id = setInterval(() => setMwTick(t => t + 1), 15000)
    return () => clearInterval(id)
  }, [manualUntil])

  // ── Not connected ──────────────────────────────────────────────────────────
  if (!mounted || !isConnected) {
    return (
      <div className="card" style={{ padding: '32px 28px', textAlign: 'center', marginBottom: 28 }}>
        <div style={{ fontSize: 15, fontWeight: 600, marginBottom: 6 }}>Connect your wallet to continue</div>
        <div style={{ fontSize: 13, color: 'var(--ink-3)', marginBottom: 20, lineHeight: 1.6 }}>
          Your subscription, API key, and trade settings are wallet-bound. No account needed — just sign a message.
        </div>
        <button className="btn amber" style={{ padding: '10px 24px' }} onClick={() => { void handleConnectWallet() }}>
          Connect wallet
        </button>
        {connectErr && <p style={{ fontSize: 12, color: 'var(--down)', margin: '12px 0 0' }}>{connectErr}</p>}
      </div>
    )
  }

  // ── helpers ────────────────────────────────────────────────────────────────
  const Switch = ({ on, onChange, tone = 'amber' }: { on: boolean; onChange: (v: boolean) => void; tone?: 'amber' | 'up' }) => (
    <label className={`switch ${tone === 'amber' ? '' : tone}`}>
      <input type="checkbox" checked={on} onChange={e => { onChange(e.target.checked); setDirty(true) }} />
      <span className="switch-track" />
    </label>
  )

  const trumpOn = !!settings.trump_enabled
  const macroOn = !!settings.macro_enabled

  // ── Not subscribed ─────────────────────────────────────────────────────────
  if (!isSubscribed) {
    return (
      <div className="card" id="bot-config-panel" style={{ padding: '28px 28px 24px', marginBottom: 28 }}>
        <div style={{ fontSize: 15, fontWeight: 600, marginBottom: 4 }}>Set up your bot</div>
        <div style={{ fontSize: 13, color: 'var(--ink-3)', lineHeight: 1.55, marginBottom: 24 }}>
          Trade-only API key — no withdrawals. Try Paper first, or Live if you already have a key.
        </div>

        {/* Paper / Live choice */}
        <div style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          padding: '14px 16px', borderRadius: 10, marginBottom: 16,
          background: subPaperChoice === 'paper' ? 'var(--up-soft)' : 'var(--amber-soft)',
          border: `1px solid ${subPaperChoice === 'paper'
            ? 'color-mix(in oklab, var(--up) 25%, var(--line))'
            : 'color-mix(in oklab, var(--amber-ink) 25%, var(--line))'}`,
        }}>
          <div>
            <div style={{ fontSize: 13, fontWeight: 600 }}>
              {subPaperChoice === 'paper' ? '📝 Paper mode — simulated trades, no real funds' : '💰 Live trading — real positions on Hyperliquid'}
            </div>
            <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 3, lineHeight: 1.5 }}>
              {subPaperChoice === 'paper'
                ? 'Nothing is at risk. No Hyperliquid API key needed. You can switch to live at any time.'
                : 'Real positions will be opened and managed on Hyperliquid. A trade-only API key is required before the first trade.'}
            </div>
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexShrink: 0 }}>
            <span style={{ fontSize: 11, color: subPaperChoice === 'paper' ? 'var(--ink-2)' : 'var(--ink-4)', fontWeight: subPaperChoice === 'paper' ? 600 : 400 }}>Paper</span>
            <Switch
              on={subPaperChoice === 'live'}
              onChange={v => {
                setSubPaperChoice(v ? 'live' : 'paper')
                // Clear any stale subscribe-error from a previous attempt so
                // the user sees a clean state for the new paper/live choice.
                if (subState === 'err') { setSubState('idle'); setSubErr('') }
              }}
              tone="amber"
            />
            <span style={{ fontSize: 11, color: subPaperChoice === 'live' ? 'var(--amber-ink)' : 'var(--ink-4)', fontWeight: subPaperChoice === 'live' ? 600 : 400 }}>Live</span>
          </div>
        </div>

        <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
          <button
            className="btn amber"
            style={{ padding: '10px 22px', fontSize: 13 }}
            onClick={handleSubscribe}
            disabled={subState === 'signing' || subState === 'saving'}
          >
            {subState === 'signing' ? 'Sign in wallet…' : subState === 'saving' ? 'Activating…' : 'Get started →'}
          </button>
          {subState === 'err' && <span style={{ fontSize: 12, color: 'var(--down)' }}>{subErr}</span>}
        </div>
      </div>
    )
  }

  // ── Subscribed but settings not loaded yet ─────────────────────────────────
  if (loadState !== 'loaded') {
    return (
      <div className="card" id="bot-config-panel" style={{ padding: '28px 28px 24px', marginBottom: 28 }}>
        <div style={{ fontSize: 15, fontWeight: 600, marginBottom: 4 }}>Sign in to view your settings</div>
        <div style={{ fontSize: 13, color: 'var(--ink-3)', lineHeight: 1.55, marginBottom: 20 }}>
          Settings are wallet-private. Sign once to load — cached for 4 min.
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
          <button className="btn amber" style={{ padding: '10px 22px', fontSize: 13 }} onClick={handleLoadSettings} disabled={loadState === 'loading'}>
            {loadState === 'loading' ? 'Waiting for signature…' : 'Load my settings'}
          </button>
          {loadState === 'err' && <span style={{ fontSize: 12, color: 'var(--down)' }}>{loadErr}</span>}
        </div>
      </div>
    )
  }

  // ── Main config (subscribed + loaded) ──────────────────────────────────────

  // Readiness check — shown at bottom near save button
  const missingItems: string[] = []
  if (!paperMode && !hlApiKeySet) missingItems.push('link a Hyperliquid API key')
  if (!trumpOn && !macroOn) missingItems.push('enable Trump Signal or Macro Vibes below')
  if (trumpOn && !tpConfigured) missingItems.push('set a Trump Signal take-profit %')
  if (trumpOn && !slConfigured) missingItems.push('set a Trump Signal stop-loss %')
  // Macro Vibes is manage-only through the Telegram /adopt command. If Macro is
  // the only enabled system and Telegram isn't bound, the bot can't manage any
  // position — so "ready" would be a lie. (tgBound === null = status unknown /
  // still loading: don't block on it.)
  if (macroOn && !trumpOn && tgBound === false) {
    missingItems.push('connect Telegram (Settings → Telegram) so the bot can manage Macro positions via /adopt')
  }
  const botReady = missingItems.length === 0

  // Manual window countdown
  void mwTick
  const untilMs = manualUntil ? new Date(manualUntil).getTime() : 0
  const remainingMin = untilMs ? Math.max(0, Math.round((untilMs - Date.now()) / 60000)) : 0
  const armed = remainingMin > 0
  const remainingLabel = remainingMin >= 60 ? `${Math.floor(remainingMin / 60)}h ${remainingMin % 60}m` : `${remainingMin}m`
  const mwBusy = mwState === 'signing' || mwState === 'saving'

  const saveBtnLabel =
    saveState === 'signing' ? 'Sign in wallet…'
    : saveState === 'saving' ? 'Saving…'
    : saveState === 'ok' ? '✓ Saved'
    : 'Save changes'

  return (
    <div style={{ marginBottom: 28 }} id="bot-config-panel">

      {/* ── Onboarding stepper ─────────────────────────────────────────────── */}
      {(!isSubscribed || !(hlApiKeySet || paperMode) || !autoTrade) && (
        <div className="card" style={{ padding: '14px 18px 16px', marginBottom: 12 }}>
          <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.06em',
                        textTransform: 'uppercase', color: 'var(--ink-4)', marginBottom: 14 }}>
            Setup progress
          </div>

          {/* Steps row: steps are fixed-width, connectors flex-grow to fill */}
          <div style={{ display: 'flex', alignItems: 'flex-start', marginBottom: 14 }}>
            {([
              { label: 'Subscribe',         done: isSubscribed },
              { label: 'Add HL key',        done: hlApiKeySet || paperMode },
              { label: 'Enable Auto-Trade', done: autoTrade },
            ] as const).map((step, i, arr) => {
              const isActive = !step.done && (i === 0 || arr[i - 1].done)
              const isLast = i === arr.length - 1
              return (
                <div key={step.label} style={{
                  display: 'flex', alignItems: 'flex-start',
                  flex: isLast ? '0 0 auto' : 1,  // last step: natural width; others: expand via connector
                  minWidth: 0,
                }}>
                  {/* Circle + label — fixed, never stretches */}
                  <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6, flexShrink: 0 }}>
                    <div style={{
                      width: 24, height: 24, borderRadius: '50%',
                      fontSize: 11, fontWeight: 700,
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                      background: step.done ? 'var(--up)' : isActive ? 'var(--amber, #f59e0b)' : 'var(--bg-sunk)',
                      color: step.done ? '#fff' : isActive ? '#000' : 'var(--ink-4)',
                      border: `2px solid ${step.done ? 'var(--up)' : isActive ? 'var(--amber, #f59e0b)' : 'var(--line)'}`,
                      boxSizing: 'border-box',
                    }}>
                      {step.done ? '✓' : i + 1}
                    </div>
                    <div style={{
                      fontSize: 10, lineHeight: 1.3,
                      textAlign: isLast ? 'right' : 'center',
                      fontWeight: isActive ? 600 : 400,
                      color: step.done ? 'var(--up)' : isActive ? 'var(--ink)' : 'var(--ink-4)',
                      whiteSpace: 'nowrap',
                    }}>
                      {step.label}
                    </div>
                  </div>
                  {/* Connector — takes all remaining space between this and next step */}
                  {!isLast && (
                    <div style={{
                      flex: 1, height: 2, marginTop: 11,
                      background: step.done ? 'var(--up)' : 'var(--line)',
                      transition: 'background 0.3s',
                    }} />
                  )}
                </div>
              )
            })}
          </div>

          {/* Bottom progress bar */}
          <div style={{ height: 3, borderRadius: 999, background: 'var(--bg-sunk)', overflow: 'hidden' }}>
            <div style={{
              height: '100%', borderRadius: 999,
              background: 'var(--up)',
              width: `${Math.round(([isSubscribed, hlApiKeySet || paperMode, autoTrade].filter(Boolean).length / 3) * 100)}%`,
              transition: 'width .4s ease',
            }} />
          </div>
        </div>
      )}

      {/* ── HL API Key ─────────────────────────────────────────────────────── */}
      <div className="card" style={{ padding: '16px 20px', marginBottom: 12 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
          {/* Status dot */}
          <div style={{
            width: 36, height: 36, borderRadius: 9, flexShrink: 0,
            background: (paperMode || hlApiKeySet) ? 'var(--up-soft)' : 'var(--bg-sunk)',
            border: `1px solid ${(paperMode || hlApiKeySet) ? 'color-mix(in oklab, var(--up) 25%, var(--line))' : 'var(--line)'}`,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontSize: 14, color: (paperMode || hlApiKeySet) ? 'var(--up)' : 'var(--ink-4)',
          }}>
            {(paperMode || hlApiKeySet) ? '✓' : '◇'}
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 13, fontWeight: 600 }}>Hyperliquid API wallet</div>
            <div style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 1 }}>
              {paperMode
                ? <span style={{ color: 'var(--up)' }}>📝 Paper mode — no real money involved. Add an API key below to switch to live.</span>
                : hlApiKeySet && !apiKey
                  ? <><span className="mono">{hlApiKeyMasked ?? '···'}</span><span> · trade-only · cannot withdraw</span></>
                  : <>Don&apos;t have Hyperliquid?{' '}
                      <a href="https://app.hyperliquid.xyz" target="_blank" rel="noopener noreferrer" style={{ color: 'var(--amber)', textDecoration: 'none' }}>Sign up free ↗</a>
                      {' '}· Then go to API → generate a trade-only key → paste below.</>}
            </div>
          </div>
          {/* Paper mode: show upgrade path instead of key input */}
          {paperMode && (
            <button
              className="btn amber"
              style={{ padding: '6px 14px', fontSize: 12, flexShrink: 0 }}
              onClick={handleUpgradeToLive}
              disabled={subState === 'signing' || subState === 'saving'}
            >
              {subState === 'signing' ? 'Sign…' : subState === 'saving' ? 'Switching…' : 'Switch to live →'}
            </button>
          )}
          {/* Live mode: show key input */}
          {!paperMode && (hlApiKeySet && !apiKey ? (
            <button
              className="btn ghost"
              style={{ padding: '6px 14px', fontSize: 12, flexShrink: 0 }}
              onClick={() => {
                // Switch to edit mode AND clear any stale error / status from
                // a previous failed save so the row isn't pre-stained red.
                setApiKey(' '); setKeyState('idle'); setKeyErr('')
              }}
            >Rotate</button>
          ) : (
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
              <input
                value={apiKey.trim() === '' && hlApiKeySet ? '' : apiKey}
                onChange={e => { setApiKey(e.target.value); if (keyState !== 'idle') setKeyState('idle') }}
                placeholder="0x…"
                style={{ width: 200, maxWidth: '100%', fontFamily: 'var(--mono)', fontSize: 12, padding: '8px 10px' }}
              />
              <button
                className={`btn ${keyState === 'ok' ? 'ghost' : 'amber'}`}
                style={{ padding: '8px 14px', fontSize: 12, flexShrink: 0 }}
                onClick={handleSaveKey}
                disabled={keyState === 'signing' || keyState === 'saving' || !apiKey.trim()}
              >
                {keyState === 'signing' ? 'Sign…' : keyState === 'saving' ? 'Saving…' : keyState === 'ok' ? '✓' : 'Save'}
              </button>
            </div>
          ))}
        </div>
        {keyState === 'err' && <div style={{ fontSize: 12, color: 'var(--down)', marginTop: 8, paddingLeft: 50 }}>{keyErr}</div>}
        {keyState === 'ok' && <div style={{ fontSize: 11, color: 'var(--up)', marginTop: 6, paddingLeft: 50 }}>Saved. Enable Auto-Trade below, then try a paper trade first to confirm the live path works.</div>}
        {!paperMode && !hlApiKeySet && (
          <div style={{
            marginTop: 10, paddingLeft: 50,
            display: 'flex', alignItems: 'flex-start', gap: 6,
          }}>
            <span style={{ fontSize: 13, flexShrink: 0, marginTop: 1 }}>⚠️</span>
            <div style={{ fontSize: 11, color: 'var(--down)', lineHeight: 1.5 }}>
              <strong>Do not paste your main wallet private key.</strong>{' '}
              This field only accepts a Hyperliquid <em>trade-only API key</em> — generate one at{' '}
              <a href="https://app.hyperliquid.xyz/API" target="_blank" rel="noopener noreferrer"
                style={{ color: 'var(--down)', textDecorationColor: 'var(--down)' }}>
                app.hyperliquid.xyz/API
              </a>
              {' '}→ &ldquo;Generate API wallet&rdquo;. A trade-only key cannot withdraw funds.
            </div>
          </div>
        )}
        {subState === 'err' && subErr && <div style={{ fontSize: 12, color: 'var(--down)', marginTop: 8 }}>{subErr}</div>}
      </div>

      {/* ── Auto-Trade master switch ───────────────────────────────────────── */}
      {isSubscribed && (hlApiKeySet || paperMode) && (
        <div className="card" style={{ padding: '14px 20px', marginBottom: 12 }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
            <div>
              <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 2 }}>Auto-Trade</div>
              <div style={{ fontSize: 11, color: 'var(--ink-4)', lineHeight: 1.4 }}>
                {autoTrade
                  ? 'ON — bot opens positions automatically when qualifying Trump signals fire.'
                  : 'OFF — signals are shown but no trades are opened. Turn on when ready to go live.'}
              </div>
            </div>
            <div style={{ display: 'flex', gap: 6, flexShrink: 0 }}>
              <button
                onClick={() => flipAutoTrade(true)}
                disabled={atState !== 'idle'}
                style={{
                  padding: '6px 14px', borderRadius: 6, fontSize: 12, fontWeight: 700, cursor: 'pointer',
                  border: 'none',
                  background: autoTrade ? 'var(--up)' : 'var(--bg-sunk)',
                  color: autoTrade ? '#fff' : 'var(--ink-4)',
                  opacity: atState !== 'idle' ? 0.5 : 1,
                }}
              >ON</button>
              <button
                onClick={() => flipAutoTrade(false)}
                disabled={atState !== 'idle'}
                style={{
                  padding: '6px 14px', borderRadius: 6, fontSize: 12, fontWeight: 700, cursor: 'pointer',
                  border: '1px solid var(--line)',
                  background: !autoTrade ? 'var(--ink)' : 'transparent',
                  color: !autoTrade ? 'var(--bg)' : 'var(--ink-4)',
                  opacity: atState !== 'idle' ? 0.5 : 1,
                }}
              >OFF</button>
            </div>
          </div>
          {atState !== 'idle' && (
            <div style={{ fontSize: 11, marginTop: 8, color: atState === 'err' ? 'var(--down)' : 'var(--ink-4)' }}>
              {atState === 'signing' ? 'Waiting for wallet signature…'
                : atState === 'saving' ? 'Saving…'
                : 'Failed to update — please try again.'}
            </div>
          )}
        </div>
      )}

      {/* ── Trump Signal ───────────────────────────────────────────────────── */}
      <div id="config-trump" className="card" style={{ padding: 0, overflow: 'hidden', marginBottom: 12 }}>
        {/* Header */}
        <div style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          padding: '16px 20px',
          borderBottom: trumpOn ? '1px solid var(--line)' : 'none',
          background: trumpOn ? 'var(--amber-soft)' : undefined,
          transition: 'background 0.2s',
        }}>
          <div>
            <div style={{ fontSize: 14, fontWeight: 700, color: trumpOn ? 'var(--amber-ink)' : 'var(--ink)' }}>
              Trump Signal
            </div>
            <div style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 2 }}>
              {trumpOn
                ? 'Opens positions when a Trump Truth Social post fires a signal.'
                : 'Disabled — bot ignores Trump posts.'}
            </div>
          </div>
          <Switch on={trumpOn} onChange={v => updateSettings({ trump_enabled: v })} tone="amber" />
        </div>

        {/* Form — only when enabled */}
        {trumpOn && (
          <div style={{ padding: '20px 20px 8px' }}>

            <div className="form-row">
              <div className="form-row-label">
                Per-trade size
                <span className="hint">How much to bet per trade. At {settings.leverage}×, HL holds ~${(settings.position_size_usd / settings.leverage).toFixed(0)} as collateral.</span>
              </div>
              <div className="form-row-control">
                <div className="num-field">
                  <span className="prefix">$</span>
                  <input type="number" min={5} max={10000} step={5} value={settings.position_size_usd}
                    onChange={e => updateSettings({ position_size_usd: Math.max(5, +e.target.value || 0) })} />
                </div>
                <span style={{ fontSize: 12, color: 'var(--ink-4)' }}>$5 – $10,000</span>
              </div>
            </div>

            <div className="form-row">
              <div className="form-row-label">
                Leverage
                <span className="hint">How aggressive the position is. Start low (2–3×) and increase once you've seen the bot trade live.</span>
              </div>
              <div className="form-row-control">
                <div className="slider-field">
                  <input type="range" min={1} max={50} value={settings.leverage}
                    onChange={e => updateSettings({ leverage: +e.target.value })} />
                  <div className="ticks"><span>1×</span><span>25×</span><span>50×</span></div>
                </div>
                <span className="slider-readout">{settings.leverage}×</span>
                {settings.leverage > 10 && (
                  <div className="settings-note warn" style={{ marginTop: 6 }}>
                    {settings.leverage}× is high for event-driven signals. A 1.5% stop-loss at {settings.leverage}× means the trade closes on a {(1.5 / settings.leverage).toFixed(1)}% price move against you. Consider 3–5× until you see how the bot performs live.
                  </div>
                )}
              </div>
            </div>

            <div className="form-row">
              <div className="form-row-label">
                Min AI confidence
                <span className="hint">Filter out weak signals. Higher = fewer trades, but only the strongest ones.</span>
              </div>
              <div className="form-row-control">
                <div className="slider-field">
                  <input type="range" min={0} max={100} value={settings.min_confidence}
                    onChange={e => updateSettings({ min_confidence: +e.target.value })} />
                  <div className="ticks"><span>0</span><span>50</span><span>100</span></div>
                </div>
                <span className="slider-readout">{settings.min_confidence}%</span>
              </div>
            </div>

            <div style={{ height: 1, background: 'var(--line)', margin: '8px 0 16px' }} />

            <div className="form-row">
              <div className="form-row-label">
                Take profit <span style={{ color: 'var(--down)' }}>*</span>
                <span className="hint">Bot locks in profit when the position gains this much. Required.</span>
              </div>
              <div className="form-row-control">
                <div className="num-field">
                  <input type="number" min={0.1} max={50} step={0.1}
                    value={settings.take_profit_pct ?? ''}
                    onChange={e => updateSettings({ take_profit_pct: e.target.value === '' ? null : +e.target.value })} />
                  <span className="suffix">% gain</span>
                </div>
                {!tpConfigured && <span style={{ fontSize: 11, color: 'var(--down)' }}>Required — not saved</span>}
              </div>
            </div>

            <div className="form-row" style={{ marginBottom: 16 }}>
              <div className="form-row-label">
                Stop loss <span style={{ color: 'var(--down)' }}>*</span>
                <span className="hint">Bot cuts the loss when the position falls this much. Required.</span>
              </div>
              <div className="form-row-control">
                <div className="num-field">
                  <input type="number" min={0.1} max={50} step={0.1}
                    value={settings.stop_loss_pct ?? ''}
                    onChange={e => updateSettings({ stop_loss_pct: e.target.value === '' ? null : +e.target.value })} />
                  <span className="suffix">% loss</span>
                </div>
                {!slConfigured && <span style={{ fontSize: 11, color: 'var(--down)' }}>Required — not saved</span>}
              </div>
            </div>
          </div>
        )}
      </div>

      {/* ── Macro Vibes ────────────────────────────────────────────────────── */}
      <div id="config-macro" className="card" style={{ padding: 0, overflow: 'hidden', marginBottom: 12 }}>
        {/* Header */}
        <div style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          padding: '16px 20px',
          borderBottom: macroOn ? '1px solid var(--line)' : 'none',
          background: macroOn ? 'var(--up-soft)' : undefined,
          transition: 'background 0.2s',
        }}>
          <div>
            <div style={{ fontSize: 14, fontWeight: 700, color: macroOn ? 'var(--up)' : 'var(--ink)' }}>
              Macro Vibes
            </div>
            <div style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 2 }}>
              {macroOn
                ? 'When a signal fires: open a BTC long on Hyperliquid → type /adopt in the Telegram bot → bot manages the exit for you.'
                : 'Disabled — Macro Vibes alerts sent without bot management. Enable + set up Telegram to activate.'}
            </div>
          </div>
          <Switch on={macroOn} onChange={v => updateSettings({ macro_enabled: v })} tone="up" />
        </div>

        {/* Form — only when enabled */}
        {macroOn && (() => {
          const aggressive = settings.sys2_mode === 'aggressive'
          const defLev = aggressive ? 8 : 2
          const lev = Math.max(1, Math.min(10, settings.sys2_leverage ?? defLev))
          const prot = Math.min(35, 0.85 * 100 / lev)
          const liq  = 100 / lev
          const risky = lev > 2
          return (
            <div style={{ padding: '20px 20px 8px' }}>

              <div className="form-row">
                <div className="form-row-label">
                  BTC strategy mode
                  <span className="hint">Standard is calmer. Aggressive uses higher leverage and wider pyramiding.</span>
                </div>
                <div className="form-row-control">
                  <div style={{
                    display: 'inline-flex', gap: 6, padding: 4,
                    borderRadius: 999, background: 'var(--bg-sunk)', border: '1px solid var(--line)',
                  }}>
                    <button type="button"
                      onClick={() => updateSettings({ sys2_mode: 'standard' })}
                      aria-pressed={!aggressive}
                      style={{
                        padding: '8px 16px', fontSize: 12, fontWeight: 700, borderRadius: 999, cursor: 'pointer',
                        border: !aggressive ? '1px solid var(--line)' : '1px solid transparent',
                        background: !aggressive ? 'var(--surface)' : 'transparent',
                        color: !aggressive ? 'var(--ink)' : 'var(--ink-3)',
                        boxShadow: !aggressive ? 'var(--shadow-1)' : 'none',
                      }}>
                      Standard
                    </button>
                    <button type="button"
                      onClick={() => updateSettings({ sys2_mode: 'aggressive' })}
                      aria-pressed={aggressive}
                      style={{
                        padding: '8px 16px', fontSize: 12, fontWeight: 700, borderRadius: 999, cursor: 'pointer',
                        border: aggressive ? '1px solid var(--down)' : '1px solid transparent',
                        background: aggressive ? 'var(--down)' : 'transparent',
                        color: aggressive ? '#fff' : 'var(--ink-3)',
                        boxShadow: aggressive ? 'var(--shadow-1)' : 'none',
                      }}>
                      🔥 Aggressive
                    </button>
                  </div>
                  {aggressive && (
                    <div className="settings-note warn" style={{ marginTop: 6 }}>
                      High-risk sleeve. More leverage, harder pyramiding, wider peak-trail. Treat as a separate risk bucket.
                    </div>
                  )}
                </div>
              </div>

              <div className="form-row" style={{ marginBottom: 16 }}>
                <div className="form-row-label">
                  BTC bottom leverage
                  <span className="hint">Higher leverage = less room for BTC to drop before the bot starts reducing the position.</span>
                </div>
                <div className="form-row-control">
                  <div className="slider-field">
                    <input type="range" min={1} max={10} value={lev}
                      onChange={e => updateSettings({ sys2_leverage: +e.target.value })} />
                    <div className="ticks"><span>1×</span><span>5×</span><span>10×</span></div>
                  </div>
                  <span className="slider-readout">{lev}×</span>
                  <div className={`settings-note ${risky ? 'warn' : ''}`} style={{ marginTop: 6 }}>
                    {risky
                      ? `⚠️ At ${lev}×, BTC only needs to drop ${prot.toFixed(0)}% before the bot fully closes the position. A normal correction can trigger early exits — use lower leverage unless you're comfortable with that.`
                      : `At ${lev}×, the bot has room for a ${prot.toFixed(0)}% BTC drop before closing. It reduces the position gradually as the drawdown deepens, so you don't lose everything at once.`}
                  </div>
                </div>
              </div>
            </div>
          )
        })()}
      </div>

      {/* ── Advanced / Global controls ─────────────────────────────────────── */}
      <div id="config-global" className="card" style={{ padding: 0, overflow: 'hidden', marginBottom: 12 }}>
        <button
          type="button"
          onClick={() => setShowAdvanced(v => !v)}
          style={{
            width: '100%', padding: '14px 20px', display: 'flex', alignItems: 'center',
            justifyContent: 'space-between', background: 'transparent', border: 'none',
            cursor: 'pointer', borderBottom: showAdvanced ? '1px solid var(--line)' : 'none',
          }}
        >
          <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink-2)' }}>Risk limits &amp; schedule</span>
          <span style={{
            fontSize: 11, color: 'var(--ink-4)',
            display: 'flex', alignItems: 'center', gap: 10,
          }}>
            {armed && <span style={{ color: 'var(--up)', fontWeight: 600 }}>● Override {remainingLabel}</span>}
            {useBudget && settings.daily_budget_usd && <span>${settings.daily_budget_usd}/day limit</span>}
            {useSchedule && <span>Scheduled</span>}
            <span style={{ fontSize: 13 }}>{showAdvanced ? '▴' : '▾'}</span>
          </span>
        </button>

        {showAdvanced && (
          <div style={{ padding: '16px 20px 8px' }}>

            {/* Daily spend cap */}
            <div className="form-row">
              <div className="form-row-label">
                Daily trading cap
                <span className="hint">Daily spending cap — bot stops opening new trades once it hits this amount.</span>
              </div>
              <div className="form-row-control" style={{ flexWrap: 'wrap', gap: 10 }}>
                <Switch on={useBudget} onChange={v => { setUseBudget(v); setDirty(true) }} />
                {useBudget ? (
                  <div className="num-field">
                    <span className="prefix">$</span>
                    <input type="number" min={1} max={100000} step={5}
                      value={settings.daily_budget_usd ?? ''}
                      onChange={e => updateSettings({ daily_budget_usd: e.target.value === '' ? null : Math.max(0, +e.target.value) })} />
                    <span className="suffix">/day</span>
                  </div>
                ) : (
                  <span style={{ fontSize: 12, color: 'var(--ink-4)' }}>No cap</span>
                )}
              </div>
            </div>

            {/* Schedule */}
            <div className="form-row">
              <div className="form-row-label">
                Trading schedule
                <span className="hint">Daily recurring UTC window — bot only opens new trades within this time range. Overnight windows (e.g. 22:00–02:00) are allowed. Leave off for 24/7.</span>
              </div>
              <div className="form-row-control" style={{ flexWrap: 'wrap' }}>
                <Switch on={useSchedule} onChange={v => { setUseSchedule(v); setDirty(true) }} />
                {useSchedule && (
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
                    <div className="num-field">
                      <span className="prefix">From</span>
                      <input type="time" value={fromLocal}
                        onChange={e => { setFromLocal(e.target.value); setDirty(true) }} style={{ width: 110 }} />
                    </div>
                    <span style={{ fontSize: 12, color: 'var(--ink-4)' }}>→</span>
                    <div className="num-field">
                      <span className="prefix">Until</span>
                      <input type="time" value={untilLocal}
                        onChange={e => { setUntilLocal(e.target.value); setDirty(true) }} style={{ width: 110 }} />
                    </div>
                  </div>
                )}
              </div>
            </div>

            {/* Override window — only relevant when a schedule is set */}
            {useSchedule && (
              <div className="form-row" style={{ marginBottom: 16 }}>
                <div className="form-row-label">
                  Override window
                  <span className="hint">Temporarily bypass your schedule. Useful when a high-conviction event (e.g. CPI, FOMC) happens outside your trading hours.</span>
                </div>
                <div className="form-row-control" style={{ flexWrap: 'wrap', gap: 8 }}>
                  {armed ? (
                    <>
                      <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--up)' }}>● Override active — {remainingLabel} remaining</span>
                      <button className="btn ghost" style={{ padding: '6px 14px', fontSize: 12 }}
                        onClick={() => handleManualWindow(0)} disabled={mwBusy}>
                        {mwBusy ? 'Sign…' : 'Cancel'}
                      </button>
                    </>
                  ) : (
                    [4, 12, 24].map(h => (
                      <button key={h} className="btn ghost" style={{ padding: '6px 12px', fontSize: 12 }}
                        onClick={() => handleManualWindow(h)} disabled={mwBusy}>
                        {mwBusy && mwState === 'signing' ? 'Sign…' : `+${h}h`}
                      </button>
                    ))
                  )}
                  {mwState === 'err' && <span style={{ fontSize: 11, color: 'var(--down)', width: '100%' }}>{mwErr}</span>}
                </div>
              </div>
            )}

          </div>
        )}
      </div>

      {/* ── Save ───────────────────────────────────────────────────────────── */}
      <div style={{
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        gap: 12, flexWrap: 'wrap',
        padding: '16px 20px',
        background: 'var(--surface)',
        border: '1px solid var(--line)',
        borderRadius: 12,
      }}>
        {/* Status */}
        <div style={{ fontSize: 12, color: botReady ? 'var(--up)' : 'var(--ink-3)', lineHeight: 1.5 }}>
          {saveState === 'err' ? (
            <span style={{ color: 'var(--down)' }}>⚠ {saveErr}</span>
          ) : botReady ? (
            <span>✓ Bot is configured and ready</span>
          ) : (
            <span>Still needed: {missingItems.join(' · ')}</span>
          )}
        </div>
        {/* Button */}
        <button
          className={`btn ${saveState === 'ok' ? 'ghost' : 'amber'}`}
          style={{ padding: '10px 24px', fontSize: 13 }}
          onClick={handleSaveSettings}
          disabled={!dirty || saveState === 'signing' || saveState === 'saving'}
        >
          {saveBtnLabel}
        </button>
      </div>

    </div>
  )
}
