// Saddle Alert — Landing page + Stock Detail panel + Price chart
const { useState: useState_e, useEffect: useEffect_e, useMemo: useMemo_e } = React;

const hasDisplayPrice = (price) => (
  typeof price === 'number' && Number.isFinite(price) && price > 0
);

const priceSymbolFor = (currency) => (
  currency === 'USD' ? '$' : currency === 'GBP' ? '£' : '$'
);

const formatCurrencyCode = (currency) => (
  String(currency || '').trim().toUpperCase()
);

const compactCurrencyPrefixFor = (currency) => {
  const code = formatCurrencyCode(currency);
  if (code === 'AUD') return 'A$';
  if (code === 'SGD') return 'S$';
  if (code === 'NZD') return 'NZ$';
  if (code === 'USD') return 'US$';
  if (code === 'GBP') return '£';
  return priceSymbolFor(currency);
};

const formatMoney = (price, currency, fallback = 'Price unavailable') => (
  hasDisplayPrice(price)
    ? `${formatCurrencyCode(currency)} ${priceSymbolFor(currency)}${new Intl.NumberFormat('en-US').format(price)}`.trim()
    : fallback
);

const formatCompactMoney = (price, currency, fallback = 'Price unavailable') => (
  hasDisplayPrice(price)
    ? `${compactCurrencyPrefixFor(currency)}${new Intl.NumberFormat('en-US').format(price)}`
    : fallback
);

const categoryForLandingItem = (item) => {
  const explicit = String(item?.category || '').trim();
  if (explicit) return explicit;
  const text = `${item?.model || ''} ${item?.title || ''}`.toLowerCase();
  if (/\b(charm|key ring|key holder|pendant)\b/.test(text)) return 'charms';
  if (/(wallet|card holder|passport holder|change purse|coin purse|\bpouch\b|magsafe|calvi|bastia|silk'in)/.test(text)) return 'slg';
  return 'bags';
};

// ----- Supabase auth helpers (used by the landing page) ------------------

// ====================================================================
// LANDING PAGE — single-screen layout, warm palette
// Variant C density + original's serif hero
// ====================================================================
const LandingPage = ({ onAuthSuccess }) => {
  const validInfoPage = (value) => (
    value === 'privacy' || value === 'terms' || value === 'contact' ? value : null
  );
  const initialAuthPage = (() => {
    if (typeof window === 'undefined') return null;
    const mode = new URLSearchParams(window.location.search || '').get('auth');
    return mode === 'signup' || mode === 'login' ? mode : null;
  })();
  const initialInfoPage = (() => {
    if (typeof window === 'undefined') return null;
    return validInfoPage(new URLSearchParams(window.location.search || '').get('page'));
  })();
  const [authPage, setAuthPage] = useState_e(initialAuthPage);
  const [infoPage, setInfoPage] = useState_e(initialInfoPage);
  const [authMode, setAuthMode] = useState_e(initialAuthPage || 'signup');
  const [name, setName] = useState_e('');
  const [email, setEmail] = useState_e('');
  const [password, setPassword] = useState_e('');
  const [authError, setAuthError] = useState_e(null);
  const [authLoading, setAuthLoading] = useState_e(false);
  const [googleEnabled, setGoogleEnabled] = useState_e(
    Boolean(window.OBT_PUBLIC_CONFIG && window.OBT_PUBLIC_CONFIG.google?.enabled)
  );
  const [landingSnapshot, setLandingSnapshot] = useState_e(null);
  const [visitorRegion, setVisitorRegion] = useState_e({ status: 'unknown', country: null, region: null });

  // Public config may load after the component mounts; pick it up when it does.
  useEffect_e(() => {
    const onCfg = () => {
      setGoogleEnabled(Boolean((window.OBT_PUBLIC_CONFIG || {}).google?.enabled));
    };
    window.addEventListener('obt:public-config', onCfg);
    return () => window.removeEventListener('obt:public-config', onCfg);
  }, []);

  useEffect_e(() => {
    fetch('/api/au-live')
      .then(res => res.json())
      .then(json => setLandingSnapshot(json))
      .catch(() => setLandingSnapshot({ ready: false, items: [] }));

    fetch('/api/visitor-region')
      .then(res => res.json())
      .then(json => setVisitorRegion(json || { status: 'unknown', country: null, region: null }))
      .catch(() => setVisitorRegion({ status: 'unknown', country: null, region: null }));
  }, []);

  // Detect a failed Google OAuth callback — the server sets ?auth_error= on redirect.
  useEffect_e(() => {
    if (typeof window === 'undefined') return;
    const search = new URLSearchParams(window.location.search || '');
    if (search.get('auth_error')) {
      setAuthError('Google sign-in failed. Please try again.');
      window.history.replaceState({}, document.title, window.location.pathname);
    }
  }, []);

  useEffect_e(() => {
    if (typeof window === 'undefined') return;
    const onPop = () => {
      const mode = new URLSearchParams(window.location.search || '').get('auth');
      const page = new URLSearchParams(window.location.search || '').get('page');
      const nextPage = mode === 'signup' || mode === 'login' ? mode : null;
      setAuthPage(nextPage);
      setInfoPage(validInfoPage(page));
      if (nextPage) setAuthMode(nextPage);
    };
    window.addEventListener('popstate', onPop);
    return () => window.removeEventListener('popstate', onPop);
  }, []);

  const openAuthPage = (mode) => {
    if (typeof window !== 'undefined') {
      const url = new URL(window.location.href);
      url.searchParams.set('auth', mode);
      url.searchParams.delete('page');
      window.history.pushState({}, '', url);
    }
    setAuthPage(mode);
    setInfoPage(null);
    setAuthMode(mode);
    setAuthError(null);
  };

  const closeAuthPage = () => {
    if (typeof window !== 'undefined') {
      const url = new URL(window.location.href);
      url.searchParams.delete('auth');
      window.history.pushState({}, '', url);
    }
    setAuthPage(null);
    setAuthError(null);
  };

  const openInfoPage = (page) => {
    const nextPage = validInfoPage(page);
    if (!nextPage) return;
    if (typeof window !== 'undefined') {
      const url = new URL(window.location.href);
      url.searchParams.delete('auth');
      url.searchParams.set('page', nextPage);
      window.history.pushState({}, '', url);
    }
    setAuthPage(null);
    setInfoPage(nextPage);
    setAuthError(null);
  };

  const closeInfoPage = () => {
    if (typeof window !== 'undefined') {
      const url = new URL(window.location.href);
      url.searchParams.delete('page');
      window.history.pushState({}, '', url);
    }
    setInfoPage(null);
  };

  const signInWithGoogle = () => {
    window.location.href = '/api/auth/google/start';
  };
  const drops = landingSnapshot?.ready
    ? landingSnapshot.items.map((item, idx) => ({
        id: `${item.region || 'AU'}-${idx}-${item.url}`,
        model: item.model || item.title || 'Storefront item',
        size: item.size || '',
        leather: item.material || '',
        color: item.color || 'Unknown',
        region: item.region || 'AU',
        category: categoryForLandingItem(item),
        price: item.price,
        currency: item.currency || 'AUD',
        ago: Math.max(0, Math.round((Date.now() - new Date(item.observedAt).getTime()) / 60000)),
        firstObservedAt: item.firstObservedAt || item.observedAt,
        status: item.availability === 'sold_out' ? 'sold' : 'live',
        rarity: item.rarity || 'common',
        rarityScore: item.rarityScore || 0,
        rarityBasis: item.rarityBasis || '',
      }))
    : [];
  const landingRegionCodes = ['AU', 'SG'];
  const detectedLandingRegion = visitorRegion?.status === 'known_supported' ? visitorRegion.region : 'AU';
  const isUnsupportedCountry = visitorRegion?.status === 'known_unsupported';
  const visibleDrops = isUnsupportedCountry
    ? []
    : (detectedLandingRegion ? drops.filter(d => d.region === detectedLandingRegion) : drops);
  const latestVisibleDrops = [...visibleDrops]
    .filter(d => d.status === 'live')
    .sort((a, b) => new Date(b.firstObservedAt || b.observedAt || 0) - new Date(a.firstObservedAt || a.observedAt || 0));
  const ticker = latestVisibleDrops.reduce((items, item) => {
    if (items.length >= 5) return items;
    if (item.category === 'bags' && items.some(existing => existing.category === 'bags')) return items;
    return [...items, item];
  }, []);
  const regionCounts = window.DW_DATA.REGIONS.filter(r => landingRegionCodes.includes(r.code)).map(r => ({
    ...r,
    count: drops.filter(d => d.region === r.code && d.status === 'live').length,
    total: drops.filter(d => d.region === r.code).length,
    droppedToday: drops.filter(d => {
      if (d.region !== r.code || d.status !== 'live') return false;
      const firstSeen = new Date(d.firstObservedAt);
      if (Number.isNaN(firstSeen.getTime())) return false;
      return firstSeen.toDateString() === new Date().toDateString();
    }).length,
  }));
  const isSignup = authMode === 'signup';

  const submitAuth = (e) => {
    e && e.preventDefault();
    setAuthLoading(true);
    setAuthError(null);
    fetch(isSignup ? '/api/auth/signup' : '/api/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name, email, password }),
    })
      .then(res => res.json().then(json => ({ ok: res.ok, json })))
      .then(({ ok, json }) => {
        if (!ok || !json.user) throw new Error(json.error || 'Authentication failed');
        onAuthSuccess && onAuthSuccess(json.user, isSignup);
      })
      .catch((error) => setAuthError(error.message || 'Authentication failed'))
      .finally(() => setAuthLoading(false));
  };

  const authForm = (
    <>
      <form onSubmit={submitAuth} style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
          {[
            { id: 'signup', label: 'Create account' },
            { id: 'login', label: 'Sign in' },
          ].map(opt => (
            <button
              key={opt.id}
              type="button"
              onClick={() => openAuthPage(opt.id)}
              style={{
                height: 36,
                border: `1px solid ${authMode === opt.id ? 'var(--ink)' : 'var(--line-2)'}`,
                borderRadius: 8,
                background: authMode === opt.id ? 'var(--ink)' : 'var(--bg)',
                color: authMode === opt.id ? 'var(--bg)' : 'var(--ink-2)',
                fontWeight: 600,
                cursor: 'pointer',
              }}
            >
              {opt.label}
            </button>
          ))}
        </div>
        {isSignup && (
          <input
            value={name}
            onChange={(e) => setName(e.target.value)}
            placeholder="Name"
            autoComplete="name"
            style={authInputStyle}
          />
        )}
        <input
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="Email"
          type="email"
          autoComplete="email"
          style={authInputStyle}
        />
        <input
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          placeholder="Password"
          type="password"
          autoComplete={isSignup ? 'new-password' : 'current-password'}
          style={authInputStyle}
        />
        {authError && (
          <div style={{ color: 'var(--red)', fontSize: 12 }}>{authError}</div>
        )}
        <Btn kind="accent" size="lg" iconRight={isSignup ? 'chevron-right' : undefined} full disabled={authLoading} type="submit">
          {authLoading ? 'Please wait...' : isSignup ? 'Create account' : 'Sign in'}
        </Btn>
      </form>
      {googleEnabled && (
        <>
          <div style={{
            display: 'flex', alignItems: 'center', gap: 10,
            margin: '14px 0 10px', color: 'var(--ink-3)', fontSize: 11,
            textTransform: 'uppercase', letterSpacing: '0.08em',
          }}>
            <div style={{ flex: 1, height: 1, background: 'var(--line)' }} />
            <span>or</span>
            <div style={{ flex: 1, height: 1, background: 'var(--line)' }} />
          </div>
          <button
            type="button"
            onClick={signInWithGoogle}
            disabled={authLoading}
            style={{
              width: '100%', height: 40,
              display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 10,
              border: '1px solid var(--line-2)', borderRadius: 8,
              background: 'var(--bg)', color: 'var(--ink)',
              fontWeight: 600, fontSize: 14, cursor: 'pointer',
            }}
          >
            <svg width="16" height="16" viewBox="0 0 48 48" aria-hidden="true">
              <path fill="#FFC107" d="M43.6 20.5H42V20H24v8h11.3c-1.6 4.6-6 8-11.3 8a12 12 0 1 1 0-24c3 0 5.8 1.1 7.9 3l5.7-5.7A20 20 0 1 0 24 44a20 20 0 0 0 19.6-23.5z"/>
              <path fill="#FF3D00" d="M6.3 14.7l6.6 4.8A12 12 0 0 1 24 12c3 0 5.8 1.1 7.9 3l5.7-5.7A20 20 0 0 0 6.3 14.7z"/>
              <path fill="#4CAF50" d="M24 44c5.2 0 9.9-2 13.4-5.2l-6.2-5.2a12 12 0 0 1-18-6.3l-6.5 5A20 20 0 0 0 24 44z"/>
              <path fill="#1976D2" d="M43.6 20.5H42V20H24v8h11.3a12 12 0 0 1-4.1 5.6l6.2 5.2C41 35.7 44 30.3 44 24c0-1.2-.1-2.4-.4-3.5z"/>
            </svg>
            Continue with Google
          </button>
        </>
      )}
    </>
  );

  const infoPages = {
    privacy: {
      title: 'Privacy',
      updated: 'June 7, 2026',
      sections: [
        {
          heading: 'What Saddle Alert collects',
          body: 'We collect the account details you provide, authentication identifiers from Google login when used, alert settings, saved filters, notification preferences, Telegram connection details if you connect Telegram, and operational records such as matches, delivery logs, and storefront observations.',
        },
        {
          heading: 'How data is used',
          body: 'Your data is used to operate your account, run alert matching, show live feed and price history, send notifications you request, troubleshoot delivery issues, protect the service from abuse, and improve reliability. We do not sell personal data.',
        },
        {
          heading: 'Service providers',
          body: 'Saddle Alert uses service providers for hosting, database storage, authentication, scheduling, and notifications. Current providers may include Vercel, Render, Supabase, Google OAuth, and Telegram. These providers process data only as needed to provide the service.',
        },
        {
          heading: 'Telegram and alerts',
          body: 'If you connect Telegram, Saddle Alert stores the chat identifier and username needed to deliver alert messages. You can disconnect Telegram from account settings. Alert notifications are sent only where you have enabled that channel.',
        },
        {
          heading: 'Retention',
          body: 'Account records, alert history, product observations, price history, and notification records are retained while the service is operating so matches and history can be shown accurately. You can ask for your account data to be deleted, subject to records we must keep for security, billing, or legal reasons.',
        },
        {
          heading: 'Security',
          body: 'We use reasonable technical and organisational controls for an early-stage service, including server-side sessions and limited access to admin tooling. No internet service can be guaranteed perfectly secure.',
        },
      ],
    },
    terms: {
      title: 'Terms',
      updated: 'June 7, 2026',
      sections: [
        {
          heading: 'Service scope',
          body: 'Saddle Alert is an independent availability alert tool. It monitors configured public boutique storefront pages and notifies users when captured items appear to match saved alerts. It does not sell retailer products and does not complete purchases for users.',
        },
        {
          heading: 'No affiliation',
          body: 'Saddle Alert is independent and is not affiliated with, endorsed by, or sponsored by any retailer or brand whose storefronts may be monitored.',
        },
        {
          heading: 'Best-effort alerts',
          body: 'Alerts, prices, images, availability, and product details are best-effort snapshots. They may be delayed, incomplete, incorrect, or stale. Storefront stock can change quickly and an alert is not a guarantee that an item can be purchased.',
        },
        {
          heading: 'User responsibility',
          body: 'Users must check the retailer listing directly before purchasing and comply with the retailer’s own terms, purchase limits, delivery rules, and account requirements. Saddle Alert is not responsible for retailer decisions, checkout failures, cancellations, or changes to product information.',
        },
        {
          heading: 'Paid access',
          body: 'Saddle Alert is being prepared as a paid product. Fees, billing periods, cancellation rules, and refund handling will be shown before payment. Payment does not guarantee that any particular product will appear or be purchasable.',
        },
        {
          heading: 'Access',
          body: 'Access may be limited, changed, paused, or removed if the service is abused, if a notification channel fails, if a storefront changes or blocks monitoring, if continued monitoring is not viable, or if required by legal, security, or operational reasons.',
        },
        {
          heading: 'Content and links',
          body: 'Saddle Alert may display product names, prices, images, and links as captured from storefront pages for identification and alerting. Retailer content and trademarks remain the property of their respective owners.',
        },
      ],
    },
    contact: {
      title: 'Contact',
      updated: 'June 7, 2026',
      sections: [
        {
          heading: 'Support',
          body: 'For account, alert, or notification issues, sign in and use account settings so support can identify the correct account and connected notification channel.',
        },
        {
          heading: 'Access requests',
          body: 'If you are not yet a customer, use the sign-up flow to request access. Saddle Alert is being built as a paid product, so access and availability may be managed manually while the service is early.',
        },
        {
          heading: 'Retailer or brand concerns',
          body: 'Saddle Alert is intended as an independent availability alert tool. Retailers, brands, or rights holders can raise concerns about monitored storefronts, links, images, or brand references with the product owner so the relevant monitoring can be reviewed or paused quickly.',
        },
        {
          heading: 'Privacy requests',
          body: 'For account deletion, data access, or privacy questions, contact support from the email address connected to your account so the request can be verified.',
        },
      ],
    },
  };

  if (authPage) {
    return (
      <div style={{
        minHeight: '100vh',
        minHeight: '100dvh',
        background: 'var(--bg)',
        display: 'flex',
        flexDirection: 'column',
      }} data-screen-label="ST · Auth">
        <header style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          padding: '18px clamp(20px, 4vw, 40px)',
          borderBottom: '1px solid var(--line)',
          flexShrink: 0,
        }}>
          <Logo />
          <Btn kind="ghost" size="sm" onClick={closeAuthPage}>Back</Btn>
        </header>
        <main style={{
          flex: 1,
          display: 'grid',
          placeItems: 'center',
          padding: 'clamp(28px, 5vw, 64px) clamp(20px, 4vw, 40px)',
        }}>
          <section style={{
            width: 'min(100%, 460px)',
            background: 'var(--bg-elev)',
            border: '1px solid var(--line)',
            borderRadius: 14,
            padding: '26px',
          }}>
            <h1 style={{
              fontFamily: 'var(--font-serif)',
              fontSize: 38,
              fontWeight: 400,
              lineHeight: 1,
              margin: '0 0 10px',
              color: 'var(--ink)',
            }}>
              {isSignup ? 'Create account' : 'Sign in'}
            </h1>
            <p style={{
              margin: '0 0 18px',
              color: 'var(--ink-3)',
              fontSize: 14,
              lineHeight: 1.45,
            }}>
              {isSignup ? 'Create your account to continue to Saddle Alert.' : 'Welcome back to Saddle Alert.'}
            </p>
            {authForm}
          </section>
        </main>
      </div>
    );
  }

  if (infoPage) {
    const page = infoPages[infoPage];
    return (
      <div style={{
        minHeight: '100vh',
        minHeight: '100dvh',
        background: 'var(--bg)',
        display: 'flex',
        flexDirection: 'column',
      }} data-screen-label={`ST · ${page.title}`}>
        <header style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          padding: '18px clamp(20px, 4vw, 40px)',
          borderBottom: '1px solid var(--line)',
          flexShrink: 0,
        }}>
          <Logo />
          <div style={{ display: 'flex', gap: 10 }}>
            <Btn kind="ghost" size="sm" onClick={() => openAuthPage('login')}>Sign in</Btn>
            <Btn kind="primary" size="sm" onClick={closeInfoPage}>Back</Btn>
          </div>
        </header>
        <main style={{
          flex: 1,
          width: 'min(100%, 820px)',
          margin: '0 auto',
          padding: 'clamp(32px, 5vw, 68px) clamp(20px, 4vw, 40px)',
        }}>
          <p style={{
            margin: '0 0 10px',
            fontFamily: 'var(--font-mono)',
            fontSize: 11,
            letterSpacing: '0.12em',
            textTransform: 'uppercase',
            color: 'var(--ink-3)',
          }}>Last updated {page.updated}</p>
          <h1 style={{
            fontFamily: 'var(--font-serif)',
            fontSize: 'clamp(42px, 7vw, 72px)',
            fontWeight: 400,
            lineHeight: 0.96,
            margin: '0 0 28px',
            color: 'var(--ink)',
          }}>{page.title}</h1>
          <section style={{
            background: 'var(--bg-elev)',
            border: '1px solid var(--line)',
            borderRadius: 14,
            overflow: 'hidden',
          }}>
            {page.sections.map((section, index) => (
              <div key={section.heading} style={{
                padding: '22px 24px',
                borderTop: index === 0 ? 'none' : '1px solid var(--line)',
              }}>
                <h2 style={{
                  margin: '0 0 8px',
                  fontSize: 16,
                  color: 'var(--ink)',
                }}>{section.heading}</h2>
                <p style={{
                  margin: 0,
                  color: 'var(--ink-2)',
                  lineHeight: 1.6,
                  fontSize: 14,
                }}>{section.body}</p>
              </div>
            ))}
          </section>
        </main>
      </div>
    );
  }

  return (
    <div style={{
      minHeight: '100vh',
      minHeight: '100dvh',
      background: 'var(--bg)',
      display: 'flex',
      flexDirection: 'column',
    }} data-screen-label="OBT · Landing">
      {/* Top bar */}
      <header style={{
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        padding: '18px clamp(20px, 4vw, 40px)',
        borderBottom: '1px solid var(--line)',
        flexShrink: 0,
      }}>
        <Logo />
        <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
          <Btn kind="ghost" size="sm" onClick={() => openAuthPage('login')}>Sign in</Btn>
          <Btn kind="primary" size="sm" onClick={() => openAuthPage('signup')}>Create account</Btn>
        </div>
      </header>

      {/* Body — two-column hero + dense data below */}
      <div style={{
        flex: 1,
        display: 'flex', flexDirection: 'column',
        padding: 'clamp(28px, 4vw, 48px) clamp(20px, 4vw, 40px) clamp(20px, 3vw, 32px)',
        gap: 'clamp(28px, 4vw, 44px)',
        maxWidth: 1280,
        width: '100%',
        margin: '0 auto',
      }}>
        {/* Hero row */}
        <div className="dw-hero-row" style={{
          display: 'grid',
          gridTemplateColumns: 'minmax(0, 1.3fr) minmax(0, 1fr)',
          gap: 48,
          alignItems: 'center',
        }}>
          {/* Left: headline */}
          <div>
            <h1 style={{
              fontFamily: 'var(--font-serif)',
              fontSize: 'clamp(40px, 7vw, 80px)',
              fontWeight: 400,
              lineHeight: 0.96,
              letterSpacing: '-0.025em',
              margin: 0,
              color: 'var(--ink)',
            }}>
              Never miss the<br />
              perfect drop.<span aria-hidden="true" style={{
                display: 'inline-block',
                position: 'relative',
                width: '1em',
                height: '0.9em',
                verticalAlign: 'baseline',
                marginLeft: '0.18em',
              }}>
                <svg viewBox="0 0 68 68" style={{
                  position: 'absolute',
                  left: 0,
                  bottom: 0,
                  width: '0.95em',
                  height: '0.95em',
                  display: 'block',
                  animation: 'dropLogo 14s cubic-bezier(.5, .05, .3, 1) infinite',
                  transformOrigin: 'center center',
                  opacity: 0,
                }}>
                  {/* Saddle Alert mark — bag with tassel charm */}
                  <path d="M11.5 16 C 13 14 16 13.5 18 15.5 C 22 20 26 22 34 22 C 42 22 46 20 50 15.5 C 52 13.5 55 14 56.5 16 L 58 58 C 58.2 62 56 64 53 64 L 15 64 C 12 64 9.8 62 10 58 Z" fill="var(--clay, #C8541F)" />
                  <path d="M14 18 C 20 22.5 26 24 34 24 C 42 24 48 22.5 54 18" fill="none" stroke="#FFFCF6" strokeWidth="0.9" opacity="0.5" strokeDasharray="1.4 2.2" />
                  <path d="M49 17 L 49 10 C 49 6.5 57 6.5 57 10 L 57 17" fill="none" stroke="var(--clay, #C8541F)" strokeWidth="1.6" strokeLinecap="round" />
                  <circle cx="53" cy="18.6" r="2.2" fill="#FFFCF6" stroke="var(--clay, #C8541F)" strokeWidth="1.3" />
                  <path d="M53 20.8 L 52 24" stroke="var(--clay, #C8541F)" strokeWidth="1.3" strokeLinecap="round" />
                  <g transform="translate(52 28) rotate(22)" fill="#FFFCF6" stroke="var(--clay, #C8541F)" strokeWidth="1.2" strokeLinejoin="round" strokeLinecap="round">
                    <path d="M -3.4 -1.4 C -3.4 -3.6 3.4 -3.6 3.4 -1.4 L 3.8 3.4 L -3.8 3.4 Z" />
                    <rect x="-4.0" y="3.4" width="8.0" height="1.8" rx="0.4" />
                    <path d="M -3.8 5.2 L 3.8 5.2 L 4.6 13.6 C 4.6 14.2 4.2 14.4 3.6 14.4 L -3.6 14.4 C -4.2 14.4 -4.6 14.2 -4.6 13.6 Z" />
                    <g fill="none" stroke="var(--clay, #C8541F)" strokeWidth="0.7">
                      <path d="M -2.6 5.5 L -2.9 13.8" />
                      <path d="M -1.3 5.5 L -1.4 13.9" />
                      <path d="M 0 5.5 L 0 14.0" />
                      <path d="M 1.3 5.5 L 1.4 13.9" />
                      <path d="M 2.6 5.5 L 2.9 13.8" />
                    </g>
                    <path d="M -1.6 -2.4 L 1.6 -2.4" stroke="var(--clay, #C8541F)" strokeWidth="0.9" fill="none" />
                  </g>
                </svg>
              </span>
            </h1>
          </div>

          {/* Right: subhead + CTA card */}
          <div style={{
            padding: '24px 26px',
            background: 'var(--bg-elev)',
            border: '1px solid var(--line)',
            borderRadius: 14,
          }}>
            <p style={{
              fontSize: 15,
              color: 'var(--ink-2)',
              lineHeight: 1.55,
              margin: 0,
              marginBottom: 18,
            }}>
              A small program that watches official boutique storefronts and pings you the moment an item matching your list appears.
            </p>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
              <Btn kind="accent" size="lg" iconRight="chevron-right" full onClick={() => openAuthPage('signup')}>
                Create account
              </Btn>
              <Btn kind="ghost" size="lg" full onClick={() => openAuthPage('login')}>
                Sign in
              </Btn>
            </div>
          </div>
        </div>

        {/* Region counters */}
        <div>
          <div style={{
            display: 'flex', justifyContent: 'space-between', alignItems: 'baseline',
            fontSize: 11, fontWeight: 500,
            letterSpacing: '0.08em', color: 'var(--ink-3)',
            textTransform: 'uppercase',
            marginBottom: 10, padding: '0 2px',
          }}>
            <span>Regions</span>
            <span style={{ fontFamily: 'var(--font-mono)' }}>Drops today / listed</span>
          </div>
          <div style={{
            display: 'grid',
            gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))',
            gap: 0,
            borderTop: '1px solid var(--line-2)',
            borderBottom: '1px solid var(--line-2)',
            background: 'var(--bg-elev)',
          }}>
            {regionCounts.map((r, i) => (
              <div key={r.code} style={{
                padding: '18px 22px',
                borderLeft: i === 0 ? 'none' : '1px solid var(--line)',
                display: 'flex', flexDirection: 'column', gap: 10,
              }}>
                <div style={{
                  display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                  fontSize: 10.5, letterSpacing: '0.12em',
                  color: 'var(--ink-3)',
                  fontFamily: 'var(--font-mono)',
                }}>
                  <span>{r.displayCode || r.code} · {r.name.toUpperCase()}</span>
                  <RegionFlags code={r.code} size={14} />
                </div>
                <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
                  <span style={{
                    fontFamily: 'var(--font-serif)',
                    fontSize: 40, lineHeight: 1, color: 'var(--ink)',
                    letterSpacing: '-0.02em',
                  }} className="num">{r.droppedToday}</span>
                  <span style={{ fontSize: 11, color: 'var(--ink-3)', fontFamily: 'var(--font-mono)' }}>
                    / {r.total} listed
                  </span>
                </div>
                <div style={{
                  height: 3,
                  borderRadius: 999,
                  background: 'var(--line)',
                  overflow: 'hidden',
                }}>
                  <div style={{
                    width: `${r.total ? Math.max(4, Math.min(100, (r.droppedToday / r.total) * 100)) : 0}%`,
                    height: '100%',
                    background: 'var(--orange)',
                  }} />
                </div>
              </div>
            ))}
          </div>
        </div>

        {/* Live ticker */}
        <div style={{ minWidth: 0 }}>
          <div style={{
            display: 'flex', justifyContent: 'space-between', alignItems: 'baseline',
            fontSize: 11, fontWeight: 500,
            letterSpacing: '0.08em', color: 'var(--ink-3)',
            textTransform: 'uppercase',
            marginBottom: 10, padding: '0 2px',
          }}>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 7 }}>
              <span style={{
                width: 6, height: 6, borderRadius: '50%',
                background: 'var(--green)',
                animation: 'pulse 1.6s ease-in-out infinite',
              }} />
              Live feed
            </span>
            <span style={{ fontFamily: 'var(--font-mono)' }}>{landingSnapshot?.ready ? `${ticker.length} of ${visibleDrops.length}` : 'real data only'}</span>
          </div>
          <div style={{
            background: 'var(--bg-elev)',
            border: '1px solid var(--line)',
            borderRadius: 12,
            overflow: 'hidden',
          }}>
            {isUnsupportedCountry && (
              <div style={{ padding: '26px 18px', color: 'var(--ink-3)', fontSize: 13, textAlign: 'center' }}>
                Saddle Alert does not cover {visitorRegion.country || 'your country'} yet. Current storefront trackers are AU and SG.
              </div>
            )}
            {!ticker.length && !isUnsupportedCountry && (
              <div style={{ padding: '26px 18px', color: 'var(--ink-3)', fontSize: 13, textAlign: 'center' }}>
                No real storefront snapshot yet. Sign in to create alerts while the cloud checker builds the live feed.
              </div>
            )}
            {ticker.map((item, i) => (
              <div key={item.id} className="dw-ticker-row" style={{
                display: 'grid',
                gridTemplateColumns: '60px 32px minmax(0, 1fr) 82px 92px',
                alignItems: 'center',
                gap: 12,
                padding: '11px 18px',
                borderTop: i === 0 ? 'none' : '1px solid var(--line)',
                fontSize: 13,
                color: 'var(--ink-2)',
              }}>
                <span className="mono" style={{ fontSize: 11, color: 'var(--ink-3)' }}>
                  {timeAgo(item.ago).replace(' ago', '').replace(' min', 'm').replace(' hr', 'h').replace(' d', 'd')}
                </span>
                <span style={{
                  width: 26, height: 26, borderRadius: 5,
                  background: (window.DW_DATA.COLORS.find(c => c.name === item.color) || {}).swatch || '#ccc',
                  boxShadow: 'inset 0 0 0 1px rgba(0,0,0,0.1)',
                }} />
                <span style={{
                  display: 'flex', alignItems: 'baseline', gap: 8, minWidth: 0,
                  flexWrap: 'wrap',
                  overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis',
                }}>
                  <span style={{ fontFamily: 'var(--font-serif)', fontSize: 17, color: 'var(--ink)' }}>
                    {item.model} {item.size}
                  </span>
                  <span style={{
                    color: 'var(--ink-3)', fontSize: 12.5,
                    minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis',
                  }}>
                    · {item.color} · {item.leather}
                  </span>
                </span>
                <span style={{ display: 'flex', justifyContent: 'center' }}>
                  <RegionBadge code={item.region} />
                </span>
                <span style={{ textAlign: 'right', fontFamily: 'var(--font-serif)', fontSize: 16 }}>
                  {formatCompactMoney(item.price, item.currency, '—')}
                </span>
              </div>
            ))}
          </div>
        </div>
      </div>

      {/* Status footer */}
      <footer style={{
        padding: '14px clamp(20px, 4vw, 40px)',
        borderTop: '1px solid var(--line)',
        display: 'flex', justifyContent: 'space-between', alignItems: 'center',
        flexWrap: 'wrap', gap: 12,
        fontFamily: 'var(--font-mono)', fontSize: 10.5,
        color: 'var(--ink-3)',
        letterSpacing: '0.1em', textTransform: 'uppercase',
        flexShrink: 0,
      }}>
        <span>© 2026 SADDLE TRACKER · NOT AFFILIATED WITH ANY RETAILER</span>
        <span style={{ display: 'flex', gap: 18 }}>
          <a href="?page=privacy" onClick={(e) => { e.preventDefault(); openInfoPage('privacy'); }} style={{ textDecoration: 'none', color: 'inherit' }}>PRIVACY</a>
          <a href="?page=terms" onClick={(e) => { e.preventDefault(); openInfoPage('terms'); }} style={{ textDecoration: 'none', color: 'inherit' }}>TERMS</a>
          <a href="?page=contact" onClick={(e) => { e.preventDefault(); openInfoPage('contact'); }} style={{ textDecoration: 'none', color: 'inherit' }}>CONTACT</a>
        </span>
      </footer>
    </div>
  );
};

const authInputStyle = {
  height: 42,
  border: '1px solid var(--line-2)',
  borderRadius: 8,
  background: 'var(--bg)',
  color: 'var(--ink)',
  padding: '0 12px',
  fontSize: 14,
  outline: 'none',
};

const TickerRow = ({ item, last }) => {
  const swatch = window.DW_DATA.COLORS.find(c => c.name === item.color);
  return (
    <div style={{
      display: 'grid',
      gridTemplateColumns: '40px 1fr auto auto auto',
      gap: 14,
      alignItems: 'center',
      padding: '14px 20px',
      borderBottom: last ? 'none' : '1px solid var(--line)',
    }}>
      <div style={{
        width: 32, height: 32, borderRadius: 6,
        background: swatch ? swatch.swatch : '#ccc',
        boxShadow: 'inset 0 0 0 1px rgba(0,0,0,0.1)',
      }} />
      <div style={{ minWidth: 0 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
          <span style={{ fontFamily: 'var(--font-serif)', fontSize: 16 }}>
            {item.model} {item.size}
          </span>
          <RarityBadge rarity={item.rarity} score={item.rarityScore} basis={item.rarityBasis} compact />
        </div>
        <div style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 2 }}>
          {item.color} · {item.leather}
        </div>
      </div>
      <RegionBadge code={item.region} />
      <span className="num" style={{ fontFamily: 'var(--font-serif)', fontSize: 16 }}>
        {formatCompactMoney(item.price, item.currency, '—')}
      </span>
      <span className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', minWidth: 72, textAlign: 'right' }}>
        {timeAgo(item.ago)}
      </span>
    </div>
  );
};

// ====================================================================
// PRICE HISTORY CHART
// ====================================================================
const PriceChart = ({ data, currency }) => {
  const W = 560, H = 180, P = 28;
  const prices = data.map(d => d.price);
  const min = Math.min(...prices);
  const max = Math.max(...prices);
  const range = Math.max(max - min, 1);
  const xs = data.map((_, i) => P + (i * (W - 2 * P)) / (data.length - 1));
  const ys = data.map(d => H - P - ((d.price - min) / range) * (H - 2 * P));

  const pathLine = xs.map((x, i) => `${i === 0 ? 'M' : 'L'} ${x.toFixed(1)} ${ys[i].toFixed(1)}`).join(' ');
  const pathArea = `${pathLine} L ${xs[xs.length - 1].toFixed(1)} ${H - P} L ${xs[0].toFixed(1)} ${H - P} Z`;

  const fmt = new Intl.NumberFormat('en-US');
  const symbol = priceSymbolFor(currency);
  const moneyPrefix = formatCurrencyCode(currency) ? `${formatCurrencyCode(currency)} ${symbol}` : symbol;

  const current = prices[prices.length - 1];
  const first = prices[0];
  const pct = ((current - first) / first) * 100;

  return (
    <div>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', marginBottom: 12 }}>
        <div>
          <div style={{ fontSize: 11, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.1em', fontFamily: 'var(--font-mono)' }}>
            Current retail
          </div>
          <div style={{ fontFamily: 'var(--font-serif)', fontSize: 32, lineHeight: 1.1, marginTop: 4 }} className="num">
            {moneyPrefix}{fmt.format(current)}
          </div>
        </div>
        <div style={{ textAlign: 'right' }}>
          <div style={{ fontSize: 11, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.1em', fontFamily: 'var(--font-mono)' }}>
            12 months
          </div>
          <div style={{
            fontFamily: 'var(--font-mono)',
            fontSize: 14,
            marginTop: 6,
            color: pct >= 0 ? 'var(--green)' : 'var(--red)',
            fontWeight: 600,
          }}>
            {pct >= 0 ? '+' : ''}{pct.toFixed(1)}% · {pct >= 0 ? '+' : ''}{moneyPrefix}{fmt.format(Math.abs(Math.round(current - first)))}
          </div>
        </div>
      </div>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" height="auto" style={{ display: 'block' }}>
        {/* gridlines */}
        {[0, 0.5, 1].map(t => (
          <line key={t}
            x1={P} x2={W - P}
            y1={P + t * (H - 2 * P)}
            y2={P + t * (H - 2 * P)}
            stroke="var(--line)"
            strokeDasharray={t === 0 || t === 1 ? '0' : '2 3'}
          />
        ))}
        <path d={pathArea} fill="var(--orange)" opacity="0.08" />
        <path d={pathLine} fill="none" stroke="var(--orange)" strokeWidth="2" strokeLinejoin="round" strokeLinecap="round" />
        {xs.map((x, i) => (
          <circle key={i} cx={x} cy={ys[i]} r={i === xs.length - 1 ? 4 : 2.5}
            fill={i === xs.length - 1 ? 'var(--orange)' : 'var(--bg-elev)'}
            stroke="var(--orange)"
            strokeWidth="1.5"
          />
        ))}
        {/* X labels — every other month to avoid clutter */}
        {data.map((d, i) => i % 2 === 0 && (
          <text key={i} x={xs[i]} y={H - 8}
            fontSize="10"
            fill="var(--ink-3)"
            textAnchor="middle"
            fontFamily="var(--font-mono)"
          >{d.month}</text>
        ))}
        {/* Y labels */}
        <text x={4} y={P + 4} fontSize="10" fill="var(--ink-3)" fontFamily="var(--font-mono)">{moneyPrefix}{fmt.format(max)}</text>
        <text x={4} y={H - P + 4} fontSize="10" fill="var(--ink-3)" fontFamily="var(--font-mono)">{moneyPrefix}{fmt.format(min)}</text>
      </svg>
    </div>
  );
};

// ====================================================================
// STOCK DETAIL — slide-in panel
// ====================================================================
const StockDetail = ({ item, onClose, onCreateAlertFromItem }) => {
  const [isMobile, setIsMobile] = useState_e(typeof window !== 'undefined' ? window.innerWidth < 760 : false);
  const [priceHistory, setPriceHistory] = useState_e({ loading: false, points: [], error: null });
  useEffect_e(() => {
    const onResize = () => setIsMobile(window.innerWidth < 760);
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);

  if (!item) return null;
  const swatch = findColorSwatch(item.color);
  useEffect_e(() => {
    if (!item?.url) {
      setPriceHistory({ loading: false, points: [], error: null });
      return;
    }
    setPriceHistory({ loading: true, points: [], error: null });
    fetch(`/api/price-history?url=${encodeURIComponent(item.url)}`)
      .then(res => res.json())
      .then(json => setPriceHistory({ loading: false, points: Array.isArray(json.points) ? json.points : [], error: json.error || null }))
      .catch(err => setPriceHistory({ loading: false, points: [], error: String(err) }));
  }, [item.id, item.url]);

  const filteredPoints = useMemo_e(() => Array.isArray(priceHistory.points) ? priceHistory.points : [], [priceHistory.points]);

  const history = useMemo_e(() => {
    const grouped = new Map();
    for (const point of filteredPoints) {
      if (typeof point.price !== 'number') continue;
      const date = new Date(point.observedAt);
      if (!Number.isFinite(date.getTime())) continue;
      const key = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
      const label = date.toLocaleDateString('en', { month: 'short' });
      const current = grouped.get(key) || { month: label, price: point.price, stockEvents: 0 };
      current.price = point.price;
      current.stockEvents += 1;
      grouped.set(key, current);
    }
    return [...grouped.values()];
  }, [filteredPoints]);
  const displayName = item.title || `${item.model} ${item.size || ''}`.trim();
  const recentDrops = [...filteredPoints].slice(-5).reverse();
  const availabilityLabel = (value) => {
    const v = String(value || '').toLowerCase();
    if (v === 'in_stock') return 'In stock';
    if (v === 'unavailable' || v === 'sold_out') return 'Out of stock';
    if (v === 'unknown' || !v) return 'Not verified';
    return v.replace(/_/g, ' ');
  };

  return (
    <div
      onClick={onClose}
      style={{
        position: 'fixed',
        inset: 0,
        background: 'rgba(26, 23, 20, 0.45)',
        zIndex: 60,
        display: 'flex',
        justifyContent: 'flex-end',
        animation: 'fadeIn 0.18s ease',
        backdropFilter: 'blur(2px)',
      }}
    >
      <div
        onClick={(e) => e.stopPropagation()}
        style={{
          width: isMobile ? '100%' : 560,
          maxWidth: '100%',
          height: '100%',
          background: 'var(--bg)',
          overflowY: 'auto',
          animation: isMobile ? 'slideUpFull 0.25s ease' : 'slideInRight 0.25s ease',
          boxShadow: '-12px 0 32px rgba(0,0,0,0.12)',
        }}
      >
        {/* Top bar */}
        <div style={{
          position: 'sticky', top: 0, zIndex: 2,
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          padding: '16px 20px',
          background: 'var(--bg)',
          borderBottom: '1px solid var(--line)',
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <StatusPill status={item.status} />
            <RarityBadge rarity={item.rarity} score={item.rarityScore} basis={item.rarityBasis} compact />
          </div>
          <button onClick={onClose} style={{
            width: 32, height: 32,
            background: 'var(--bg-elev)',
            border: '1px solid var(--line-2)',
            borderRadius: 999,
            cursor: 'pointer',
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
            color: 'var(--ink-2)',
          }}>
            <Icon name="x" size={16} />
          </button>
        </div>

        <div style={{ padding: '24px 28px 32px' }}>
          {/* Hero */}
          <div style={{
            padding: '32px 28px',
            background: 'var(--bg-elev)',
            border: '1px solid var(--line)',
            borderRadius: 14,
            marginBottom: 20,
            display: 'flex', alignItems: 'center', gap: 24,
          }}>
            <div style={{
              width: 168, height: 168, borderRadius: 12,
              background: swatch ? swatch.swatch : '#ccc',
              boxShadow: 'inset 0 0 0 1px rgba(0,0,0,0.1)',
              position: 'relative', flexShrink: 0, overflow: 'hidden',
            }}>
              {item.imageUrl ? (
                <img
                  src={item.imageUrl}
                  alt={displayName}
                  loading="lazy"
                  style={{ width: '100%', height: '100%', objectFit: 'contain', background: '#fff' }}
                />
              ) : (
                <div style={{
                  position: 'absolute',
                  top: 12, left: '50%', transform: 'translateX(-50%)',
                  width: 48, height: 5,
                  background: item.hw === 'Gold' ? '#c9a45a' : item.hw === 'Rose Gold' ? '#d99c8e' : '#cfd2d4',
                  borderRadius: 2,
                  boxShadow: '0 0 0 1px rgba(0,0,0,0.1)',
                }} />
              )}
            </div>
            <div style={{ minWidth: 0 }}>
              <div style={{ fontFamily: 'var(--font-serif)', fontSize: 32, lineHeight: 1, letterSpacing: '-0.01em' }}>
                {displayName}
              </div>
              <div style={{ fontSize: 14, color: 'var(--ink-2)', marginTop: 8 }}>
                {[item.model, item.size, item.color, item.leather].filter(Boolean).join(' · ')}
              </div>
              <div style={{
                fontFamily: 'var(--font-mono)',
                fontSize: 11,
                color: 'var(--ink-3)',
                marginTop: 8,
                letterSpacing: '0.05em',
                lineHeight: 1.45,
                whiteSpace: 'normal',
                overflowWrap: 'anywhere',
                wordBreak: 'break-word',
              }}>
                REF · {item.id}
              </div>
            </div>
          </div>

          {/* Specs grid */}
          <div style={{
            display: 'grid',
            gridTemplateColumns: 'repeat(2, 1fr)',
            gap: 10,
            marginBottom: 24,
          }}>
            {[
              { l: 'Spotted in', v: <RegionBadge code={item.region} /> },
              { l: 'Price', v: <span className="num" style={{ fontFamily: 'var(--font-serif)', fontSize: 18 }}>{formatMoney(item.price, item.currency)}</span> },
              { l: 'Status', v: <StatusPill status={item.status} /> },
              { l: 'First seen', v: <span style={{ fontSize: 13 }}>{timeAgo(item.ago)}</span> },
            ].map((s, i) => (
              <div key={i} style={{
                padding: '12px 14px',
                background: 'var(--bg-elev)',
                border: '1px solid var(--line)',
                borderRadius: 10,
              }}>
                <div style={{ fontSize: 10.5, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.1em', fontFamily: 'var(--font-mono)', marginBottom: 6 }}>{s.l}</div>
                <div>{s.v}</div>
              </div>
            ))}
          </div>

          {/* Price history */}
          <div style={{
            padding: '20px 22px',
            background: 'var(--bg-elev)',
            border: '1px solid var(--line)',
            borderRadius: 14,
            marginBottom: 20,
          }}>
            <div style={{ fontSize: 11, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.1em', fontFamily: 'var(--font-mono)', marginBottom: 16 }}>
              Price history · {(window.DW_DATA.REGIONS.find(r => r.code === item.region) || {}).displayCode || item.region}
            </div>
            {priceHistory.loading ? (
              <div style={{ fontSize: 13, color: 'var(--ink-3)' }}>Loading real observation history...</div>
            ) : history.length >= 2 ? (
              <PriceChart data={history} currency={item.currency} />
            ) : (
              <div style={{
                padding: '22px 14px',
                border: '1px dashed var(--line-2)',
                borderRadius: 10,
                color: 'var(--ink-3)',
                fontSize: 13,
                textAlign: 'center',
              }}>
                Not enough real price history yet. This will build as the cloud checker sees the same item over multiple runs.
              </div>
            )}
          </div>

          {/* Recent drops */}
          <div style={{ marginBottom: 24 }}>
            <div style={{ fontSize: 11, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.1em', fontFamily: 'var(--font-mono)', marginBottom: 12 }}>
              Observation history · this listing
            </div>
            <div style={{
              background: 'var(--bg-elev)',
              border: '1px solid var(--line)',
              borderRadius: 12,
              overflow: 'hidden',
            }}>
              {recentDrops.length > 0 && (
                <div style={{
                  display: 'grid',
                  gridTemplateColumns: '84px minmax(72px, 1fr) 76px 104px',
                  columnGap: 12,
                  alignItems: 'center',
                  padding: '9px 24px 9px 16px',
                  background: 'var(--bg)',
                  borderBottom: '1px solid var(--line)',
                  fontSize: 10.5,
                  color: 'var(--ink-3)',
                  textTransform: 'uppercase',
                  letterSpacing: '0.08em',
                  fontFamily: 'var(--font-mono)',
                }}>
                  <span>Region</span>
                  <span>Seen</span>
                  <span style={{ textAlign: 'right' }}>Price</span>
                  <span style={{ textAlign: 'right' }}>Status</span>
                </div>
              )}
              {recentDrops.length === 0 && (
                <div style={{ padding: '18px 16px', color: 'var(--ink-3)', fontSize: 13 }}>
                  No repeated observations yet.
                </div>
              )}
              {recentDrops.map((d, i) => (
                <div key={i} style={{
                  display: 'grid',
                  gridTemplateColumns: '84px minmax(72px, 1fr) 76px 104px',
                  columnGap: 12,
                  alignItems: 'center',
                  padding: '11px 24px 11px 16px',
                  borderTop: i === 0 ? 'none' : '1px solid var(--line)',
                  fontSize: 13,
                }}>
                  <div style={{ justifySelf: 'start' }}>
                    <RegionBadge code={d.region} />
                  </div>
                  <span style={{ color: 'var(--ink-2)' }} className="mono">{timeAgo(Math.max(0, Math.round((Date.now() - new Date(d.observedAt).getTime()) / 60000)))}</span>
                  <span className="num" style={{ fontFamily: 'var(--font-serif)', fontSize: 15, textAlign: 'right' }}>
                    {formatMoney(d.price, d.currency || item.currency, '—')}
                  </span>
                  <span style={{
                    fontSize: 11,
                    color: d.availability === 'in_stock' ? 'var(--green)' : 'var(--ink-3)',
                    fontFamily: 'var(--font-mono)',
                    letterSpacing: '0.05em',
                    textAlign: 'right',
                  }}>
                    {availabilityLabel(d.availability).toUpperCase()}
                  </span>
                </div>
              ))}
            </div>
          </div>

          {/* Actions */}
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
            {item.status === 'live' && (
              <Btn
                kind="accent"
                size="lg"
                full
                iconRight="external"
                disabled={!item.url}
                onClick={() => item.url && window.open(item.url, '_blank', 'noopener,noreferrer')}
              >
                Open listing
              </Btn>
            )}
            <Btn kind="secondary" size="lg" full icon="bell" onClick={() => onCreateAlertFromItem(item)}>
              Create alert for this spec
            </Btn>
          </div>
        </div>
      </div>
    </div>
  );
};

// Inject panel animations
if (typeof document !== 'undefined' && !document.getElementById('dw-panel-keyframes')) {
  const s = document.createElement('style');
  s.id = 'dw-panel-keyframes';
  s.textContent = `
    @keyframes slideInRight {
      from { transform: translateX(100%); }
      to { transform: translateX(0); }
    }
    @keyframes slideUpFull {
      from { transform: translateY(100%); }
      to { transform: translateY(0); }
    }
    @keyframes dropLogo {
      0%, 4% {
        opacity: 0;
        transform: translateY(-1.05em) rotate(-22deg);
      }
      10% {
        opacity: 1;
        transform: translateY(-1.05em) rotate(-22deg);
      }
      20% {
        opacity: 1;
        transform: translateY(0.06em) rotate(-5deg);
      }
      24% {
        transform: translateY(-0.04em) rotate(-10deg);
      }
      28%, 90% {
        opacity: 1;
        transform: translateY(0) rotate(-8deg);
      }
      97% {
        opacity: 1;
        transform: translateY(-0.4em) rotate(-16deg);
      }
      100% {
        opacity: 0;
        transform: translateY(-1.05em) rotate(-22deg);
      }
    }
    @media (prefers-reduced-motion: reduce) {
      [style*="dropLogo"] {
        animation: none !important;
        opacity: 1 !important;
        transform: rotate(-8deg) !important;
      }
    }
    @media (max-width: 600px) {
      .dw-live-pill { display: none !important; }
    }
    @media (min-width: 601px) {
      .dw-live-pill { display: inline-flex !important; }
    }
    @media (max-width: 820px) {
      .dw-hero-row { grid-template-columns: 1fr !important; gap: 28px !important; }
    }
    @media (max-width: 640px) {
      .dw-ticker-row {
        grid-template-columns: 28px minmax(0, 1fr) auto auto !important;
      }
      .dw-ticker-row > :nth-child(1) {
        display: none !important;
      }
    }
  `;
  document.head.appendChild(s);
}

Object.assign(window, { LandingPage, StockDetail, PriceChart });

// ====================================================================
// ONBOARDING — first run walkthrough
// ====================================================================
const Onboarding = ({ onComplete, onSkip }) => {
  const [step, setStep] = useState_e(0);
  const [regions, setRegions] = useState_e(['AU']);
  const [model, setModel] = useState_e(null);

  const toggleRegion = (code) => {
    if (code !== 'AU') return;
    setRegions(regions.includes(code) ? regions.filter(c => c !== code) : [...regions, code]);
  };

  const steps = [
    {
      title: 'Welcome to Saddle Alert.',
      eyebrow: '01 · Hello',
      body: 'Three quick questions and we\'ll start watching the shops for you. About 30 seconds.',
      content: (
        <div style={{
          padding: '20px 24px',
          background: 'var(--bg)',
          border: '1px dashed var(--line-2)',
          borderRadius: 12,
          display: 'flex', flexDirection: 'column', gap: 14,
        }}>
          {[
            { n: '1', t: 'Pick the regions you can buy from' },
            { n: '2', t: 'Pick a model you\'re hunting' },
            { n: '3', t: 'We\'ll create your first alert' },
          ].map(it => (
            <div key={it.n} style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
              <span style={{
                width: 24, height: 24, borderRadius: '50%',
                background: 'var(--orange)',
                color: '#fff',
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                fontSize: 12, fontWeight: 600,
                fontFamily: 'var(--font-mono)',
                flexShrink: 0,
              }}>{it.n}</span>
              <span style={{ fontSize: 14, color: 'var(--ink-2)' }}>{it.t}</span>
            </div>
          ))}
        </div>
      ),
      next: 'Let\'s go',
      valid: true,
    },
    {
      title: 'Where can you shop?',
      eyebrow: '02 · Regions',
      body: 'We\'ll only check stores in these regions. Change anytime in Settings.',
      content: (
        <div style={{
          display: 'grid',
          gridTemplateColumns: 'repeat(2, 1fr)',
          gap: 10,
        }}>
          {window.DW_DATA.REGIONS.map(r => {
            const on = regions.includes(r.code);
            const disabled = r.code !== 'AU';
            return (
              <button
                key={r.code}
                onClick={() => toggleRegion(r.code)}
                disabled={disabled}
                style={{
                  padding: '16px 18px',
                  background: on ? 'var(--orange-soft)' : 'var(--bg)',
                  border: `1px solid ${on ? 'var(--orange)' : 'var(--line-2)'}`,
                  borderRadius: 12,
                  cursor: disabled ? 'not-allowed' : 'pointer',
                  opacity: disabled ? 0.42 : 1,
                  textAlign: 'left',
                  display: 'flex', alignItems: 'center', gap: 14,
                  transition: 'all 0.12s ease',
                }}
              >
                <RegionFlags code={r.code} size={22} />
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 14, fontWeight: 500, color: 'var(--ink)' }}>{r.name}</div>
                  <div style={{ fontSize: 11, color: 'var(--ink-3)', fontFamily: 'var(--font-mono)' }}>
                    {r.displayCode || r.code} · {r.currency}{disabled ? ' · Disabled for now' : ''}
                  </div>
                </div>
                {on && (
                  <div style={{
                    width: 22, height: 22, borderRadius: '50%',
                    background: 'var(--orange)',
                    color: '#fff',
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                  }}>
                    <Icon name="check" size={13} stroke={2.4} />
                  </div>
                )}
              </button>
            );
          })}
        </div>
      ),
      next: 'Next',
      valid: regions.length > 0,
    },
    {
      title: 'What are you hunting?',
      eyebrow: '03 · First alert',
      body: 'Pick one to start — you can refine it next, or add more alerts later.',
      content: (
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          {window.DW_DATA.MODELS.map(m => (
            <Chip key={m.id} active={model === m.name} onClick={() => setModel(m.name)}>
              {m.name}
            </Chip>
          ))}
        </div>
      ),
      next: model ? `Set up alert for ${model}` : 'Pick a model',
      valid: !!model,
    },
  ];

  const s = steps[step];
  const last = step === steps.length - 1;

  return (
    <div style={{
      position: 'fixed', inset: 0,
      background: 'rgba(26, 23, 20, 0.55)',
      backdropFilter: 'blur(3px)',
      zIndex: 70,
      display: 'flex',
      alignItems: 'center', justifyContent: 'center',
      padding: 16,
      animation: 'fadeIn 0.2s ease',
    }}>
      <div style={{
        width: '100%',
        maxWidth: 520,
        maxHeight: '90vh',
        overflowY: 'auto',
        background: 'var(--bg-elev)',
        borderRadius: 18,
        boxShadow: '0 24px 64px rgba(0,0,0,0.25)',
        animation: 'slideUp 0.25s ease',
        display: 'flex',
        flexDirection: 'column',
      }}>
        {/* Header */}
        <div style={{
          padding: '20px 28px 0',
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
            {steps.map((_, i) => (
              <div key={i} style={{
                width: i === step ? 24 : 6,
                height: 6,
                borderRadius: 4,
                background: i <= step ? 'var(--orange)' : 'var(--line-2)',
                transition: 'all 0.2s ease',
              }} />
            ))}
          </div>
          <button onClick={onSkip} style={{
            background: 'transparent',
            border: 'none',
            color: 'var(--ink-3)',
            fontSize: 13,
            cursor: 'pointer',
            padding: 4,
          }}>Skip</button>
        </div>

        {/* Body */}
        <div style={{ padding: '24px 28px 28px', flex: 1 }}>
          <div style={{
            fontFamily: 'var(--font-mono)',
            fontSize: 11,
            letterSpacing: '0.15em',
            color: 'var(--orange)',
            textTransform: 'uppercase',
            marginBottom: 10,
          }}>{s.eyebrow}</div>
          <h2 style={{
            fontFamily: 'var(--font-serif)',
            fontSize: 32,
            fontWeight: 400,
            margin: 0,
            letterSpacing: '-0.02em',
            lineHeight: 1.08,
          }}>{s.title}</h2>
          <p style={{
            fontSize: 14,
            color: 'var(--ink-2)',
            margin: '10px 0 22px',
            lineHeight: 1.5,
          }}>{s.body}</p>
          <div>{s.content}</div>
        </div>

        {/* Footer */}
        <div style={{
          padding: '18px 28px 24px',
          borderTop: '1px solid var(--line)',
          display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 10,
        }}>
          <Btn
            kind="ghost"
            size="md"
            icon="chevron-left"
            onClick={() => setStep(Math.max(0, step - 1))}
            disabled={step === 0}
          >Back</Btn>
          <Btn
            kind="accent"
            size="md"
            iconRight={last ? null : 'chevron-right'}
            disabled={!s.valid}
            onClick={() => {
              if (last) onComplete({ regions, model });
              else setStep(step + 1);
            }}
          >{s.next}</Btn>
        </div>
      </div>
    </div>
  );
};

// ====================================================================
// NOTIFICATION INBOX — popover from bell
// ====================================================================
const NotificationInbox = ({ notifications, isAdmin, onClose, onMarkAllRead, onSelectNotif, onResendNotif, anchor }) => {
  const [isMobile, setIsMobile] = useState_e(typeof window !== 'undefined' ? window.innerWidth < 760 : false);
  useEffect_e(() => {
    const onResize = () => setIsMobile(window.innerWidth < 760);
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);

  const today = notifications.filter(n => n.ago < 60 * 24);
  const earlier = notifications.filter(n => n.ago >= 60 * 24);
  const unreadCount = notifications.filter(n => !n.read).length;

  return (
    <div
      onClick={onClose}
      style={{
        position: 'fixed', inset: 0, zIndex: 55,
        background: isMobile ? 'rgba(26, 23, 20, 0.4)' : 'transparent',
        animation: 'fadeIn 0.15s ease',
      }}
    >
      <div
        onClick={(e) => e.stopPropagation()}
        style={{
          position: 'absolute',
          ...(isMobile
            ? { left: 12, right: 12, top: 64, maxHeight: 'calc(100vh - 80px)' }
            : { right: 24, top: anchor?.bottom != null ? anchor.bottom + 8 : 64, width: 380, maxHeight: 'calc(100vh - 100px)' }
          ),
          background: 'var(--bg-elev)',
          border: '1px solid var(--line-2)',
          borderRadius: 14,
          boxShadow: '0 12px 40px rgba(0,0,0,0.16)',
          display: 'flex', flexDirection: 'column',
          overflow: 'hidden',
          animation: 'slideUp 0.18s ease',
        }}
      >
        <div style={{
          padding: '14px 18px',
          borderBottom: '1px solid var(--line)',
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <span style={{ fontFamily: 'var(--font-serif)', fontSize: 20 }}>Inbox</span>
            {unreadCount > 0 && (
              <span style={{
                fontSize: 11, fontWeight: 600,
                background: 'var(--orange)',
                color: '#fff',
                padding: '1px 7px',
                borderRadius: 999,
                fontFamily: 'var(--font-mono)',
              }}>{unreadCount} new</span>
            )}
          </div>
          {unreadCount > 0 && (
            <button onClick={onMarkAllRead} style={{
              background: 'transparent',
              border: 'none',
              color: 'var(--ink-2)',
              fontSize: 12,
              cursor: 'pointer',
              padding: 4,
              fontWeight: 500,
            }}>Mark all read</button>
          )}
        </div>
        <div style={{ overflowY: 'auto', flex: 1 }}>
          {today.length > 0 && (
            <NotifGroup label="Today" items={today} isAdmin={isAdmin} onSelectNotif={onSelectNotif} onResendNotif={onResendNotif} />
          )}
          {earlier.length > 0 && (
            <NotifGroup label="Earlier" items={earlier} isAdmin={isAdmin} onSelectNotif={onSelectNotif} onResendNotif={onResendNotif} />
          )}
          {notifications.length === 0 && (
            <div style={{
              padding: '40px 24px',
              textAlign: 'center',
              color: 'var(--ink-3)',
            }}>
              <div style={{ fontFamily: 'var(--font-serif)', fontSize: 20, color: 'var(--ink-2)', marginBottom: 4 }}>
                Nothing yet
              </div>
              <div style={{ fontSize: 13 }}>You'll see matches here as they happen.</div>
            </div>
          )}
        </div>
        <div style={{
          padding: '10px 16px',
          borderTop: '1px solid var(--line)',
          fontSize: 11,
          color: 'var(--ink-3)',
          fontFamily: 'var(--font-mono)',
          letterSpacing: '0.05em',
          textAlign: 'center',
        }}>
          MATCHES OLDER THAN 30 DAYS ARE ARCHIVED
        </div>
      </div>
    </div>
  );
};

const NotifGroup = ({ label, items, isAdmin, onSelectNotif, onResendNotif }) => (
  <div>
    <div style={{
      padding: '10px 18px 8px',
      fontSize: 10.5,
      letterSpacing: '0.12em',
      textTransform: 'uppercase',
      color: 'var(--ink-3)',
      fontFamily: 'var(--font-mono)',
      background: 'var(--bg)',
      borderTop: '1px solid var(--line)',
      borderBottom: '1px solid var(--line)',
    }}>{label}</div>
    {items.map((n, i) => (
      <NotifRow key={n.id} notif={n} isAdmin={isAdmin} onSelectNotif={onSelectNotif} onResendNotif={onResendNotif} last={i === items.length - 1} />
    ))}
  </div>
);

const NotifRow = ({ notif, isAdmin, onSelectNotif, onResendNotif, last }) => {
  const swatch = notif.item ? window.DW_DATA.COLORS.find(c => c.name === notif.item.color) : null;

  if (notif.type === 'system') {
    return (
      <div style={{
        display: 'flex', gap: 12,
        padding: '14px 18px',
        borderBottom: last ? 'none' : '1px solid var(--line)',
        background: notif.read ? 'transparent' : 'var(--orange-soft)',
        cursor: 'default',
      }}>
        <div style={{
          width: 36, height: 36, borderRadius: 8,
          background: 'var(--bg)',
          border: '1px solid var(--line-2)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          color: 'var(--ink-2)',
          flexShrink: 0,
        }}>
          <Icon name="bell" size={16} />
        </div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 13.5, fontWeight: 500, color: 'var(--ink)' }}>{notif.title}</div>
          <div style={{ fontSize: 12.5, color: 'var(--ink-2)', marginTop: 2 }}>{notif.body}</div>
          <div style={{ fontSize: 11, color: 'var(--ink-3)', fontFamily: 'var(--font-mono)', marginTop: 6 }}>
            {timeAgo(notif.ago)}
          </div>
        </div>
      </div>
    );
  }

  const action = notif.type === 'price-drop' ? 'Price dropped' : 'Match';
  return (
    <div
      onClick={() => onSelectNotif && onSelectNotif(notif)}
      style={{
        display: 'flex', gap: 12,
        padding: '14px 18px',
        borderBottom: last ? 'none' : '1px solid var(--line)',
        background: notif.read ? 'transparent' : 'var(--orange-soft)',
        cursor: 'pointer',
        position: 'relative',
        transition: 'background 0.12s ease',
      }}
      onMouseEnter={(e) => { if (notif.read) e.currentTarget.style.background = 'var(--bg)'; }}
      onMouseLeave={(e) => { if (notif.read) e.currentTarget.style.background = 'transparent'; }}
    >
      {!notif.read && (
        <span style={{
          position: 'absolute',
          top: 18, right: 14,
          width: 7, height: 7, borderRadius: '50%',
          background: 'var(--orange)',
        }} />
      )}
      <div style={{
        width: 36, height: 36, borderRadius: 8,
        background: swatch ? swatch.swatch : '#ccc',
        boxShadow: 'inset 0 0 0 1px rgba(0,0,0,0.1)',
        flexShrink: 0,
        position: 'relative',
      }}>
        <div style={{
          position: 'absolute',
          top: 5, left: '50%', transform: 'translateX(-50%)',
          width: 16, height: 2.5,
          background: notif.item.hw === 'Gold' ? '#c9a45a' : notif.item.hw === 'Rose Gold' ? '#d99c8e' : '#cfd2d4',
          borderRadius: 1,
        }} />
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap',
        }}>
          <span style={{
            fontSize: 10.5, fontWeight: 600,
            letterSpacing: '0.08em',
            color: notif.priority ? 'var(--orange-2)' : 'var(--ink-3)',
            textTransform: 'uppercase',
            fontFamily: 'var(--font-mono)',
          }}>{notif.priority ? '★ PRIORITY · ' : ''}{action}</span>
        </div>
        <div style={{ fontSize: 13.5, color: 'var(--ink)', marginTop: 2, lineHeight: 1.35 }}>
          <span style={{ fontFamily: 'var(--font-serif)', fontSize: 15 }}>{notif.item.model} {notif.item.size}</span>
          {' '}— {notif.item.color}
        </div>
        <div style={{
          fontSize: 10.5,
          color: 'var(--ink-3)',
          fontFamily: 'var(--font-mono)',
          marginTop: 4,
          lineHeight: 1.35,
        }}>
          {isAdmin ? `${notif.ownerName || notif.ownerEmail || 'Unknown user'} · ` : ''}
          {notif.alertName ? `Alert: ${notif.alertName}` : 'Alert match'}
        </div>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 8,
          fontSize: 11.5, color: 'var(--ink-3)', marginTop: 6,
        }}>
          <RegionBadge code={notif.item.region} />
          <span className="num" style={{ fontFamily: 'var(--font-mono)' }}>
            {formatMoney(notif.item.price, notif.item.currency, '—')}
          </span>
          <span style={{ color: 'var(--line-2)' }}>·</span>
          <span style={{ fontFamily: 'var(--font-mono)' }}>{timeAgo(notif.ago)}</span>
          {isAdmin && (
            <>
              <span style={{ color: 'var(--line-2)' }}>·</span>
              <button
                type="button"
                onClick={(e) => {
                  e.stopPropagation();
                  onResendNotif && onResendNotif(notif);
                }}
                style={{
                  border: '1px solid var(--line-2)',
                  background: 'var(--bg-elev)',
                  color: 'var(--ink-2)',
                  borderRadius: 999,
                  padding: '2px 8px',
                  fontSize: 10.5,
                  fontWeight: 600,
                  cursor: 'pointer',
                }}
                title="Resend this notification to Telegram"
              >
                Resend TG
              </button>
            </>
          )}
        </div>
      </div>
    </div>
  );
};

// ====================================================================
// BELL BUTTON
// ====================================================================
const BellButton = ({ unreadCount, onClick, getRef }) => {
  const ref = React.useRef(null);
  return (
    <button
      ref={(el) => { ref.current = el; if (getRef) getRef(el); }}
      onClick={onClick}
      style={{
        position: 'relative',
        width: 38, height: 38,
        background: 'var(--bg-elev)',
        border: '1px solid var(--line-2)',
        borderRadius: 999,
        cursor: 'pointer',
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        color: 'var(--ink)',
        transition: 'background 0.12s ease',
      }}
      onMouseEnter={(e) => e.currentTarget.style.background = 'var(--bg)'}
      onMouseLeave={(e) => e.currentTarget.style.background = 'var(--bg-elev)'}
      aria-label="Inbox"
    >
      <Icon name="bell" size={17} />
      {unreadCount > 0 && (
        <span style={{
          position: 'absolute',
          top: -3, right: -3,
          minWidth: 18, height: 18,
          padding: '0 5px',
          fontSize: 10.5, fontWeight: 600,
          background: 'var(--orange)',
          color: '#fff',
          borderRadius: 999,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontFamily: 'var(--font-mono)',
          boxShadow: '0 0 0 2px var(--bg)',
        }}>{unreadCount > 9 ? '9+' : unreadCount}</span>
      )}
    </button>
  );
};

Object.assign(window, { Onboarding, NotificationInbox, BellButton });
