// Cavivo workspace dashboard

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "greetingStyle": "serif-italic",
  "showAmbientBlobs": true,
  "starterDensity": "regular",
  "inputGlow": true
} /*EDITMODE-END*/;

// ─── Icons (line-style, 20×20) ──────────────────────────────────────────────
const Icon = ({ d, size = 20, stroke = 1.6, fill = "none" }) =>
<svg width={size} height={size} viewBox="0 0 24 24" fill={fill} stroke="currentColor"
strokeWidth={stroke} strokeLinecap="round" strokeLinejoin="round">{d}</svg>;


const I = {
  home: <Icon d={<><path d="M3 11.5 12 4l9 7.5" /><path d="M5 10v10h14V10" /></>} />,
  work: <Icon d={<><rect x="3" y="5" width="18" height="15" rx="2" /><path d="M3 9h18" /><path d="M8 3v4M16 3v4" /></>} />,
  spark: <Icon d={<><path d="M12 3v4M12 17v4M3 12h4M17 12h4M5.6 5.6l2.8 2.8M15.6 15.6l2.8 2.8M5.6 18.4l2.8-2.8M15.6 8.4l2.8-2.8" /></>} />,
  bulb: <Icon d={<><path d="M9 18h6M10 21h4" /><path d="M12 3a6 6 0 0 0-4 10.5c.7.7 1 1.5 1 2.5h6c0-1 .3-1.8 1-2.5A6 6 0 0 0 12 3Z" /></>} />,
  search: <Icon d={<><circle cx="11" cy="11" r="7" /><path d="m20 20-3.5-3.5" /></>} />,
  bell: <Icon d={<><path d="M6 16V11a6 6 0 1 1 12 0v5l1.5 2h-15Z" /><path d="M10 20a2 2 0 0 0 4 0" /></>} />,
  invite: <Icon d={<><circle cx="9" cy="9" r="3.5" /><path d="M3 19c0-3 2.5-5 6-5s6 2 6 5" /><path d="M18 8v4M16 10h4" /></>} />,
  help: <Icon d={<><circle cx="12" cy="12" r="9" /><path d="M9.5 9.5a2.5 2.5 0 1 1 3.5 2.3c-.8.4-1 1-1 1.7" /><circle cx="12" cy="17" r=".6" fill="currentColor" /></>} />,
  grid: <Icon d={<><rect x="4" y="4" width="6" height="6" rx="1.2" /><rect x="14" y="4" width="6" height="6" rx="1.2" /><rect x="4" y="14" width="6" height="6" rx="1.2" /><rect x="14" y="14" width="6" height="6" rx="1.2" /></>} />,
  discover: <Icon d={<><circle cx="12" cy="12" r="9" /><path d="M12 3v2M12 19v2M3 12h2M19 12h2" /><path d="m12 8 2 4 4 1-3 3 1 4-4-2-4 2-1-4Z" /></>} />,
  mic: <Icon d={<><rect x="9" y="3" width="6" height="11" rx="3" /><path d="M5 11a7 7 0 0 0 14 0M12 18v3" /></>} />,
  send: <Icon d={<><path d="m4 12 16-8-6 16-2.5-6.5L4 12Z" /></>} />,
  attach: <Icon d={<><path d="M21 11.5 12.5 20a5 5 0 1 1-7-7l9-9a3.5 3.5 0 1 1 5 5l-8.5 8.5a2 2 0 0 1-3-3l8-8" /></>} />,
  emoji: <Icon d={<><circle cx="12" cy="12" r="9" /><path d="M8 14c1 1.5 2.5 2.2 4 2.2s3-.7 4-2.2" /><circle cx="9" cy="10" r=".7" fill="currentColor" /><circle cx="15" cy="10" r=".7" fill="currentColor" /></>} />,
  sliders: <Icon d={<><path d="M4 6h10M18 6h2M4 12h2M10 12h10M4 18h12M20 18h0M16 18h4"/><circle cx="16" cy="6" r="2" fill="#fff"/><circle cx="8" cy="12" r="2" fill="#fff"/><circle cx="14" cy="18" r="2" fill="#fff"/></>} stroke={1.8}/>,
  app: <Icon d={<><rect x="3" y="3" width="8" height="8" rx="1.5" /><rect x="13" y="3" width="8" height="8" rx="1.5" /><rect x="3" y="13" width="8" height="8" rx="1.5" /><circle cx="17" cy="17" r="4" /></>} />,
  chevDown: <Icon d={<><path d="m6 9 6 6 6-6" /></>} />,
  chevUpDown: <Icon d={<><path d="m8 9 4-4 4 4M8 15l4 4 4-4" /></>} size={14} />,
  plus: <Icon d={<><path d="M12 5v14M5 12h14" /></>} />,
  // Tile icons (filled-on-color, white stroke)
  board: <Icon d={<><rect x="3" y="5" width="18" height="14" rx="2.5" /><path d="M8 5v14M3 11h5" /></>} stroke={1.8} />,
  doc: <Icon d={<><path d="M6 3h8l4 4v14H6Z" /><path d="M14 3v4h4M9 12h6M9 16h4" /></>} stroke={1.8} />,
  zoom: <Icon d={<><circle cx="11" cy="11" r="6" /><path d="m20 20-4-4M11 8v6M8 11h6" /></>} stroke={1.8} />,
  chart: <Icon d={<><rect x="3" y="3" width="18" height="18" rx="2.5" /><path d="M8 16v-4M12 16V8M16 16v-7" /></>} stroke={1.8} />,
  idea: <Icon d={<><path d="M9 18h6M10 21h4" /><path d="M12 3a6 6 0 0 0-4 10.5c.7.7 1 1.5 1 2.5h6c0-1 .3-1.8 1-2.5A6 6 0 0 0 12 3Z" /></>} stroke={1.8} />,
  image: <Icon d={<><rect x="3" y="4" width="18" height="16" rx="2.5" /><circle cx="9" cy="10" r="1.6" /><path d="m3 17 5-5 4 4 3-3 6 6" /></>} stroke={1.8} />,
  wave: <Icon d={<><path d="M3 12c2 0 2-4 4-4s2 8 4 8 2-12 4-12 2 8 4 8 2-4 4-4" /></>} stroke={1.8} />,
  book: <Icon d={<><path d="M4 4h6a4 4 0 0 1 4 4v13" /><path d="M20 4h-6a4 4 0 0 0-4 4v13" /><path d="M4 4v15h6M20 4v15h-6" /></>} stroke={1.8} />,
  phone: <Icon d={<><rect x="7" y="3" width="10" height="18" rx="2" /><path d="M10 18h4" /></>} size={18} />,
  pin: <Icon d={<><path d="M12 21s-6-5.3-6-10a6 6 0 1 1 12 0c0 4.7-6 10-6 10Z" /><circle cx="12" cy="11" r="2" /></>} size={18} />,
  heart: <Icon d={<><path d="M12 20s-7-4.4-7-10a4 4 0 0 1 7-2.7A4 4 0 0 1 19 10c0 5.6-7 10-7 10Z" /></>} size={18} />,
  edit: <Icon d={<><path d="M4 20h4l11-11-4-4L4 16Z" /></>} size={18} />,
  list: <Icon d={<><path d="M4 7h16M4 12h10M4 17h16" /></>} size={18} />,
  sparkle: <Icon d={<><path d="M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8Z" /></>} stroke={1.5} />,
  megaphone: <Icon d={<><path d="M4 10v4h4l7 5V5L8 10H4Z" /><path d="M16 8.5a4.5 4.5 0 0 1 0 7" /><path d="M18 6v12" /></>} stroke={1.8} />,
  target: <Icon d={<><circle cx="12" cy="12" r="9" /><circle cx="12" cy="12" r="3" /><path d="M12 3v2M12 19v2M3 12h2M21 12h-2" /></>} stroke={1.8} />,
  user: <Icon d={<><circle cx="12" cy="8" r="3.5" /><path d="M5 20c0-4 3-6 7-6s7 2 7 6" /></>} stroke={1.8} />,
  diamond: <Icon d={<><path d="M12 3 4 11l8 10 8-10Z" /><path d="M4 11h16M9 11l3-8M15 11l-3-8" /></>} size={16} stroke={1.4} />,
  check: <Icon d={<><path d="m5 12 5 5 10-11" /></>} stroke={2.2} />,
  arrow: <Icon d={<><path d="M5 12h14M13 6l6 6-6 6" /></>} />,
};

// ─── Logo ───────────────────────────────────────────────────────────────────
const CavivoLogo = ({ size = 32 }) =>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
    <svg width={size} height={size} viewBox="0 0 40 40">
      <defs>
        <linearGradient id="cl" x1="0" x2="1" y1="0" y2="1">
          <stop offset="0" stopColor="#C8B6FF" />
          <stop offset=".5" stopColor="#F5B8D0" />
          <stop offset="1" stopColor="#FFD4A8" />
        </linearGradient>
      </defs>
      <path d="M20 4c8.8 0 16 7.2 16 16 0 4-3 5-7 5s-5-2-9-2-5 4-9 4-7-2-7-7c0-8.8 7.2-16 16-16Z" fill="url(#cl)" />
      <circle cx="14" cy="18" r="2.4" fill="#1c1c1c" />
      <circle cx="24" cy="18" r="2.4" fill="#1c1c1c" />
    </svg>
    <div style={{ lineHeight: 1 }}>
      <div style={{ fontFamily: '"Instrument Serif", serif', fontSize: 22, fontStyle: 'italic', color: '#1c1c1c', letterSpacing: '-.01em' }}>Kimitai</div>
      <div style={{ fontSize: 9.5, color: '#9089A0', letterSpacing: '.14em', textTransform: 'uppercase', marginTop: 2, fontWeight: 500 }}>AI video studio</div>
    </div>
  </div>;


// ─── Sidebar ────────────────────────────────────────────────────────────────
const SidebarItem = ({ icon, label, active, onClick, badge }) =>
<button className="dash-sidebar-item" onClick={onClick}
style={{ display: 'flex', alignItems: 'center', gap: 12, width: '100%', padding: '10px 12px',
  borderRadius: 10, border: 'none', cursor: 'pointer',
  background: active ? '#FFFFFF' : 'transparent',
  boxShadow: active ? '0 1px 2px rgba(28,28,28,.06), 0 0 0 1px #E0DAE8' : 'none',
  color: active ? '#1c1c1c' : '#4a4356',
  fontSize: 13.5, fontWeight: active ? 600 : 500, fontFamily: 'inherit',
  transition: 'background .15s' }}
onMouseEnter={(e) => {if (!active) e.currentTarget.style.background = 'rgba(255,255,255,.55)';}}
onMouseLeave={(e) => {if (!active) e.currentTarget.style.background = 'transparent';}}>
    <span style={{ color: active ? '#7A5AE0' : '#6b6377', display: 'flex' }}>{icon}</span>
    <span style={{ flex: 1, textAlign: 'left' }}>{label}</span>
    {badge && <span style={{ fontSize: 10, fontWeight: 600, color: '#7A5AE0', background: '#E8DFFA', padding: '2px 7px', borderRadius: 999 }}>{badge}</span>}
  </button>;


const SectionLabel = ({ children }) =>
<div style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '14px 12px 6px', color: '#9089A0', fontSize: 11, fontWeight: 600, letterSpacing: '.08em', textTransform: 'uppercase' }}>
    <span>{children}</span>
    <span style={{ color: '#C9C2D4' }}>{I.chevDown}</span>
  </div>;


const Sidebar = ({ active, setActive }) =>
<aside className="dash-sidebar" style={{ width: 264, flexShrink: 0, background: '#F7F2FB', borderRight: '1px solid #E0DAE8',
  display: 'flex', flexDirection: 'column', padding: '18px 14px 14px' }}>
    <div className="dash-sidebar-brand" style={{ padding: '2px 4px 14px', borderBottom: '1px solid #E0DAE8', marginBottom: 14 }}>
      <CavivoLogo />
    </div>

    <SidebarItem icon={I.home} label="Home" active={active === 'home'} onClick={() => setActive('home')} />
    <SidebarItem icon={I.work} label="My videos" active={active === 'work'} onClick={() => setActive('work')} />
    <SidebarItem icon={I.discover} label="Discover" active={active === 'discover'} onClick={() => setActive('discover')} />
  </aside>;


// ─── Top bar ────────────────────────────────────────────────────────────────
const IconBtn = ({ children, onClick, title }) =>
<button onClick={onClick} title={title}
style={{ width: 38, height: 38, borderRadius: 10, border: '1px solid #E0DAE8',
  background: '#FFFFFF', cursor: 'pointer', color: '#4a4356',
  display: 'flex', alignItems: 'center', justifyContent: 'center',
  transition: 'transform .12s, background .12s' }}
onMouseEnter={(e) => {e.currentTarget.style.background = '#FAF7FE';}}
onMouseLeave={(e) => {e.currentTarget.style.background = '#FFFFFF';}}>
    {children}
  </button>;


// ─── Credit packs (creditsPacks.js — same as app paywall) ─────────────────────
const CREDIT_PACKS = (typeof window !== 'undefined' && window.KimitaiCredits?.CREDIT_PACKS) || [];

function packStripeUrl(packId) {
  const cfg = getLandingConfig();
  return cfg?.stripeUrls?.[packId] || '';
}

const PlansPackCard = ({ pack, onBuy, buying }) => {
  const [hover, setHover] = React.useState(false);
  const stripeUrl = packStripeUrl(pack.id);
  const canBuy = Boolean(onBuy || stripeUrl);
  const border = pack.popular ? '2px solid #7A5AE0' : '1px solid #E0DAE8';
  const bg = pack.popular ? 'linear-gradient(180deg,#FAF7FE 0%,#FFFFFF 100%)' : '#FFFFFF';
  const buyLabel = buying ? 'Opening checkout…' : `Buy ${pack.label}`;
  const btnStyle = {
    display: 'inline-flex',
    alignItems: 'center',
    justifyContent: 'center',
    gap: 8,
    width: '100%',
    marginTop: 18,
    padding: '12px 18px',
    borderRadius: 999,
    border: 'none',
    cursor: canBuy && !buying ? 'pointer' : 'default',
    fontFamily: 'inherit',
    fontWeight: 600,
    fontSize: 13,
    color: '#1c1c1c',
    textDecoration: 'none',
    background: pack.popular
      ? 'linear-gradient(120deg,#C8B6FF 0%,#F5B8D0 50%,#C8B6FF 100%)'
      : '#FFFFFF',
    boxShadow: pack.popular ? '0 6px 18px rgba(122,90,224,.22)' : 'none',
    borderWidth: pack.popular ? 0 : 1,
    borderStyle: 'solid',
    borderColor: '#E0DAE8',
    opacity: hover && canBuy && !buying ? 0.92 : 1,
    transition: 'opacity .15s, transform .15s',
    transform: hover && canBuy && !buying ? 'translateY(-1px)' : 'translateY(0)',
  };
  const buyHandler = (e) => {
    if (!canBuy || buying) {
      e.preventDefault();
      return;
    }
    if (onBuy) {
      e.preventDefault();
      onBuy(pack.id);
    }
  };
  const BuyBtn = canBuy ? (
    stripeUrl && !onBuy ? (
      <a href={stripeUrl} target="_blank" rel="noopener noreferrer" style={btnStyle}
        onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}>
        {buyLabel} <span style={{ display: 'flex' }}>{I.arrow}</span>
      </a>
    ) : (
      <button type="button" disabled={buying} onClick={buyHandler} style={btnStyle}
        onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}>
        {buyLabel} <span style={{ display: 'flex' }}>{I.arrow}</span>
      </button>
    )
  ) : (
    <button type="button" disabled style={{ ...btnStyle, opacity: 0.55 }} title="Checkout unavailable">
      {buyLabel}
    </button>
  );
  return (
    <div style={{
      position: 'relative',
      padding: '22px 20px 20px',
      background: bg,
      border,
      borderRadius: 18,
      boxShadow: pack.popular ? '0 12px 32px rgba(122,90,224,.12)' : '0 1px 2px rgba(28,28,28,.04)',
      display: 'flex',
      flexDirection: 'column',
      height: '100%',
    }}>
      {pack.popular ? (
        <div style={{
          position: 'absolute', top: 12, right: 12, padding: '4px 9px',
          background: '#F5B8D0', color: '#1c1c1c', fontSize: 9, fontWeight: 700,
          letterSpacing: '.06em', borderRadius: 8,
        }}>POPULAR</div>
      ) : null}
      <div style={{ fontSize: 17, fontWeight: 600, color: '#1c1c1c' }}>{pack.label}</div>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 6, marginTop: 10 }}>
        <span style={{ fontSize: 30, fontWeight: 600, letterSpacing: '-.02em' }}>{pack.price}</span>
        <span style={{ fontSize: 12, color: '#6b6377' }}>one-time</span>
      </div>
      <div style={{ marginTop: 8, fontSize: 13, fontWeight: 600, color: '#7A5AE0' }}>
        {pack.credits.toLocaleString()} credits
      </div>
      {pack.savePct ? (
        <div style={{ marginTop: 4, fontSize: 11, fontWeight: 600, color: '#C46A20' }}>Save {pack.savePct}%</div>
      ) : null}
      <ul style={{ margin: '14px 0 0', padding: 0, listStyle: 'none', display: 'flex', flexDirection: 'column', gap: 7, flex: 1 }}>
        {pack.perks.map((perk, i) => (
          <li key={i} style={{ display: 'flex', gap: 7, fontSize: 11.5, lineHeight: 1.4, color: '#4a4356' }}>
            <span style={{ color: '#7A5AE0', display: 'flex', flexShrink: 0, marginTop: 1 }}>{I.check}</span>
            {perk}
          </li>
        ))}
      </ul>
      {BuyBtn}
    </div>
  );
};

// ─── Supabase (dashboard session + edge functions) ─────────────────────────
let dashboardSupabase = null;

const getLandingConfig = () =>
  (typeof window !== 'undefined' && window.LANDING_CONFIG) ? window.LANDING_CONFIG : null;

const getDashboardSupabase = () => {
  const cfg = getLandingConfig();
  if (!cfg?.supabaseUrl || !cfg?.supabaseAnonKey) return null;
  if (!dashboardSupabase && typeof window !== 'undefined' && window.supabase?.createClient) {
    dashboardSupabase = window.supabase.createClient(cfg.supabaseUrl, cfg.supabaseAnonKey, {
      auth: {
        persistSession: true,
        autoRefreshToken: true,
        detectSessionInUrl: true,
        storage: window.localStorage,
      },
    });
  }
  return dashboardSupabase;
};

function useDashboardAuth() {
  const [session, setSession] = React.useState(null);
  const [authLoading, setAuthLoading] = React.useState(true);
  const [authBootError, setAuthBootError] = React.useState('');

  React.useEffect(() => {
    const sb = getDashboardSupabase();
    if (!sb) {
      setAuthLoading(false);
      setAuthBootError('Add config.js with your Supabase URL and anon key.');
      return undefined;
    }

    let cancelled = false;
    (async () => {
      try {
        const { data: { session: s }, error } = await sb.auth.getSession();
        if (error) throw error;
        if (!cancelled) {
          setSession(s);
          setAuthBootError('');
        }
      } catch (e) {
        if (!cancelled) setAuthBootError(e.message || 'Could not restore session');
      } finally {
        if (!cancelled) setAuthLoading(false);
      }
    })();

    const { data: { subscription } } = sb.auth.onAuthStateChange((_event, s) => {
      setSession(s);
    });
    return () => subscription.unsubscribe();
  }, []);

  const signOut = async () => {
    const sb = getDashboardSupabase();
    if (sb) await sb.auth.signOut();
  };

  return { session, user: session?.user ?? null, authLoading, authBootError, signOut };
}

const SignInScreen = ({ bootError }) => {
  const [email, setEmail] = React.useState('');
  const [code, setCode] = React.useState('');
  const [step, setStep] = React.useState('form');
  const [loading, setLoading] = React.useState(false);
  const [error, setError] = React.useState('');

  const trimmedEmail = email.trim().toLowerCase();
  const codeReady = code.replace(/\D/g, '').length >= 6;

  const friendlyAuthError = (msg) => {
    if (msg.includes('rate limit')) return 'Too many attempts. Please wait a moment.';
    if (msg.includes('expired') || msg.includes('invalid')) return 'That code is invalid or expired. Request a new email.';
    return msg;
  };

  const handleSendCode = async () => {
    if (!trimmedEmail.includes('@') || !trimmedEmail.includes('.')) {
      setError('Please enter a valid email address.');
      return;
    }
    const sb = getDashboardSupabase();
    if (!sb) {
      setError('Supabase is not configured.');
      return;
    }
    setLoading(true);
    setError('');
    try {
      const { error: otpError } = await sb.auth.signInWithOtp({
        email: trimmedEmail,
        options: { shouldCreateUser: true },
      });
      if (otpError) throw otpError;
      setCode('');
      setStep('check-email');
    } catch (e) {
      setError(friendlyAuthError(e.message || 'Could not send code'));
    } finally {
      setLoading(false);
    }
  };

  const handleVerifyCode = async () => {
    const token = code.replace(/\D/g, '');
    if (token.length < 6) {
      setError('Enter the 6–8 digit code from your email.');
      return;
    }
    const sb = getDashboardSupabase();
    if (!sb) {
      setError('Supabase is not configured.');
      return;
    }
    setLoading(true);
    setError('');
    try {
      let { error: verifyError } = await sb.auth.verifyOtp({
        email: trimmedEmail,
        token,
        type: 'email',
      });
      if (verifyError) {
        const fallback = await sb.auth.verifyOtp({
          email: trimmedEmail,
          token,
          type: 'magiclink',
        });
        verifyError = fallback.error;
      }
      if (verifyError) throw verifyError;
    } catch (e) {
      setError(friendlyAuthError(e.message || 'Could not verify code'));
    } finally {
      setLoading(false);
    }
  };

  const handleResend = async () => {
    const sb = getDashboardSupabase();
    if (!sb) return;
    setLoading(true);
    setError('');
    try {
      const { error: otpError } = await sb.auth.signInWithOtp({
        email: trimmedEmail,
        options: { shouldCreateUser: true },
      });
      if (otpError) throw otpError;
      setCode('');
    } catch (e) {
      setError(friendlyAuthError(e.message || 'Could not resend'));
    } finally {
      setLoading(false);
    }
  };

  return (
    <div style={{
      minHeight: '100vh', background: '#FAF7FE',
      fontFamily: '"Inter","SF Pro Text",ui-sans-serif,system-ui,sans-serif',
      color: '#1c1c1c', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 32,
    }}>
      <div style={{ position: 'absolute', top: -120, right: -80, width: 380, height: 380, borderRadius: '50%',
        background: 'radial-gradient(circle,#E8DFFA 0%,transparent 65%)', pointerEvents: 'none' }} />
      <div style={{ position: 'absolute', bottom: -80, left: -40, width: 320, height: 320, borderRadius: '50%',
        background: 'radial-gradient(circle,#FFE8DC 0%,transparent 65%)', pointerEvents: 'none' }} />

      <div style={{
        position: 'relative', width: 'min(420px, 100%)', padding: '36px 32px',
        background: '#FFFFFF', border: '1px solid #E0DAE8', borderRadius: 22,
        boxShadow: '0 24px 48px rgba(122,90,224,.12)',
      }}>
        <div style={{ marginBottom: 24 }}>
          <CavivoLogo size={36} />
        </div>

        {step === 'form' ? (
          <>
            <h1 style={{ margin: 0, fontSize: 28, fontWeight: 500, letterSpacing: '-.02em',
              fontFamily: '"Instrument Serif", serif', fontStyle: 'italic' }}>
              Sign in to your studio
            </h1>
            <p style={{ margin: '10px 0 0', fontSize: 14, color: '#6b6377', lineHeight: 1.5 }}>
              Use the same email as the Kimitai app. We&apos;ll email you a sign-in code — no password.
            </p>

            {bootError ? (
              <p style={{ margin: '14px 0 0', fontSize: 12, color: '#B42318' }}>{bootError}</p>
            ) : null}
            {error ? (
              <p style={{ margin: '10px 0 0', fontSize: 12, color: '#B42318' }}>{error}</p>
            ) : null}

            <label style={{ display: 'block', marginTop: 22, fontSize: 12, fontWeight: 600, color: '#4a4356' }}>
              Email
            </label>
            <input type="email" value={email} onChange={(e) => setEmail(e.target.value)}
              onKeyDown={(e) => { if (e.key === 'Enter') handleSendCode(); }}
              placeholder="you@example.com" autoComplete="email"
              style={{
                width: '100%', marginTop: 8, padding: '12px 14px', borderRadius: 12,
                border: '1px solid #E0DAE8', fontFamily: 'inherit', fontSize: 15,
                boxSizing: 'border-box',
              }} />

            <button type="button" disabled={loading} onClick={handleSendCode}
              style={{
                width: '100%', marginTop: 16, padding: '13px 18px', borderRadius: 999, border: 'none',
                cursor: loading ? 'wait' : 'pointer', fontFamily: 'inherit', fontSize: 14, fontWeight: 600,
                color: '#1c1c1c',
                background: 'linear-gradient(120deg,#C8B6FF 0%,#F5B8D0 50%,#C8B6FF 100%)',
                boxShadow: '0 8px 20px rgba(122,90,224,.25)',
                opacity: loading ? 0.7 : 1,
              }}>
              {loading ? 'Sending…' : 'Send code'}
            </button>
          </>
        ) : (
          <>
            <h1 style={{ margin: 0, fontSize: 26, fontWeight: 500,
              fontFamily: '"Instrument Serif", serif', fontStyle: 'italic' }}>
              Check your email
            </h1>
            <p style={{ margin: '12px 0 0', fontSize: 14, color: '#4a4356', lineHeight: 1.55 }}>
              We sent a sign-in code to <strong>{trimmedEmail}</strong>.
              Enter it below to open your studio.
            </p>

            {error ? (
              <p style={{ margin: '12px 0 0', fontSize: 12, color: '#B42318' }}>{error}</p>
            ) : null}

            <label style={{ display: 'block', marginTop: 18, fontSize: 12, fontWeight: 600, color: '#4a4356' }}>
              Sign-in code
            </label>
            <input
              type="text"
              inputMode="numeric"
              autoComplete="one-time-code"
              value={code}
              onChange={(e) => setCode(e.target.value.replace(/[^0-9]/g, '').slice(0, 8))}
              onKeyDown={(e) => { if (e.key === 'Enter' && codeReady && !loading) handleVerifyCode(); }}
              placeholder="00000000"
              maxLength={8}
              style={{
                width: '100%', marginTop: 8, padding: '14px 16px', borderRadius: 12,
                border: '1px solid #E0DAE8', fontFamily: 'inherit', fontSize: 22, fontWeight: 600,
                letterSpacing: '0.35em', textAlign: 'center', boxSizing: 'border-box',
              }}
            />

            <button type="button" disabled={loading || !codeReady} onClick={handleVerifyCode}
              style={{
                width: '100%', marginTop: 14, padding: '13px 18px', borderRadius: 999, border: 'none',
                cursor: loading || !codeReady ? 'not-allowed' : 'pointer',
                fontFamily: 'inherit', fontSize: 14, fontWeight: 600, color: '#1c1c1c',
                background: 'linear-gradient(120deg,#C8B6FF 0%,#F5B8D0 50%,#C8B6FF 100%)',
                boxShadow: '0 8px 20px rgba(122,90,224,.25)',
                opacity: loading || !codeReady ? 0.55 : 1,
              }}>
              {loading ? 'Verifying…' : 'Sign in with code'}
            </button>

            <button type="button" disabled={loading} onClick={handleResend}
              style={{
                width: '100%', marginTop: 10, padding: '10px 14px', borderRadius: 999,
                border: '1px solid #E0DAE8', background: '#FFFFFF', cursor: loading ? 'wait' : 'pointer',
                fontFamily: 'inherit', fontSize: 13, fontWeight: 500, color: '#4a4356',
              }}>
              Resend email
            </button>

            <button type="button" onClick={() => { setStep('form'); setError(''); setCode(''); }}
              style={{
                marginTop: 16, padding: 0, border: 'none', background: 'transparent',
                color: '#7A5AE0', fontFamily: 'inherit', fontSize: 13, fontWeight: 600, cursor: 'pointer',
              }}>
              Use a different email
            </button>
          </>
        )}

        <p style={{ marginTop: 24, fontSize: 12, color: '#9089A0', textAlign: 'center' }}>
          <a href="index.html" style={{ color: '#7A5AE0', textDecoration: 'none' }}>← Back to Kimitai</a>
        </p>
      </div>
    </div>
  );
};

async function invokeAuthedFunction(name, body = {}) {
  const sb = getDashboardSupabase();
  const cfg = getLandingConfig();
  if (!sb || !cfg) throw new Error('Supabase is not configured');
  const { data: { session } } = await sb.auth.getSession();
  if (!session?.access_token) throw new Error('Not signed in');
  const res = await fetch(`${cfg.supabaseUrl}/functions/v1/${name}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      apikey: cfg.supabaseAnonKey,
      Authorization: `Bearer ${session.access_token}`,
    },
    body: JSON.stringify(body),
  });
  const json = await res.json().catch(() => ({}));
  if (!res.ok) {
    const err = new Error(json.error || res.statusText || 'Request failed');
    err.status = res.status;
    err.payload = json;
    throw err;
  }
  return json;
}

function dashboardCheckoutUrls() {
  if (typeof window === 'undefined') return { successUrl: '', cancelUrl: '' };
  const base = `${window.location.origin}${window.location.pathname}`;
  return {
    successUrl: `${base}?purchase=success`,
    cancelUrl: `${base}?purchase=cancelled`,
  };
}

async function startPackCheckout(packId) {
  const urls = dashboardCheckoutUrls();
  const data = await invokeAuthedFunction('start-stripe-checkout', {
    packId,
    successUrl: urls.successUrl,
    cancelUrl: urls.cancelUrl,
  });
  if (!data.url) throw new Error('Checkout URL missing');
  window.location.href = data.url;
}

function useDashboardCredits() {
  const [balance, setBalance] = React.useState(null);
  const [tier, setTier] = React.useState('free');
  const [loading, setLoading] = React.useState(true);

  const refresh = React.useCallback(async () => {
    const sb = getDashboardSupabase();
    if (!sb) {
      setBalance(null);
      setLoading(false);
      return;
    }
    const { data: { session } } = await sb.auth.getSession();
    if (!session) {
      setBalance(null);
      setLoading(false);
      return;
    }
    setLoading(true);
    try {
      const data = await invokeAuthedFunction('get-credits', {});
      setBalance(typeof data.balance === 'number' ? data.balance : 0);
      setTier(data.tier || 'free');
    } catch {
      setBalance(0);
    } finally {
      setLoading(false);
    }
  }, []);

  React.useEffect(() => {
    refresh();
    const sb = getDashboardSupabase();
    if (!sb) return undefined;
    const { data: { subscription } } = sb.auth.onAuthStateChange(() => {
      refresh();
    });
    return () => subscription.unsubscribe();
  }, [refresh]);

  return { balance, tier, loading, refresh, setBalance };
}

const avatarInitial = (user) => {
  if (!user) return '?';
  if (user.email) return user.email[0].toUpperCase();
  const name = user.user_metadata?.full_name || user.user_metadata?.name;
  if (typeof name === 'string' && name[0]) return name[0].toUpperCase();
  return 'G';
};

const modalShellStyle = {
  position: 'fixed', inset: 0, zIndex: 2147483647,
  background: 'rgba(60,40,90,.35)',
  backdropFilter: 'blur(8px)', WebkitBackdropFilter: 'blur(8px)',
  display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24,
};

const modalPanelStyle = {
  width: 'min(480px, 100%)', maxHeight: '90vh',
  background: 'linear-gradient(160deg,#F0E5F8 0%,#FCE0EC 45%,#FFE8DC 100%)',
  borderRadius: 22, overflow: 'hidden', display: 'flex', flexDirection: 'column',
  position: 'relative',
  boxShadow: '0 40px 80px -20px rgba(122,90,224,.35), 0 0 0 1px rgba(255,255,255,.6) inset',
  fontFamily: 'inherit', color: '#1c1c1c',
};

const AiProcessingConsentModal = ({ onAgree, onDismiss }) => {
  const sw = window.SeedanceWeb;
  const title = sw?.AI_CONSENT_TITLE ?? 'AI Processing Consent';
  const body = sw?.AI_CONSENT_BODY ?? '';
  const privacyUrl = sw?.AI_CONSENT_PRIVACY_URL ?? 'privacy.html';
  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 20000,
      background: 'rgba(0,0,0,0.72)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24,
    }}>
      <div style={{
        width: 'min(340px, 100%)', background: '#1c1c22', borderRadius: 20,
        padding: '22px 22px 18px', border: '1px solid rgba(122,90,224,0.35)',
        boxShadow: '0 24px 48px rgba(0,0,0,0.45)',
      }}>
        <div style={{ fontSize: 18, fontWeight: 600, color: '#fff', textAlign: 'center', marginBottom: 14 }}>
          {title}
        </div>
        <p style={{ margin: 0, fontSize: 13, lineHeight: 1.55, color: 'rgba(255,255,255,0.82)', textAlign: 'center' }}>
          {body}
        </p>
        <a href={privacyUrl} target="_blank" rel="noopener noreferrer"
          style={{ display: 'block', marginTop: 14, marginBottom: 20, textAlign: 'center',
            fontSize: 14, fontWeight: 500, color: '#B8A4FF', textDecoration: 'none' }}>
          Read Privacy Policy
        </a>
        <div style={{ display: 'flex', gap: 10 }}>
          <button type="button" onClick={onDismiss}
            style={{ flex: 1, padding: '12px 0', borderRadius: 12, border: 'none', cursor: 'pointer',
              background: '#2a2a32', color: '#fff', fontFamily: 'inherit', fontSize: 14, fontWeight: 500 }}>
            Not Now
          </button>
          <button type="button" onClick={onAgree}
            style={{ flex: 1, padding: '12px 0', borderRadius: 12, border: 'none', cursor: 'pointer',
              background: '#7A5AE0', color: '#fff', fontFamily: 'inherit', fontSize: 14, fontWeight: 600 }}>
            I Agree
          </button>
        </div>
      </div>
    </div>
  );
};

const AccountModal = ({ onClose, onSignOut }) => {
  const [user, setUser] = React.useState(null);
  const [billing, setBilling] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [billingError, setBillingError] = React.useState('');
  const [deleteStep, setDeleteStep] = React.useState(false);
  const [deleteConfirm, setDeleteConfirm] = React.useState('');
  const [deleting, setDeleting] = React.useState(false);
  const [deleteError, setDeleteError] = React.useState('');

  React.useEffect(() => {
    const sb = getDashboardSupabase();
    if (!sb) {
      setLoading(false);
      return undefined;
    }
    let cancelled = false;
    const load = async () => {
      const { data: { session } } = await sb.auth.getSession();
      if (cancelled) return;
      setUser(session?.user ?? null);
      if (!session?.user) {
        setLoading(false);
        return;
      }
      try {
        const data = await invokeAuthedFunction('get-account-billing');
        if (!cancelled) {
          setBilling(data);
          setBillingError('');
        }
      } catch (e) {
        if (!cancelled) setBillingError(e.message || 'Could not load billing');
      } finally {
        if (!cancelled) setLoading(false);
      }
    };
    load();
    const { data: sub } = sb.auth.onAuthStateChange((_event, session) => {
      setUser(session?.user ?? null);
    });
    return () => {
      cancelled = true;
      sub?.subscription?.unsubscribe();
    };
  }, []);

  const handleDelete = async () => {
    if (deleteConfirm !== 'DELETE') return;
    setDeleting(true);
    setDeleteError('');
    try {
      await invokeAuthedFunction('delete-account');
      const sb = getDashboardSupabase();
      if (sb) await sb.auth.signOut();
      window.location.href = 'index.html';
    } catch (e) {
      setDeleteError(e.message || 'Could not delete account');
      setDeleting(false);
    }
  };

  const email = billing?.email ?? user?.email ?? null;
  const isGuest = billing?.isAnonymous ?? !user?.email;
  const payment = billing?.payment ?? null;

  return (
    <div style={modalShellStyle} onClick={onClose}>
      <div onClick={(e) => e.stopPropagation()} style={modalPanelStyle}>
        <div style={{ position: 'absolute', top: -80, right: -40, width: 240, height: 240, borderRadius: '50%',
          background: 'radial-gradient(circle,#C8B6FF 0%,transparent 65%)', opacity: .5, pointerEvents: 'none' }} />
        <div style={{ position: 'relative', padding: '20px 24px', display: 'flex', alignItems: 'flex-start',
          justifyContent: 'space-between', borderBottom: '1px solid rgba(28,28,28,.08)' }}>
          <div>
            <div style={{ fontFamily: '"Instrument Serif",serif', fontStyle: 'italic', fontSize: 26,
              letterSpacing: '-.01em' }}>Account</div>
            <div style={{ fontSize: 13, color: '#6b6377', marginTop: 6, lineHeight: 1.5 }}>
              Profile, payment method, and account deletion.
            </div>
          </div>
        <button type="button" onClick={onClose}
          style={{ width: 34, height: 34, borderRadius: 10, border: '1px solid #E0DAE8', background: '#FFFFFF',
            color: '#4a4356', cursor: 'pointer', fontSize: 18, lineHeight: 1 }}>×</button>
      </div>

        <div style={{ position: 'relative', padding: '16px 24px 8px', overflow: 'auto' }}>
          {!getDashboardSupabase() ? (
            <p style={{ fontSize: 13, color: '#6b6377', margin: 0 }}>
              Add <code style={{ fontSize: 12 }}>config.js</code> with Supabase keys to enable account features.
            </p>
          ) : loading ? (
            <p style={{ fontSize: 13, color: '#6b6377', margin: 0 }}>Loading…</p>
          ) : !user ? (
            <p style={{ fontSize: 13, color: '#6b6377', margin: 0 }}>Session expired. Refresh the page to sign in again.</p>
          ) : (
            <>
              <div style={{ display: 'flex', alignItems: 'center', gap: 14, padding: 14, background: '#FFFFFF',
                borderRadius: 14, border: '1px solid #E0DAE8' }}>
                <div style={{ width: 48, height: 48, borderRadius: 12, flexShrink: 0,
                  background: 'linear-gradient(135deg,#FFD4A8 0%,#F5B8D0 50%,#C8B6FF 100%)',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  color: '#fff', fontWeight: 700, fontSize: 18 }}>
                  {avatarInitial(user)}
                </div>
                <div style={{ minWidth: 0 }}>
                  <div style={{ fontSize: 15, fontWeight: 600 }}>{isGuest ? 'Guest account' : (email || 'Account')}</div>
                  <div style={{ fontSize: 12, color: '#6b6377', marginTop: 3 }}>
                    {isGuest ? 'Add an email in the app to save your work across devices.' : email}
                  </div>
                </div>
              </div>

              <div style={{ marginTop: 18, fontSize: 11, fontWeight: 600, letterSpacing: '.06em',
                textTransform: 'uppercase', color: '#9089A0' }}>Payment</div>
              <div style={{ marginTop: 8, padding: 14, background: '#FFFFFF', borderRadius: 14,
                border: '1px solid #E0DAE8' }}>
                {billingError ? (
                  <p style={{ margin: 0, fontSize: 12, color: '#B42318' }}>{billingError}</p>
                ) : payment ? (
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12 }}>
                    <div>
                      <div style={{ fontSize: 14, fontWeight: 600, textTransform: 'capitalize' }}>
                        {payment.brand} •••• {payment.last4}
                      </div>
                      <div style={{ fontSize: 12, color: '#6b6377', marginTop: 4 }}>
                        Expires {String(payment.expMonth).padStart(2, '0')}/{payment.expYear}
                      </div>
                    </div>
                    <span style={{ fontSize: 11, fontWeight: 600, color: '#7A5AE0', background: '#E8DFFA',
                      padding: '4px 10px', borderRadius: 999 }}>Linked</span>
                  </div>
                ) : (
                  <p style={{ margin: 0, fontSize: 13, color: '#4a4356', lineHeight: 1.5 }}>
                    No payment method on file. Purchases in the app will appear here once Stripe is connected.
                  </p>
                )}
              </div>

              <div style={{ marginTop: 18 }}>
                <button type="button"
                  onClick={async () => {
                    if (onSignOut) await onSignOut();
                    onClose();
                  }}
                  style={{
                    width: '100%', padding: '10px 16px', borderRadius: 10, border: '1px solid #E0DAE8',
                    background: '#FFFFFF', fontFamily: 'inherit', fontSize: 13, fontWeight: 500,
                    cursor: 'pointer', color: '#4a4356',
                  }}>
                  Sign out
                </button>
              </div>

              <div style={{ marginTop: 22, paddingTop: 18, borderTop: '1px solid rgba(28,28,28,.08)' }}>
                <div style={{ fontSize: 11, fontWeight: 600, letterSpacing: '.06em',
                  textTransform: 'uppercase', color: '#B42318' }}>Danger zone</div>
                {!deleteStep ? (
                  <>
                    <p style={{ fontSize: 12, color: '#6b6377', margin: '8px 0 12px', lineHeight: 1.5 }}>
                      Permanently delete your account, library videos, and billing link. This cannot be undone.
                    </p>
                    <button type="button" onClick={() => { setDeleteStep(true); setDeleteConfirm(''); setDeleteError(''); }}
                      style={{ padding: '10px 16px', borderRadius: 10, border: '1px solid #FECACA',
                        background: '#FEF2F2', color: '#B42318', fontFamily: 'inherit', fontSize: 13,
                        fontWeight: 600, cursor: 'pointer' }}>
                      Delete account
                    </button>
                  </>
                ) : (
                  <div style={{ marginTop: 10 }}>
                    <p style={{ fontSize: 12, color: '#4a4356', margin: '0 0 10px', lineHeight: 1.5 }}>
                      Type <strong>DELETE</strong> to confirm. All videos and account data will be removed.
                    </p>
                    <input type="text" value={deleteConfirm} onChange={(e) => setDeleteConfirm(e.target.value)}
                      placeholder="DELETE" autoComplete="off"
                      style={{ width: '100%', padding: '10px 12px', borderRadius: 10, border: '1px solid #E0DAE8',
                        fontFamily: 'inherit', fontSize: 13, marginBottom: 10, boxSizing: 'border-box' }} />
                    {deleteError ? (
                      <p style={{ margin: '0 0 10px', fontSize: 12, color: '#B42318' }}>{deleteError}</p>
                    ) : null}
                    <div style={{ display: 'flex', gap: 8 }}>
                      <button type="button" disabled={deleting}
                        onClick={() => { setDeleteStep(false); setDeleteConfirm(''); setDeleteError(''); }}
                        style={{ flex: 1, padding: '10px 14px', borderRadius: 10, border: '1px solid #E0DAE8',
                          background: '#FFFFFF', fontFamily: 'inherit', fontSize: 13, cursor: 'pointer' }}>
                        Cancel
                      </button>
                      <button type="button" disabled={deleting || deleteConfirm !== 'DELETE'}
                        onClick={handleDelete}
                        style={{ flex: 1, padding: '10px 14px', borderRadius: 10, border: 'none',
                          background: deleteConfirm === 'DELETE' ? '#B42318' : '#E0DAE8',
                          color: deleteConfirm === 'DELETE' ? '#fff' : '#9089A0',
                          fontFamily: 'inherit', fontSize: 13, fontWeight: 600,
                          cursor: deleteConfirm === 'DELETE' && !deleting ? 'pointer' : 'not-allowed' }}>
                        {deleting ? 'Deleting…' : 'Delete permanently'}
                      </button>
                    </div>
                  </div>
                )}
              </div>
            </>
          )}
        </div>
      </div>
    </div>
  );
};

const PlansModal = ({ onClose, onBuy, buyingPackId, balance }) => (
  <div style={modalShellStyle} onClick={onClose}>
    <div onClick={(e) => e.stopPropagation()}
      style={{
        ...modalPanelStyle,
        width: 'min(1040px, 100%)',
      }}>
      <div style={{ position: 'absolute', top: -100, right: -60, width: 320, height: 320, borderRadius: '50%',
        background: 'radial-gradient(circle,#C8B6FF 0%,transparent 65%)', opacity: .5, pointerEvents: 'none' }} />
      <div style={{ position: 'relative', padding: '20px 24px', display: 'flex', alignItems: 'flex-start',
        justifyContent: 'space-between', borderBottom: '1px solid rgba(28,28,28,.08)' }}>
        <div>
          <div style={{ fontFamily: '"Instrument Serif",serif', fontStyle: 'italic', fontSize: 26,
            letterSpacing: '-.01em' }}>Get credits</div>
          <div style={{ fontSize: 13, color: '#6b6377', marginTop: 6, lineHeight: 1.5, maxWidth: 480 }}>
            One-time packs. No subscription. Credits expire 60 days after purchase.
            {typeof balance === 'number' ? (
              <span style={{ display: 'block', marginTop: 6, fontWeight: 600, color: '#7A5AE0' }}>
                Balance: {balance.toLocaleString()} credits
              </span>
            ) : null}
          </div>
        </div>
        <button type="button" onClick={onClose}
          style={{ width: 34, height: 34, borderRadius: 10, border: '1px solid #E0DAE8', background: '#FFFFFF',
            color: '#4a4356', cursor: 'pointer', fontSize: 18, lineHeight: 1 }}>×</button>
      </div>
      <div style={{ position: 'relative', padding: 20, overflow: 'auto' }}>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 14 }}>
          {CREDIT_PACKS.map((pack) => (
            <PlansPackCard key={pack.id} pack={pack} onBuy={onBuy} buying={buyingPackId === pack.id} />
          ))}
        </div>
      </div>
    </div>
  </div>
);

const TopBar = ({ onSignOut, balance, creditsLoading, onOpenPlans, onRefreshCredits, plansRequest }) => {
  const [plansOpen, setPlansOpen] = React.useState(false);
  const [accountOpen, setAccountOpen] = React.useState(false);
  const [user, setUser] = React.useState(null);
  const [buyingPackId, setBuyingPackId] = React.useState('');
  const [buyError, setBuyError] = React.useState('');

  React.useEffect(() => {
    const sb = getDashboardSupabase();
    if (!sb) return undefined;
    sb.auth.getSession().then(({ data: { session } }) => setUser(session?.user ?? null));
    const { data: sub } = sb.auth.onAuthStateChange((_event, session) => {
      setUser(session?.user ?? null);
    });
    return () => sub?.subscription?.unsubscribe();
  }, []);

  React.useEffect(() => {
    if (plansRequest > 0) setPlansOpen(true);
  }, [plansRequest]);

  const handleBuyPack = async (packId) => {
    setBuyError('');
    if (packStripeUrl(packId)) {
      window.open(packStripeUrl(packId), '_blank', 'noopener,noreferrer');
      return;
    }
    setBuyingPackId(packId);
    try {
      await startPackCheckout(packId);
    } catch (e) {
      setBuyError(e.message || 'Checkout failed');
      setBuyingPackId('');
    }
  };

  const openPlans = () => {
    setPlansOpen(true);
    if (onOpenPlans) onOpenPlans();
    else if (onRefreshCredits) onRefreshCredits();
  };

  const initial = avatarInitial(user);
  const balanceLabel = creditsLoading
    ? '…'
    : (typeof balance === 'number' ? balance.toLocaleString() : '—');

  return (
    <>
      <div className="dash-topbar" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '18px 32px 0' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <button type="button" onClick={openPlans}
            style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '9px 14px 9px 11px',
              background: '#FFFFFF', border: '1px solid #E0DAE8', borderRadius: 10, cursor: 'pointer',
              fontFamily: 'inherit', fontSize: 13, color: '#1c1c1c', fontWeight: 500,
              boxShadow: '0 1px 2px rgba(28,28,28,.03)' }}>
            <span style={{ color: '#7A5AE0' }}>{I.diamond}</span>
            {balanceLabel} credits
          </button>
          <button type="button" onClick={openPlans}
            style={{ padding: '9px 12px', background: 'transparent', border: 'none', cursor: 'pointer',
              fontFamily: 'inherit', fontSize: 12, fontWeight: 600, color: '#7A5AE0' }}>
            Buy credits
          </button>
        </div>
        <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
          <button type="button" title="Account" aria-label="Account"
            onClick={() => setAccountOpen(true)}
            style={{ width: 40, height: 40, borderRadius: 10, border: '1px solid #E0DAE8', cursor: 'pointer',
              padding: 2, background: '#FFFFFF', overflow: 'hidden' }}>
            <div style={{ width: '100%', height: '100%', borderRadius: 8,
              background: 'linear-gradient(135deg,#FFD4A8 0%,#F5B8D0 50%,#C8B6FF 100%)',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              color: '#fff', fontWeight: 700, fontSize: 13 }}>{initial}</div>
          </button>
        </div>
      </div>
      {buyError ? (
        <p style={{ margin: '0 32px', fontSize: 12, color: '#B42318' }}>{buyError}</p>
      ) : null}
      {plansOpen && typeof document !== 'undefined'
        ? ReactDOM.createPortal(
            <PlansModal
              balance={balance}
              buyingPackId={buyingPackId}
              onBuy={handleBuyPack}
              onClose={() => { setPlansOpen(false); setBuyError(''); }}
            />,
            document.body,
          )
        : null}
      {accountOpen && typeof document !== 'undefined'
        ? ReactDOM.createPortal(
            <AccountModal onClose={() => setAccountOpen(false)} onSignOut={onSignOut} />,
            document.body,
          )
        : null}
    </>
  );
};


// ─── Quick action tile (small, top row) ─────────────────────────────────────
const QuickTile = ({ icon, label, color, onClick, href, active }) => {
  const [hover, setHover] = React.useState(false);
  const Tag = href ? 'a' : 'button';
  const tagProps = href ? { href } : { onClick };
  return (
    <Tag {...tagProps}
    onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
    style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8,
      background: 'transparent', border: 'none', outline: 'none', cursor: 'pointer',
      textDecoration: 'none', padding: '6px 4px', fontFamily: 'inherit',
      transform: hover || active ? 'translateY(-2px)' : 'translateY(0)',
      transition: 'transform .18s' }}>
      <div style={{ width: 54, height: 54, borderRadius: 14, background: color.bg,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        color: color.fg,
        boxShadow: active || hover
          ? `0 8px 22px ${color.shadow}, 0 0 0 1px ${color.ring}`
          : `0 2px 6px ${color.shadow}, 0 0 0 1px ${color.ring}`,
        transition: 'box-shadow .18s' }}>
        {icon}
      </div>
      <div style={{ fontSize: label.length > 9 ? 10 : 11.5, color: '#4a4356', fontWeight: 500, letterSpacing: '-.005em',
        textAlign: 'center', lineHeight: 1.25, maxWidth: 72 }}>{label}</div>
    </Tag>);

};

// ─── Starter card (large, gradient) ─────────────────────────────────────────
const StarterCard = ({ icon, title, body, palette, onClick }) => {
  const [hover, setHover] = React.useState(false);
  return (
    <button onClick={onClick}
    onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
    style={{ position: 'relative', overflow: 'hidden', textAlign: 'left',
      background: palette.bg,
      border: '1px solid ' + palette.border,
      borderRadius: 18, padding: '18px 18px 20px', cursor: 'pointer',
      fontFamily: 'inherit', color: palette.ink, minHeight: 158,
      boxShadow: hover ? `0 14px 30px ${palette.shadow}` : `0 2px 6px ${palette.shadow}`,
      transform: hover ? 'translateY(-3px)' : 'translateY(0)',
      transition: 'transform .2s, box-shadow .2s' }}>
      {/* Ambient blob */}
      <div style={{ position: 'absolute', right: -30, bottom: -40, width: 180, height: 180, borderRadius: '50%',
        background: palette.blob, filter: 'blur(20px)', opacity: .65, pointerEvents: 'none' }} />
      <div style={{ position: 'relative' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 14 }}>
          <div style={{ width: 34, height: 34, borderRadius: 9,
            background: palette.iconBg,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            color: palette.iconFg }}>{icon}</div>
          <div style={{ fontWeight: 600, fontSize: 15.5, letterSpacing: '-.01em' }}>{title}</div>
        </div>
        <div style={{ fontSize: 12.5, lineHeight: 1.55, color: palette.body, maxWidth: '92%' }}>{body}</div>
      </div>
      {/* Hover arrow */}
      <div style={{ position: 'absolute', top: 18, right: 18, opacity: hover ? 1 : 0, transition: 'opacity .18s',
        color: palette.ink }}>
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <path d="M7 17 17 7M9 7h8v8" />
        </svg>
      </div>
    </button>);

};

// ─── Palettes for cards ─────────────────────────────────────────────────────
const lavenderP = {
  bg: 'linear-gradient(135deg,#E8DFFA 0%,#F0E5F8 100%)',
  blob: 'radial-gradient(circle,#C8B6FF 0%,transparent 70%)',
  border: 'rgba(200,182,255,.5)',
  iconBg: 'rgba(255,255,255,.7)', iconFg: '#7A5AE0',
  ink: '#3D2A6B', body: '#5B4A8A',
  shadow: 'rgba(122,90,224,.18)'
};
const pinkP = {
  bg: 'linear-gradient(135deg,#FCE0EC 0%,#F5B8D0 100%)',
  blob: 'radial-gradient(circle,#F5B8D0 0%,transparent 70%)',
  border: 'rgba(245,184,208,.55)',
  iconBg: 'rgba(255,255,255,.7)', iconFg: '#C4407A',
  ink: '#6B2A4A', body: '#8A4A6A',
  shadow: 'rgba(196,64,122,.18)'
};
const mintP = {
  bg: 'linear-gradient(135deg,#D6F0F5 0%,#B5E5D6 100%)',
  blob: 'radial-gradient(circle,#B5E5D6 0%,transparent 70%)',
  border: 'rgba(181,229,214,.6)',
  iconBg: 'rgba(255,255,255,.7)', iconFg: '#2A8060',
  ink: '#1F4D3D', body: '#3D6A5A',
  shadow: 'rgba(42,128,96,.16)'
};
const peachP = {
  bg: 'linear-gradient(135deg,#FFE8DC 0%,#FFD4A8 100%)',
  blob: 'radial-gradient(circle,#FFD4A8 0%,transparent 70%)',
  border: 'rgba(255,212,168,.6)',
  iconBg: 'rgba(255,255,255,.7)', iconFg: '#C46A20',
  ink: '#6B3A1A', body: '#8A5A30',
  shadow: 'rgba(196,106,32,.18)'
};
const creamP = {
  bg: 'linear-gradient(135deg,#FFF4D6 0%,#FFE8B5 100%)',
  blob: 'radial-gradient(circle,#FFD75A 0%,transparent 70%)',
  border: 'rgba(255,215,90,.4)',
  iconBg: 'rgba(255,255,255,.7)', iconFg: '#A87A20',
  ink: '#5C4318', body: '#7A6030',
  shadow: 'rgba(168,122,32,.15)'
};
const lightVioletP = {
  bg: 'linear-gradient(135deg,#F0E5F8 0%,#E8DFFA 100%)',
  blob: 'radial-gradient(circle,#C8B6FF 0%,transparent 70%)',
  border: 'rgba(232,223,250,.7)',
  iconBg: 'rgba(255,255,255,.8)', iconFg: '#9070D0',
  ink: '#4A3680', body: '#6B5A9A',
  shadow: 'rgba(122,90,224,.12)'
};
const lightMintP = {
  bg: 'linear-gradient(135deg,#E5F5EE 0%,#D6F0F5 100%)',
  blob: 'radial-gradient(circle,#B5E5D6 0%,transparent 70%)',
  border: 'rgba(214,240,245,.7)',
  iconBg: 'rgba(255,255,255,.8)', iconFg: '#3A9075',
  ink: '#2A5D4A', body: '#4D7A6A',
  shadow: 'rgba(42,128,96,.12)'
};
const lightPeachP = {
  bg: 'linear-gradient(135deg,#FFF4E8 0%,#FFE8DC 100%)',
  blob: 'radial-gradient(circle,#FFD4A8 0%,transparent 70%)',
  border: 'rgba(255,232,220,.7)',
  iconBg: 'rgba(255,255,255,.8)', iconFg: '#D08550',
  ink: '#6B4520', body: '#8A6540',
  shadow: 'rgba(196,106,32,.12)'
};

// ─── Quick tile colors (small) ──────────────────────────────────────────────
const qcLight = (bg, fg, ring, shadow) => ({ bg, fg, ring, shadow });
const QC = {
  violet: qcLight('linear-gradient(140deg,#E8DFFA,#F0E5F8)', '#7A5AE0', 'rgba(200,182,255,.5)', 'rgba(122,90,224,.16)'),
  pink: qcLight('linear-gradient(140deg,#FCE0EC,#FAD0E0)', '#C4407A', 'rgba(245,184,208,.5)', 'rgba(196,64,122,.14)'),
  mint: qcLight('linear-gradient(140deg,#D6F0F5,#C5E8DC)', '#2A8060', 'rgba(181,229,214,.55)', 'rgba(42,128,96,.14)'),
  peach: qcLight('linear-gradient(140deg,#FFE8DC,#FFD8B8)', '#C46A20', 'rgba(255,212,168,.55)', 'rgba(196,106,32,.14)'),
  cream: qcLight('linear-gradient(140deg,#FFF4D6,#FFE8B5)', '#A87A20', 'rgba(255,215,90,.4)', 'rgba(168,122,32,.14)'),
  lvioletDeep: qcLight('linear-gradient(140deg,#C8B6FF,#B59AFE)', '#FFFFFF', 'rgba(200,182,255,.6)', 'rgba(122,90,224,.25)'),
  pinkDeep: qcLight('linear-gradient(140deg,#F5B8D0,#F09CC0)', '#FFFFFF', 'rgba(245,184,208,.6)', 'rgba(196,64,122,.22)'),
  mintDeep: qcLight('linear-gradient(140deg,#B5E5D6,#9AD8C2)', '#FFFFFF', 'rgba(181,229,214,.6)', 'rgba(42,128,96,.22)')
};

// ─── User library (get-library) ─────────────────────────────────────────────
const MODE_LABEL = {
  create: 'Create',
  compose: 'Compose',
  animate: 'Animate',
  marketing: 'Marketing',
  text: 'Create',
  image: 'Create',
  hybrid: 'Compose',
  voice: 'Compose',
};

const LIBRARY_TABS = [
  { id: 'all', label: 'All' },
  { id: 'done', label: 'Done' },
  { id: 'running', label: 'In progress' },
  { id: 'failed', label: 'Failed' },
];

const timeAgo = (iso) => {
  const diff = Date.now() - new Date(iso).getTime();
  const mins = Math.floor(diff / 60000);
  if (mins < 1) return 'just now';
  if (mins < 60) return `${mins}m ago`;
  const hrs = Math.floor(mins / 60);
  if (hrs < 24) return `${hrs}h ago`;
  return `${Math.floor(hrs / 24)}d ago`;
};

const modeLabel = (mode) => MODE_LABEL[mode] ?? (mode ? String(mode) : 'Video');

const filterLibraryJobs = (jobs, tab) => {
  if (tab === 'done') return jobs.filter((j) => j.status === 'succeeded');
  if (tab === 'running') return jobs.filter((j) => j.status === 'running' || j.status === 'queued');
  if (tab === 'failed') return jobs.filter((j) => j.status === 'failed' || j.status === 'canceled');
  return jobs;
};

function useDashboardLibrary(active) {
  const [jobs, setJobs] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState('');
  const [signedIn, setSignedIn] = React.useState(false);

  const load = React.useCallback(async () => {
    const sb = getDashboardSupabase();
    if (!sb) {
      setLoading(false);
      setSignedIn(false);
      setJobs([]);
      return;
    }
    const { data: { session } } = await sb.auth.getSession();
    setSignedIn(!!session);
    if (!session) {
      setJobs([]);
      setLoading(false);
      setError('');
      return;
    }
    setLoading(true);
    setError('');
    try {
      const data = await invokeAuthedFunction('get-library', {});
      setJobs(Array.isArray(data.jobs) ? data.jobs : []);
    } catch (e) {
      setError(e.message || 'Could not load videos');
      setJobs([]);
    } finally {
      setLoading(false);
    }
  }, []);

  React.useEffect(() => {
    load();
  }, [load, active]);

  return { jobs, loading, error, signedIn, reload: load };
}

const discoverItemToJob = (item) => ({
  id: item.id,
  mode: item.mode,
  prompt: item.prompt,
  videoUrl: item.videoUrl,
  thumbnailUrl: item.thumbnailUrl ?? null,
  createdAt: item.createdAt,
  status: 'succeeded',
  creator: item.creator ?? null,
  creatorId: item.creatorId ?? null,
  remix: item.remix ?? null,
});

function fallbackRemixFromJob(job) {
  if (job?.remix?.seedanceMode) return job.remix;
  return {
    seedanceMode: job?.mode === 'animate' ? 'animate' : job?.mode === 'compose' ? 'compose' : 'create',
    prompt: job?.prompt ?? '',
    imageUris: job?.thumbnailUrl ? [job.thumbnailUrl] : [],
    characterImageUri: job?.thumbnailUrl ?? null,
    referenceVideoUri: job?.videoUrl ?? null,
    composeVideoUris: job?.videoUrl ? [job.videoUrl] : [],
    animateAudioUri: null,
    aspect: '9:16',
    resolution: '720p',
    duration: 8,
    nativeAudio: true,
  };
}

async function urlToPreviewMedia(url, name, type) {
  const res = await fetch(url);
  if (!res.ok) throw new Error('Could not load media');
  const blob = await res.blob();
  const file = new File([blob], name, { type: blob.type || type });
  return { file, preview: URL.createObjectURL(file) };
}

function saveBlobAsFile(blob, filename) {
  const href = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = href;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  a.remove();
  URL.revokeObjectURL(href);
}

/** Direct R2 fetch — can fail when bucket CORS blocks kimitai.com. */
async function downloadViaPresignedUrl(videoUrl, filename) {
  const res = await fetch(videoUrl);
  if (!res.ok) throw new Error('Download failed');
  const blob = await res.blob();
  saveBlobAsFile(blob, filename);
}

/** Proxy through Supabase so the browser never fetches R2 directly (no R2 CORS). */
async function downloadUserVideo(job) {
  if (!job?.id) throw new Error('Video not found');
  const cfg = getLandingConfig();
  const sb = getDashboardSupabase();
  if (!sb || !cfg) throw new Error('Supabase is not configured');
  const { data: { session } } = await sb.auth.getSession();
  if (!session?.access_token) throw new Error('Not signed in');

  const res = await fetch(`${cfg.supabaseUrl}/functions/v1/download-library-video`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      apikey: cfg.supabaseAnonKey,
      Authorization: `Bearer ${session.access_token}`,
    },
    body: JSON.stringify({ jobId: job.id }),
  });

  if (!res.ok) {
    let message = 'Download failed';
    try {
      const json = await res.json();
      if (json?.error) message = String(json.error);
    } catch {
      const text = await res.text().catch(() => '');
      if (text) message = text;
    }
    throw new Error(message);
  }

  const blob = await res.blob();
  saveBlobAsFile(blob, `kimitai-${job.id}.mp4`);
}

async function downloadLibraryVideo(job) {
  const filename = `kimitai-${job.id || 'video'}.mp4`;
  if (job?.id) {
    try {
      await downloadUserVideo(job);
      return;
    } catch (proxyErr) {
      console.warn('download-library-video', proxyErr);
      if (job.videoUrl) {
        await downloadViaPresignedUrl(job.videoUrl, filename);
        return;
      }
      throw proxyErr;
    }
  }
  if (!job?.videoUrl) throw new Error('Video URL not available');
  await downloadViaPresignedUrl(job.videoUrl, filename);
}

async function fetchDiscoverFeed() {
  const cfg = getLandingConfig();
  if (!cfg?.supabaseUrl || !cfg?.supabaseAnonKey) {
    return { items: [], error: 'Supabase is not configured' };
  }
  const sb = getDashboardSupabase();
  if (sb) {
    const { data: { session } } = await sb.auth.getSession();
    if (session?.access_token) {
      try {
        const data = await invokeAuthedFunction('get-discover', {});
        const items = Array.isArray(data.items) ? data.items : [];
        return { items: items.map(discoverItemToJob), error: null };
      } catch (e) {
        console.warn('get-discover', e);
      }
    }
  }
  try {
    const res = await fetch(`${cfg.supabaseUrl}/functions/v1/get-discover-public`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        apikey: cfg.supabaseAnonKey,
        Authorization: `Bearer ${cfg.supabaseAnonKey}`,
      },
      body: JSON.stringify({ limit: 50 }),
    });
    const data = await res.json().catch(() => ({}));
    if (!res.ok) throw new Error(data.error || res.statusText || 'Request failed');
    const items = Array.isArray(data.items) ? data.items : [];
    return { items: items.map(discoverItemToJob), error: null };
  } catch (e) {
    return { items: [], error: e.message || 'Could not load Discover' };
  }
}

function useDashboardDiscover(active) {
  const [items, setItems] = React.useState([]);
  const [loading, setLoading] = React.useState(false);
  const [error, setError] = React.useState('');

  const load = React.useCallback(async () => {
    setLoading(true);
    setError('');
    const { items: next, error: err } = await fetchDiscoverFeed();
    setItems(next);
    setError(err || '');
    setLoading(false);
  }, []);

  React.useEffect(() => {
    if (active === 'discover') load();
  }, [active, load]);

  return { items, loading, error, reload: load };
}

const VideoPlayerModal = ({ job, onClose, onRemix, onDownload, allowDownload = true }) => {
  const [downloading, setDownloading] = React.useState(false);
  if (!job?.videoUrl) return null;
  const handleDownload = async () => {
    if (onDownload) {
      onDownload(job);
      return;
    }
    setDownloading(true);
    try {
      await downloadLibraryVideo(job);
    } catch (e) {
      console.warn('download', e);
      window.alert(e.message || 'Download failed');
    } finally {
      setDownloading(false);
    }
  };
  return (
    <div style={modalShellStyle} onClick={onClose}>
      <div onClick={(e) => e.stopPropagation()}
        style={{
          width: 'min(420px, 100%)', maxHeight: '90vh', background: '#1c1c1c',
          borderRadius: 18, overflow: 'hidden', position: 'relative',
          boxShadow: '0 24px 60px rgba(0,0,0,.4)',
        }}>
        <button type="button" onClick={onClose}
          style={{ position: 'absolute', top: 10, right: 10, zIndex: 2, width: 32, height: 32,
            borderRadius: 8, border: 'none', background: 'rgba(0,0,0,.5)', color: '#fff',
            cursor: 'pointer', fontSize: 18 }}>×</button>
        <video src={job.videoUrl} controls autoPlay playsInline
          style={{ width: '100%', display: 'block', maxHeight: '70vh', background: '#000' }} />
        <div style={{ padding: '12px 16px 14px', color: '#fff', fontSize: 12 }}>
          <div style={{ fontWeight: 600 }}>{modeLabel(job.mode)}</div>
          {job.creator ? (
            <div style={{ opacity: .85, marginTop: 4, fontSize: 11 }}>{job.creator}</div>
          ) : null}
          <div style={{ opacity: .75, marginTop: 4, lineHeight: 1.4, marginBottom: 12 }}>
            {(job.prompt || 'Untitled').slice(0, 120)}
          </div>
          <div style={{ display: 'flex', gap: 8 }}>
            <button type="button" onClick={() => onRemix?.(job)}
              style={{
                flex: 1, padding: '10px 12px', borderRadius: 999, border: 'none', cursor: 'pointer',
                fontFamily: 'inherit', fontSize: 12, fontWeight: 600, color: '#1c1c1c',
                background: 'linear-gradient(135deg,#C8B6FF 0%,#F5B8D0 100%)',
              }}>
              Remix
            </button>
            {allowDownload ? (
              <button type="button" onClick={handleDownload} disabled={downloading}
                style={{
                  flex: 1, padding: '10px 12px', borderRadius: 999, border: 'none', cursor: 'pointer',
                  fontFamily: 'inherit', fontSize: 12, fontWeight: 600, color: '#fff',
                  background: '#1c1c1c', opacity: downloading ? 0.65 : 1,
                }}>
                {downloading ? 'Downloading…' : 'Download'}
              </button>
            ) : null}
          </div>
        </div>
      </div>
    </div>
  );
};

const VideoCard = ({ job, onPlay }) => {
  const [hover, setHover] = React.useState(false);
  const isRunning = job.status === 'running' || job.status === 'queued';
  const isFailed = job.status === 'failed' || job.status === 'canceled';
  const canPlay = job.status === 'succeeded' && job.videoUrl;
  const poster = job.thumbnailUrl || undefined;

  return (
    <button type="button" disabled={!canPlay && !isRunning}
      onClick={() => { if (canPlay) onPlay(job); }}
      onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
      style={{
        position: 'relative', aspectRatio: '9 / 16', width: '100%',
        borderRadius: 14, overflow: 'hidden', border: '1px solid #E0DAE8',
        padding: 0, cursor: canPlay ? 'pointer' : 'default',
        background: 'linear-gradient(160deg,#D6F0F5 0%,#E8DFFA 100%)',
        fontFamily: 'inherit', textAlign: 'left',
        transform: hover && canPlay ? 'translateY(-2px)' : 'none',
        boxShadow: hover && canPlay ? '0 12px 28px rgba(122,90,224,.18)' : '0 2px 6px rgba(28,28,28,.06)',
        transition: 'transform .18s, box-shadow .18s',
      }}>
      {poster ? (
        <img src={poster} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
      ) : canPlay ? (
        <video src={job.videoUrl} muted playsInline preload="metadata"
          style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block', pointerEvents: 'none' }} />
      ) : null}
      {isRunning ? (
        <div style={{ position: 'absolute', inset: 0, background: 'rgba(80,40,192,.55)',
          display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff',
          fontSize: 12, fontWeight: 600 }}>
          Generating…
        </div>
      ) : null}
      {isFailed ? (
        <div style={{ position: 'absolute', inset: 0, background: 'rgba(200,40,40,.45)',
          display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: 11 }}>
          Failed
        </div>
      ) : null}
      {canPlay ? (
        <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center',
          background: hover ? 'rgba(0,0,0,.25)' : 'rgba(0,0,0,.12)', transition: 'background .15s' }}>
          <div style={{ width: 40, height: 40, borderRadius: '50%', background: 'rgba(255,255,255,.92)',
            display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#7A5AE0' }}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7Z" /></svg>
          </div>
        </div>
      ) : null}
      <div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, padding: '8px 9px',
        background: 'linear-gradient(transparent, rgba(0,0,0,.55))' }}>
        <div style={{ fontSize: 10, fontWeight: 600, color: '#fff' }}>{modeLabel(job.mode)}</div>
        <div style={{ fontSize: 9, color: 'rgba(255,255,255,.85)', marginTop: 2 }}>
          {job.creator || timeAgo(job.createdAt)}
        </div>
      </div>
    </button>
  );
};

const VideoGrid = ({ jobs, limit, columns = 'repeat(auto-fill, minmax(140px, 1fr))', onPlay, className = '' }) => {
  const list = limit ? jobs.slice(0, limit) : jobs;
  if (list.length === 0) return null;
  return (
    <div className={className} style={{ display: 'grid', gridTemplateColumns: columns, gap: 14 }}>
      {list.map((job) => (
        <VideoCard key={job.id} job={job} onPlay={onPlay} />
      ))}
    </div>
  );
};

const LibraryEmpty = ({ signedIn, loading, error, message }) => {
  if (loading) {
    return <p style={{ fontSize: 13, color: '#6b6377', margin: 0, textAlign: 'center', padding: '32px 0' }}>Loading videos…</p>;
  }
  if (error) {
    return <p style={{ fontSize: 13, color: '#B42318', margin: 0, textAlign: 'center', padding: '24px 0' }}>{error}</p>;
  }
  if (!signedIn) {
    return (
      <div style={{ textAlign: 'center', padding: '28px 0' }}>
        <p style={{ fontSize: 13, color: '#6b6377', margin: '0 0 14px' }}>Sign in to see videos from your account.</p>
        <a href="index.html" style={{
          display: 'inline-flex', padding: '10px 18px', borderRadius: 999, background: '#1c1c1c',
          color: '#fff', fontSize: 13, fontWeight: 600, textDecoration: 'none',
        }}>Sign in</a>
      </div>
    );
  }
  return (
    <p style={{ fontSize: 13, color: '#6b6377', margin: 0, textAlign: 'center', padding: '32px 0' }}>
      {message || 'No videos yet. Create one in the app to see it here.'}
    </p>
  );
};

const SectionRule = ({ label }) => (
  <div style={{ display: 'flex', alignItems: 'center', gap: 12, margin: '34px 0 16px' }}>
    <div style={{ flex: 1, height: 1, background: '#E0DAE8' }} />
    <div style={{ fontSize: 12.5, color: '#6b6377', letterSpacing: '.02em' }}>{label}</div>
    <div style={{ flex: 1, height: 1, background: '#E0DAE8' }} />
  </div>
);

const HomeView = ({
  library,
  greetingFont,
  greetingStyle,
  displayName,
  tweaks,
  onPlay,
  onCreditsChange,
  onNeedCredits,
  composerMode,
  setComposerMode,
  remixPrefill,
  onRemixApplied,
}) => {
  const { jobs, loading, error, reload } = library;
  const recent = jobs;

  return (
    <>
      <div style={{ textAlign: 'center', marginBottom: 28 }}>
        <h1 className="dash-home-greeting" style={{ margin: 0, fontSize: 38, fontWeight: 500, letterSpacing: '-.02em',
          fontFamily: greetingFont, fontStyle: greetingStyle, color: '#1c1c1c' }}>
          Good morning,{' '}
          <span style={{ background: 'linear-gradient(120deg,#9070D0 0%,#C4407A 50%,#D08550 100%)',
            WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>{displayName || 'there'}</span>
        </h1>
        <p style={{ margin: '10px 0 0', fontSize: 14.5, color: '#6b6377' }}>
          Create, Animate, Compose, or make marketing videos.
        </p>
      </div>

      <div className="dash-home-tiles" style={{
        display: 'flex', flexWrap: 'wrap', justifyContent: 'center', alignItems: 'flex-start',
        gap: 6, marginBottom: 0, maxWidth: 'fit-content', margin: '0 auto',
      }}>
        <QuickTile icon={I.sparkle} label="Create" color={QC.violet} active={composerMode === 'create'}
          onClick={() => setComposerMode('create')} />
        <QuickTile icon={I.wave} label="Animate" color={QC.peach} active={composerMode === 'animate'}
          onClick={() => setComposerMode('animate')} />
        <QuickTile icon={I.grid} label="Compose" color={QC.lvioletDeep} active={composerMode === 'compose'}
          onClick={() => setComposerMode('compose')} />
        <QuickTile icon={I.megaphone} label="Marketing" color={QC.mint} active={composerMode === 'marketing'}
          onClick={() => setComposerMode('marketing')} />
      </div>

      <ModeComposer mode={composerMode} glow={tweaks.inputGlow} onComplete={reload}
        onCreditsChange={onCreditsChange} onNeedCredits={onNeedCredits}
        remixPrefill={remixPrefill} onRemixApplied={onRemixApplied} />

      <SectionRule label="Recent videos" />

      {recent.length > 0 ? (
        <VideoGrid jobs={recent} limit={3} columns="repeat(3, 1fr)" className="dash-video-grid" onPlay={onPlay} />
      ) : (
        <LibraryEmpty signedIn loading={loading} error={error}
          message="No videos yet. Your latest creations will show up here." />
      )}
    </>
  );
};

const DiscoverView = ({ discover, onPlayDiscover }) => {
  const { items, loading, error } = discover;

  return (
    <>
      <div style={{ marginBottom: 24 }}>
        <h1 style={{ margin: 0, fontSize: 32, fontWeight: 500, letterSpacing: '-.02em',
          fontFamily: '"Instrument Serif", serif', fontStyle: 'italic' }}>Discover</h1>
        <p style={{ margin: '8px 0 0', fontSize: 14, color: '#6b6377' }}>
          Featured videos from the community — same feed as the app Discover tab.
        </p>
      </div>

      {items.length > 0 ? (
        <VideoGrid jobs={items} columns="repeat(auto-fill, minmax(148px, 1fr))" className="dash-video-grid" onPlay={onPlayDiscover} />
      ) : (
        <>
          {loading ? (
            <p style={{ fontSize: 13, color: '#6b6377', textAlign: 'center', padding: '32px 0' }}>Loading Discover…</p>
          ) : error ? (
            <p style={{ fontSize: 13, color: '#B42318', textAlign: 'center', padding: '24px 0' }}>{error}</p>
          ) : (
            <p style={{ fontSize: 13, color: '#6b6377', textAlign: 'center', padding: '32px 0' }}>
              No featured videos yet. Publish a completed video to Discover from the app.
            </p>
          )}
        </>
      )}
    </>
  );
};

const MyVideosView = ({ library, onPlay }) => {
  const { jobs, loading, error, signedIn } = library;
  const [tab, setTab] = React.useState('all');
  const filtered = filterLibraryJobs(jobs, tab);

  return (
    <>
      <div style={{ marginBottom: 24 }}>
        <h1 style={{ margin: 0, fontSize: 32, fontWeight: 500, letterSpacing: '-.02em',
          fontFamily: '"Instrument Serif", serif', fontStyle: 'italic' }}>My videos</h1>
        <p style={{ margin: '8px 0 0', fontSize: 14, color: '#6b6377' }}>
          Everything you’ve generated, synced from your account.
        </p>
      </div>

      <div style={{ display: 'flex', gap: 20, marginBottom: 20, flexWrap: 'wrap' }}>
        {LIBRARY_TABS.map((t) => (
          <button key={t.id} type="button" onClick={() => setTab(t.id)}
            style={{
              border: 'none', background: 'transparent', cursor: 'pointer', fontFamily: 'inherit',
              fontSize: 12, fontWeight: tab === t.id ? 600 : 500,
              color: tab === t.id ? '#1c1c1c' : '#9089A0',
              borderBottom: tab === t.id ? '2px solid #1c1c1c' : '2px solid transparent',
              padding: '0 0 6px',
            }}>
            {t.label}
          </button>
        ))}
      </div>

      {filtered.length > 0 ? (
        <VideoGrid jobs={filtered} columns="repeat(auto-fill, minmax(148px, 1fr))" className="dash-video-grid" onPlay={onPlay} />
      ) : (
        <LibraryEmpty signedIn={signedIn} loading={loading} error={error}
          message={tab === 'all' ? 'No videos yet.' : 'No videos in this filter.'} />
      )}
    </>
  );
};

// ─── Settings popover (Create / Compose output) ─────────────────────────────
const SETTINGS_OPTIONS = {
  aspect: ['9:16', '16:9', '1:1'],
  resolution: ['480p', '720p', '1080p'],
  duration: ['3', '5', '8', '10', '12', '15'],
};

/** UI state may be a single pick object or an array — normalize before .map. */
function asMediaList(value) {
  if (!value) return [];
  return Array.isArray(value) ? value : [value];
}

function mediaFilesFromList(value) {
  return asMediaList(value).map((x) => x?.file).filter(Boolean);
}

const MODE_META = {
  create: {
    title: 'Create',
    placeholder: 'Describe your scene…\nA fox sprinting through a neon-lit alley at midnight.',
    hint: 'Text or 1 image → video',
  },
  animate: {
    title: 'Animate',
    placeholder: 'Optional note (motion comes from your reference video)…',
    hint: 'Image + reference video',
  },
  compose: {
    title: 'Compose',
    placeholder: 'Describe the scene using @Image1, @Video1, @Audio1…',
    hint: 'Mix images, video & audio',
  },
  marketing: {
    title: 'Marketing videos',
    placeholder: 'Describe what happens in your ad…\nA creator unboxes the product, highlights one feature, ends with a CTA.',
    hint: 'Seedance · product & avatar',
  },
};

const MARKETING_OPTIONS = {
  ugc: ['Auto', 'Talking head', 'Testimonial', 'Behind the scenes'],
  hook: ['Auto', 'Bold opener', 'Question', 'Visual punch'],
  setting: ['Auto', 'Studio', 'Lifestyle', 'Outdoor', 'Product shelf'],
};

const MarketingChoicePopover = ({ title, icon, value, options, onChange, onClose }) => (
  <>
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 40 }} />
    <div style={{
      position: 'absolute', top: 'calc(100% + 8px)', left: 0, zIndex: 50, width: 280,
      background: '#FFFFFF', border: '1px solid #E0DAE8', borderRadius: 14,
      boxShadow: '0 20px 50px -10px rgba(122,90,224,.28), 0 0 0 1px rgba(255,255,255,.5) inset',
      overflow: 'hidden', fontFamily: 'inherit',
    }}>
      <div style={{ padding: '12px 16px 10px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <span style={{
            width: 22, height: 22, borderRadius: 6, background: '#E8DFFA',
            display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#7A5AE0',
          }}>{icon}</span>
          <div style={{ fontSize: 13, fontWeight: 600, color: '#1c1c1c' }}>{title}</div>
        </div>
        <button type="button" onClick={onClose} style={{
          border: 'none', background: 'transparent', cursor: 'pointer',
          color: '#9089A0', fontSize: 18, lineHeight: 1, padding: 4,
        }}>×</button>
      </div>
      <div style={{ padding: '4px 16px 14px', display: 'flex', flexWrap: 'wrap', gap: 6 }}>
        {options.map((opt) => {
          const active = value === opt;
          return (
            <button key={opt} type="button" onClick={() => { onChange(opt); onClose(); }}
              style={{
                padding: '6px 11px', borderRadius: 8, cursor: 'pointer', fontFamily: 'inherit',
                fontSize: 12, fontWeight: active ? 600 : 500,
                border: '1px solid ' + (active ? '#1c1c1c' : '#E0DAE8'),
                background: active ? '#1c1c1c' : '#FFFFFF',
                color: active ? '#FFFFFF' : '#4a4356',
              }}>
              {opt}
            </button>
          );
        })}
      </div>
    </div>
  </>
);

const MarketingAssetPopover = ({ title, previewUrl, onChange, onRemove, onClose }) => (
  <>
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 40 }} />
    <div style={{
      position: 'absolute', top: 'calc(100% + 8px)', left: 0, zIndex: 50, width: 200,
      background: '#FFFFFF', border: '1px solid #E0DAE8', borderRadius: 14,
      boxShadow: '0 20px 50px -10px rgba(122,90,224,.28)', padding: 12, fontFamily: 'inherit',
    }}>
      <div style={{ fontSize: 12, fontWeight: 600, color: '#1c1c1c', marginBottom: 8 }}>{title}</div>
      {previewUrl ? (
        <img src={previewUrl} alt="" style={{
          width: '100%', height: 100, objectFit: 'cover', borderRadius: 10,
          border: '1px solid #E0DAE8', marginBottom: 10,
        }} />
      ) : null}
      <div style={{ display: 'flex', gap: 6 }}>
        <button type="button" onClick={onChange}
          style={{
            flex: 1, padding: '7px 10px', borderRadius: 8, border: '1px solid #E0DAE8',
            background: '#FAF7FE', color: '#7A5AE0', fontFamily: 'inherit', fontSize: 12,
            fontWeight: 600, cursor: 'pointer',
          }}>Change</button>
        <button type="button" onClick={onRemove}
          style={{
            flex: 1, padding: '7px 10px', borderRadius: 8, border: '1px solid #E0DAE8',
            background: '#FFFFFF', color: '#4a4356', fontFamily: 'inherit', fontSize: 12,
            fontWeight: 500, cursor: 'pointer',
          }}>Remove</button>
      </div>
    </div>
  </>
);

const ibtn = { width: 30, height: 30, borderRadius: 8, border: 'none', background: 'transparent', cursor: 'pointer',
  display: 'flex', alignItems: 'center', justifyContent: 'center' };

const MarketingToolbarBtn = ({ title, icon, active, highlight, disabled, onClick, popover }) => (
  <div style={{ position: 'relative' }}>
    <button type="button" title={title} disabled={disabled} onClick={onClick}
      style={{
        ...ibtn, position: 'relative',
        background: active ? '#F0E5F8' : 'transparent',
        opacity: disabled ? 0.55 : 1,
      }}>
      <span style={{ color: active || highlight ? '#7A5AE0' : '#6b6377', display: 'flex' }}>{icon}</span>
      {highlight ? (
        <span style={{
          position: 'absolute', top: 4, right: 4, width: 6, height: 6, borderRadius: 3,
          background: '#7A5AE0', border: '1.5px solid #fff',
        }} />
      ) : null}
    </button>
    {popover}
  </div>
);

const MediaChip = ({ file, previewUrl, onRemove, label }) => (
  <div style={{ position: 'relative', width: 72, flexShrink: 0 }}>
    <div style={{
      width: 72, height: 72, borderRadius: 10, overflow: 'hidden',
      border: '1px solid #E0DAE8', background: '#F7F2FB',
    }}>
      {previewUrl && file?.type?.startsWith('video/') ? (
        <video src={previewUrl} style={{ width: '100%', height: '100%', objectFit: 'cover' }} muted />
      ) : previewUrl ? (
        <img src={previewUrl} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
      ) : (
        <div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontSize: 10, color: '#9089A0' }}>{label}</div>
      )}
    </div>
    <button type="button" onClick={onRemove}
      style={{ position: 'absolute', top: -6, right: -6, width: 20, height: 20, borderRadius: 10,
        border: 'none', background: '#1c1c1c', color: '#fff', cursor: 'pointer', fontSize: 12, lineHeight: 1 }}>×</button>
  </div>
);

const FilePickButton = ({ label, accept, multiple, disabled, onPick }) => (
  <button type="button" disabled={disabled} onClick={onPick}
    style={{
      padding: '8px 12px', borderRadius: 10, border: '1px dashed #C8B6FF',
      background: '#FAF7FE', color: '#7A5AE0', fontFamily: 'inherit', fontSize: 12,
      fontWeight: 600, cursor: disabled ? 'not-allowed' : 'pointer', opacity: disabled ? 0.5 : 1,
    }}>
    + {label}
  </button>
);

const SettingsPopover = ({ values, onChange, onClose, showResolution = true, showDuration = true }) => {
  const Row = ({ label, hint, options, k }) => (
    <div style={{padding:'14px 16px',borderTop:'1px solid #F0EBF6'}}>
      <div style={{display:'flex',alignItems:'baseline',justifyContent:'space-between',marginBottom:9}}>
        <div style={{fontSize:12,fontWeight:600,color:'#1c1c1c',letterSpacing:'.02em'}}>{label}</div>
        {hint && <div style={{fontSize:10.5,color:'#9089A0'}}>{hint}</div>}
      </div>
      <div style={{display:'flex',flexWrap:'wrap',gap:6}}>
        {options.map(opt => {
          const active = k === 'duration' ? String(values[k]) === String(opt) : values[k] === opt;
          const label = k === 'duration' ? `${opt}s` : opt;
          return (
            <button key={opt} onClick={()=>onChange(k,opt)}
              style={{padding:'6px 11px',borderRadius:8,cursor:'pointer',fontFamily:'inherit',
                fontSize:12,fontWeight:active?600:500,
                border:'1px solid '+(active?'#1c1c1c':'#E0DAE8'),
                background: active ? '#1c1c1c' : '#FFFFFF',
                color: active ? '#FFFFFF' : '#4a4356',
                transition:'background .12s, color .12s, border-color .12s'}}>
              {label}
            </button>
          );
        })}
      </div>
    </div>
  );
  return (
    <>
      <div onClick={onClose} style={{position:'fixed',inset:0,zIndex:40}}/>
      <div style={{position:'absolute',top:'calc(100% + 8px)',left:0,zIndex:50,
        width:300,background:'#FFFFFF',border:'1px solid #E0DAE8',borderRadius:14,
        boxShadow:'0 20px 50px -10px rgba(122,90,224,.28), 0 0 0 1px rgba(255,255,255,.5) inset',
        overflow:'hidden',fontFamily:'inherit'}}>
        <div style={{padding:'12px 16px 10px',display:'flex',alignItems:'center',justifyContent:'space-between'}}>
          <div style={{display:'flex',alignItems:'center',gap:8}}>
            <span style={{width:22,height:22,borderRadius:6,
              background:'linear-gradient(135deg,#C8B6FF,#F5B8D0)',
              display:'flex',alignItems:'center',justifyContent:'center',color:'#fff'}}>{I.sliders}</span>
            <div style={{fontSize:13,fontWeight:600,color:'#1c1c1c'}}>Output settings</div>
          </div>
          <button onClick={onClose} style={{border:'none',background:'transparent',cursor:'pointer',
            color:'#9089A0',fontSize:18,lineHeight:1,padding:4}}>×</button>
        </div>
        <Row label="Aspect ratio" k="aspect" options={SETTINGS_OPTIONS.aspect} />
        {showResolution ? (
          <Row label="Resolution" k="resolution" options={SETTINGS_OPTIONS.resolution} />
        ) : null}
        {showDuration ? (
          <Row label="Duration" hint="Up to 15s" k="duration" options={SETTINGS_OPTIONS.duration} />
        ) : null}
      </div>
    </>
  );
};

// ─── Mode composer (Create / Animate / Compose) ────────────────────────────
const ModeComposer = ({ mode, glow, onComplete, onCreditsChange, onNeedCredits, remixPrefill, onRemixApplied }) => {
  const meta = MODE_META[mode] || MODE_META.create;
  const [prompt, setPrompt] = React.useState('');
  const [composePrompt, setComposePrompt] = React.useState('');
  const [images, setImages] = React.useState([]);
  const [characterImage, setCharacterImage] = React.useState(null);
  const [referenceVideo, setReferenceVideo] = React.useState(null);
  const [animateAudio, setAnimateAudio] = React.useState(null);
  const [composeImages, setComposeImages] = React.useState([]);
  const [composeVideos, setComposeVideos] = React.useState([]);
  const [composeAudios, setComposeAudios] = React.useState([]);
  const [marketingPrompt, setMarketingPrompt] = React.useState('');
  const [marketingUgc, setMarketingUgc] = React.useState('Auto');
  const [marketingHook, setMarketingHook] = React.useState('Auto');
  const [marketingSetting, setMarketingSetting] = React.useState('Auto');
  const [marketingProduct, setMarketingProduct] = React.useState(null);
  const [marketingAvatar, setMarketingAvatar] = React.useState(null);
  const [focused, setFocused] = React.useState(false);
  const [settingsOpen, setSettingsOpen] = React.useState(false);
  const [marketingPanel, setMarketingPanel] = React.useState(null);
  const [output, setOutput] = React.useState({ aspect: '9:16', resolution: '720p', duration: 8 });
  const [generating, setGenerating] = React.useState(false);
  const [genProgress, setGenProgress] = React.useState(0);
  const [error, setError] = React.useState('');
  const [consentOpen, setConsentOpen] = React.useState(false);
  const consentPendingRef = React.useRef(null);
  const taRef = React.useRef(null);
  const fileRefs = {
    createImages: React.useRef(null),
    character: React.useRef(null),
    refVideo: React.useRef(null),
    animAudio: React.useRef(null),
    composeImages: React.useRef(null),
    composeVideos: React.useRef(null),
    composeAudios: React.useRef(null),
    marketingProduct: React.useRef(null),
    marketingAvatar: React.useRef(null),
  };

  const previewUrls = React.useRef([]);
  React.useEffect(() => () => {
    previewUrls.current.forEach((u) => URL.revokeObjectURL(u));
    previewUrls.current = [];
  }, []);

  const rememberPreview = (file) => {
    const url = URL.createObjectURL(file);
    previewUrls.current.push(url);
    return url;
  };

  const runWithConsent = (action) => {
    const sw = window.SeedanceWeb;
    if (sw?.hasAiProcessingConsent?.()) {
      action();
      return;
    }
    consentPendingRef.current = action;
    setConsentOpen(true);
  };

  const pickFiles = (ref, { multiple, accept, max, setter, current, asList }) => {
    if (!ref.current) return;
    runWithConsent(() => {
    ref.current.accept = accept;
    ref.current.multiple = multiple;
    ref.current.onchange = (e) => {
      const picked = Array.from(e.target.files || []);
      e.target.value = '';
      if (!picked.length) return;
      const toItem = (f) => ({ file: f, preview: rememberPreview(f) });
      if (multiple) {
        const next = [...current, ...picked].slice(0, max);
        setter(next.map((f) => toItem(f)));
      } else {
        const item = toItem(picked[0]);
        setter(asList ? [item] : item);
      }
    };
    ref.current.click();
    });
  };

  const autoSize = (el) => {
    if (!el) return;
    el.style.height = 'auto';
    el.style.height = Math.min(el.scrollHeight, 220) + 'px';
  };

  const textValue = mode === 'compose' ? composePrompt
    : mode === 'marketing' ? marketingPrompt : prompt;
  const setTextValue = mode === 'compose' ? setComposePrompt
    : mode === 'marketing' ? setMarketingPrompt : setPrompt;

  React.useEffect(() => autoSize(taRef.current), [textValue, mode]);
  React.useEffect(() => {
    setError('');
    setImages([]);
    setCharacterImage(null);
    setReferenceVideo(null);
    setAnimateAudio(null);
    setComposeImages([]);
    setComposeVideos([]);
    setComposeAudios([]);
    setMarketingPrompt('');
    setMarketingUgc('Auto');
    setMarketingHook('Auto');
    setMarketingSetting('Auto');
    setMarketingProduct(null);
    setMarketingAvatar(null);
    setMarketingPanel(null);
  }, [mode]);

  const closeComposerPanels = () => {
    setSettingsOpen(false);
    setMarketingPanel(null);
  };

  React.useEffect(() => {
    if (!remixPrefill) return;
    let cancelled = false;
    (async () => {
      try {
        const r = remixPrefill;
        setOutput({
          aspect: r.aspect || '9:16',
          resolution: r.resolution || '720p',
          duration: r.duration || 8,
        });
        if (r.seedanceMode === 'animate') {
          setPrompt(r.prompt || '');
          const face = r.characterImageUri
            ? await urlToPreviewMedia(r.characterImageUri, 'character.jpg', 'image/jpeg')
            : null;
          const motion = r.referenceVideoUri
            ? await urlToPreviewMedia(r.referenceVideoUri, 'motion.mp4', 'video/mp4')
            : null;
          const audio = r.animateAudioUri
            ? await urlToPreviewMedia(r.animateAudioUri, 'audio.mp3', 'audio/mpeg')
            : null;
          if (!cancelled) {
            setCharacterImage(face);
            setReferenceVideo(motion);
            setAnimateAudio(audio);
          }
        } else if (r.seedanceMode === 'compose') {
          setComposePrompt(r.prompt || '');
          const imgs = [];
          const maxComposeImgs = window.SeedanceWeb?.COMPOSE_MAX_IMAGES ?? 9;
          for (let i = 0; i < (r.imageUris || []).length && imgs.length < maxComposeImgs; i += 1) {
            const m = await urlToPreviewMedia(r.imageUris[i], `image${i}.jpg`, 'image/jpeg');
            imgs.push(m);
          }
          const vids = [];
          const videoUrls = r.composeVideoUris?.length ? r.composeVideoUris : (r.referenceVideoUri ? [r.referenceVideoUri] : []);
          for (let i = 0; i < videoUrls.length && vids.length < 3; i += 1) {
            const m = await urlToPreviewMedia(videoUrls[i], `video${i}.mp4`, 'video/mp4');
            vids.push(m);
          }
          const auds = [];
          if (r.animateAudioUri) {
            auds.push(await urlToPreviewMedia(r.animateAudioUri, 'audio.mp3', 'audio/mpeg'));
          }
          if (!cancelled) {
            setComposeImages(imgs);
            setComposeVideos(vids);
            setComposeAudios(auds);
          }
        } else {
          setPrompt(r.prompt || '');
          const imgs = [];
          for (let i = 0; i < (r.imageUris || []).length && imgs.length < 1; i += 1) {
            const m = await urlToPreviewMedia(r.imageUris[i], `image${i}.jpg`, 'image/jpeg');
            imgs.push(m);
          }
          if (!cancelled) setImages(imgs);
        }
      } catch (e) {
        console.warn('remix prefill', e);
      } finally {
        if (!cancelled && onRemixApplied) onRemixApplied();
      }
    })();
    return () => { cancelled = true; };
  }, [remixPrefill, onRemixApplied]);

  const canSend = () => {
    if (generating) return false;
    if (mode === 'create') return prompt.trim().length > 0;
    if (mode === 'animate') return characterImage && referenceVideo;
    if (mode === 'compose') return composePrompt.trim().length > 0;
    if (mode === 'marketing') {
      return marketingPrompt.trim().length > 0 && (marketingProduct || marketingAvatar);
    }
    return false;
  };

  const runGenerate = async () => {
    const sw = window.SeedanceWeb;
    const sb = getDashboardSupabase();
    if (!sw || !sb) {
      setError('Supabase is not configured');
      return;
    }
    setGenerating(true);
    setGenProgress(0);
    setError('');
    let taskId = null;
    let libraryDraft = null;
    try {
      const state = {
        prompt,
        composePrompt,
        images: mediaFilesFromList(images).slice(0, sw.CREATE_MAX_IMAGES ?? 1),
        characterImage: characterImage?.file ?? null,
        referenceVideo: referenceVideo?.file ?? null,
        animateAudio: animateAudio?.file ?? null,
        composeImages: mediaFilesFromList(composeImages).slice(0, sw.COMPOSE_MAX_IMAGES ?? 9),
        composeVideos: mediaFilesFromList(composeVideos).slice(0, sw.COMPOSE_MAX_VIDEOS ?? 3),
        composeAudios: mediaFilesFromList(composeAudios).slice(0, sw.COMPOSE_MAX_AUDIO ?? 3),
        marketingPrompt,
        marketingUgc,
        marketingHook,
        marketingSetting,
        marketingProduct: marketingProduct?.file ?? null,
        marketingAvatar: marketingAvatar?.file ?? null,
        output,
      };
      const started = await sw.startCreate(mode, state, invokeAuthedFunction, sb);
      taskId = started.taskId;
      libraryDraft = started.libraryDraft;
      if (typeof started.balance === 'number' && onCreditsChange) {
        onCreditsChange(started.balance);
      }
      const status = await sw.pollUntilDone(taskId, invokeAuthedFunction, (s) => {
        setGenProgress(s.progress ?? 0);
      });
      if (status.videoUrl) {
        await sw.persistCompleted(taskId, status.videoUrl, libraryDraft, invokeAuthedFunction);
      }
      if (onComplete) onComplete();
      setPrompt('');
      setComposePrompt('');
      setImages([]);
      setCharacterImage(null);
      setReferenceVideo(null);
      setAnimateAudio(null);
      setComposeImages([]);
      setComposeVideos([]);
      setComposeAudios([]);
      setMarketingPrompt('');
      setMarketingProduct(null);
      setMarketingAvatar(null);
    } catch (e) {
      let msg = e.message || 'Generation failed';
      if (e.status === 402 || e.message === 'insufficient_credits') {
        const need = e.payload?.creditsRequired;
        msg = need
          ? `Not enough credits (need ${need}). Buy a pack to continue.`
          : 'Not enough credits. Buy a pack to continue.';
        if (onNeedCredits) onNeedCredits();
      }
      setError(msg);
      if (taskId && libraryDraft && window.SeedanceWeb) {
        window.SeedanceWeb.markFailed(taskId, libraryDraft, invokeAuthedFunction);
      }
      if (onComplete) onComplete();
    } finally {
      setGenerating(false);
    }
  };

  const handleGenerate = () => {
    runWithConsent(() => runGenerate());
  };

  const imageList = asMediaList(images);
  const composeImageList = asMediaList(composeImages);
  const composeVideoList = asMediaList(composeVideos);
  const composeAudioList = asMediaList(composeAudios);
  const maxComposeImages = window.SeedanceWeb?.COMPOSE_MAX_IMAGES ?? 9;
  const maxComposeVideos = window.SeedanceWeb?.COMPOSE_MAX_VIDEOS ?? 3;
  const maxComposeAudio = window.SeedanceWeb?.COMPOSE_MAX_AUDIO ?? 3;
  const showCreateImages = mode === 'create';
  const showAnimate = mode === 'animate';
  const showComposeMedia = mode === 'compose';
  const showMarketing = mode === 'marketing';

  return (
    <div style={{ position: 'relative', marginTop: 18 }}>
      {consentOpen ? (
        <AiProcessingConsentModal
          onAgree={() => {
            window.SeedanceWeb?.setAiProcessingConsent?.();
            setConsentOpen(false);
            const fn = consentPendingRef.current;
            consentPendingRef.current = null;
            if (fn) fn();
          }}
          onDismiss={() => {
            consentPendingRef.current = null;
            setConsentOpen(false);
          }}
        />
      ) : null}
      {glow ? (
        <div style={{ position: 'absolute', inset: -3, borderRadius: 22,
          background: 'linear-gradient(120deg,#C8B6FF 0%,#F5B8D0 33%,#FFD4A8 66%,#B5E5D6 100%)',
          filter: focused ? 'blur(14px)' : 'blur(10px)',
          opacity: focused ? .85 : .55, transition: 'opacity .2s, filter .2s', pointerEvents: 'none' }} />
      ) : null}
      <div style={{ position: 'relative', background: '#FFFFFF', border: '1px solid #E0DAE8',
        borderRadius: 20, padding: '18px 20px 14px', boxShadow: '0 6px 18px rgba(122,90,224,.06)' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
          <div style={{ fontSize: 13, fontWeight: 600, color: '#1c1c1c' }}>{meta.title}</div>
          <div style={{ fontSize: 11, color: '#9089A0' }}>{meta.hint}</div>
        </div>

        <textarea ref={taRef} value={textValue} onChange={(e) => setTextValue(e.target.value)}
          onFocus={() => setFocused(true)} onBlur={() => setFocused(false)}
          onKeyDown={(e) => {
            if (e.key === 'Enter' && !e.shiftKey && canSend()) {
              e.preventDefault();
              handleGenerate();
            }
          }}
          placeholder={meta.placeholder} rows={2}
          disabled={generating}
          style={{ width: '100%', border: 'none', outline: 'none', fontFamily: 'inherit',
            fontSize: 15, color: '#1c1c1c', background: 'transparent',
            padding: '4px 0 14px', resize: 'none', overflow: 'auto',
            lineHeight: 1.5, minHeight: 48, maxHeight: 220, opacity: generating ? 0.6 : 1 }} />

        {(showCreateImages && imageList.length > 0) || (showAnimate && (characterImage || referenceVideo || animateAudio)) || (showComposeMedia && (composeImageList.length > 0 || composeVideoList.length > 0 || composeAudioList.length > 0)) || (showMarketing && (marketingProduct || marketingAvatar)) ? (
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginBottom: 12, alignItems: 'center' }}>
            {showCreateImages && imageList.map((item, i) => (
              <MediaChip key={i} file={item.file} previewUrl={item.preview} label="IMG"
                onRemove={() => setImages((prev) => asMediaList(prev).filter((_, j) => j !== i))} />
            ))}
            {showAnimate && characterImage ? (
              <MediaChip file={characterImage.file} previewUrl={characterImage.preview} label="Face"
                onRemove={() => setCharacterImage(null)} />
            ) : null}
            {showAnimate && referenceVideo ? (
              <MediaChip file={referenceVideo.file} previewUrl={referenceVideo.preview} label="Vid"
                onRemove={() => setReferenceVideo(null)} />
            ) : null}
            {showAnimate && animateAudio ? (
              <MediaChip file={animateAudio.file} previewUrl={null} label="Audio"
                onRemove={() => setAnimateAudio(null)} />
            ) : null}
            {showComposeMedia && composeImageList.map((item, i) => (
              <MediaChip key={`ci-${i}`} file={item.file} previewUrl={item.preview} label="IMG"
                onRemove={() => setComposeImages((p) => asMediaList(p).filter((_, j) => j !== i))} />
            ))}
            {showComposeMedia && composeVideoList.map((item, i) => (
              <MediaChip key={`cv-${i}`} file={item.file} previewUrl={item.preview} label="Vid"
                onRemove={() => setComposeVideos((p) => asMediaList(p).filter((_, j) => j !== i))} />
            ))}
            {showComposeMedia && composeAudioList.map((item, i) => (
              <MediaChip key={`ca-${i}`} file={item.file} previewUrl={null} label="Audio"
                onRemove={() => setComposeAudios((p) => asMediaList(p).filter((_, j) => j !== i))} />
            ))}
            {showMarketing && marketingProduct ? (
              <MediaChip file={marketingProduct.file} previewUrl={marketingProduct.preview} label="Product"
                onRemove={() => setMarketingProduct(null)} />
            ) : null}
            {showMarketing && marketingAvatar ? (
              <MediaChip file={marketingAvatar.file} previewUrl={marketingAvatar.preview} label="Avatar"
                onRemove={() => setMarketingAvatar(null)} />
            ) : null}
          </div>
        ) : null}

        <input ref={fileRefs.createImages} type="file" style={{ display: 'none' }} />
        <input ref={fileRefs.character} type="file" style={{ display: 'none' }} />
        <input ref={fileRefs.refVideo} type="file" style={{ display: 'none' }} />
        <input ref={fileRefs.animAudio} type="file" style={{ display: 'none' }} />
        <input ref={fileRefs.composeImages} type="file" style={{ display: 'none' }} />
        <input ref={fileRefs.composeVideos} type="file" style={{ display: 'none' }} />
        <input ref={fileRefs.composeAudios} type="file" style={{ display: 'none' }} />
        <input ref={fileRefs.marketingProduct} type="file" style={{ display: 'none' }} />
        <input ref={fileRefs.marketingAvatar} type="file" style={{ display: 'none' }} />

        {error ? (
          <p style={{ margin: '0 0 10px', fontSize: 12, color: '#B42318' }}>{error}</p>
        ) : null}
        {generating ? (
          <p style={{ margin: '0 0 10px', fontSize: 12, color: '#7A5AE0' }}>
            Generating… {Math.round(genProgress * 100)}%
          </p>
        ) : null}

        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <div style={{ display: 'flex', gap: 6, position: 'relative', alignItems: 'center', flexWrap: 'wrap' }}>
            <button type="button" title="Output settings" onClick={() => {
              setMarketingPanel(null);
              setSettingsOpen((o) => !o);
            }}
              style={{ ...ibtn, background: settingsOpen ? '#F0E5F8' : 'transparent' }}>
              <span style={{ color: settingsOpen ? '#7A5AE0' : '#6b6377' }}>{I.sliders}</span>
            </button>
            {showMarketing ? (
              <>
                <MarketingToolbarBtn title={`UGC: ${marketingUgc}`} icon={I.invite}
                  active={marketingPanel === 'ugc'} highlight={marketingUgc !== 'Auto'}
                  disabled={generating}
                  onClick={() => {
                    setSettingsOpen(false);
                    setMarketingPanel((p) => (p === 'ugc' ? null : 'ugc'));
                  }}
                  popover={marketingPanel === 'ugc' ? (
                    <MarketingChoicePopover title="UGC style" icon={I.invite} value={marketingUgc}
                      options={MARKETING_OPTIONS.ugc} onChange={setMarketingUgc}
                      onClose={() => setMarketingPanel(null)} />
                  ) : null}
                />
                <MarketingToolbarBtn title={`Hook: ${marketingHook}`} icon={I.target}
                  active={marketingPanel === 'hook'} highlight={marketingHook !== 'Auto'}
                  disabled={generating}
                  onClick={() => {
                    setSettingsOpen(false);
                    setMarketingPanel((p) => (p === 'hook' ? null : 'hook'));
                  }}
                  popover={marketingPanel === 'hook' ? (
                    <MarketingChoicePopover title="Opening hook" icon={I.target} value={marketingHook}
                      options={MARKETING_OPTIONS.hook} onChange={setMarketingHook}
                      onClose={() => setMarketingPanel(null)} />
                  ) : null}
                />
                <MarketingToolbarBtn title={`Setting: ${marketingSetting}`} icon={I.pin}
                  active={marketingPanel === 'setting'} highlight={marketingSetting !== 'Auto'}
                  disabled={generating}
                  onClick={() => {
                    setSettingsOpen(false);
                    setMarketingPanel((p) => (p === 'setting' ? null : 'setting'));
                  }}
                  popover={marketingPanel === 'setting' ? (
                    <MarketingChoicePopover title="Scene setting" icon={I.pin} value={marketingSetting}
                      options={MARKETING_OPTIONS.setting} onChange={setMarketingSetting}
                      onClose={() => setMarketingPanel(null)} />
                  ) : null}
                />
                <MarketingToolbarBtn title={marketingProduct ? 'Product image' : 'Add product image'}
                  icon={I.image} active={marketingPanel === 'product'} highlight={!!marketingProduct}
                  disabled={generating}
                  onClick={() => {
                    setSettingsOpen(false);
                    if (marketingProduct) {
                      setMarketingPanel((p) => (p === 'product' ? null : 'product'));
                    } else {
                      pickFiles(fileRefs.marketingProduct, {
                        multiple: false, accept: 'image/*', max: 1, current: [],
                        setter: (v) => setMarketingProduct(v),
                      });
                    }
                  }}
                  popover={marketingPanel === 'product' && marketingProduct ? (
                    <MarketingAssetPopover title="Product" previewUrl={marketingProduct.preview}
                      onChange={() => {
                        setMarketingPanel(null);
                        pickFiles(fileRefs.marketingProduct, {
                          multiple: false, accept: 'image/*', max: 1, current: [],
                          setter: (v) => setMarketingProduct(v),
                        });
                      }}
                      onRemove={() => { setMarketingProduct(null); setMarketingPanel(null); }}
                      onClose={() => setMarketingPanel(null)} />
                  ) : null}
                />
                <MarketingToolbarBtn title={marketingAvatar ? 'Avatar image' : 'Add avatar image'}
                  icon={I.user} active={marketingPanel === 'avatar'} highlight={!!marketingAvatar}
                  disabled={generating}
                  onClick={() => {
                    setSettingsOpen(false);
                    if (marketingAvatar) {
                      setMarketingPanel((p) => (p === 'avatar' ? null : 'avatar'));
                    } else {
                      pickFiles(fileRefs.marketingAvatar, {
                        multiple: false, accept: 'image/*', max: 1, current: [],
                        setter: (v) => setMarketingAvatar(v),
                      });
                    }
                  }}
                  popover={marketingPanel === 'avatar' && marketingAvatar ? (
                    <MarketingAssetPopover title="Avatar" previewUrl={marketingAvatar.preview}
                      onChange={() => {
                        setMarketingPanel(null);
                        pickFiles(fileRefs.marketingAvatar, {
                          multiple: false, accept: 'image/*', max: 1, current: [],
                          setter: (v) => setMarketingAvatar(v),
                        });
                      }}
                      onRemove={() => { setMarketingAvatar(null); setMarketingPanel(null); }}
                      onClose={() => setMarketingPanel(null)} />
                  ) : null}
                />
              </>
            ) : null}
            {showCreateImages ? (
              <FilePickButton label="Start image" accept="image/*" multiple={false}
                disabled={generating || imageList.length >= (window.SeedanceWeb?.CREATE_MAX_IMAGES ?? 1)}
                onPick={() => pickFiles(fileRefs.createImages, {
                  multiple: false, accept: 'image/*', max: 1,
                  current: mediaFilesFromList(images),
                  setter: setImages, asList: true,
                })} />
            ) : null}
            {showAnimate ? (
              <>
                <FilePickButton label="Character" accept="image/*" multiple={false}
                  disabled={generating || !!characterImage}
                  onPick={() => pickFiles(fileRefs.character, {
                    multiple: false, accept: 'image/*', max: 1, current: [],
                    setter: (v) => setCharacterImage(v),
                  })} />
                <FilePickButton label="Motion video" accept="video/*" multiple={false}
                  disabled={generating || !!referenceVideo}
                  onPick={() => pickFiles(fileRefs.refVideo, {
                    multiple: false, accept: 'video/*', max: 1, current: [],
                    setter: (v) => setReferenceVideo(v),
                  })} />
                <FilePickButton label="Audio (optional)" accept="audio/*,video/*" multiple={false}
                  disabled={generating || !!animateAudio}
                  onPick={() => pickFiles(fileRefs.animAudio, {
                    multiple: false, accept: 'audio/*,video/*', max: 1, current: [],
                    setter: (v) => setAnimateAudio(v),
                  })} />
              </>
            ) : null}
            {showComposeMedia ? (
              <>
                <FilePickButton label="Images" accept="image/*" multiple
                  disabled={generating || composeImageList.length >= maxComposeImages}
                  onPick={() => pickFiles(fileRefs.composeImages, {
                    multiple: true, accept: 'image/*', max: maxComposeImages,
                    current: mediaFilesFromList(composeImages),
                    setter: setComposeImages, asList: true,
                  })} />
                <FilePickButton label="Videos" accept="video/*" multiple
                  disabled={generating || composeVideoList.length >= maxComposeVideos}
                  onPick={() => pickFiles(fileRefs.composeVideos, {
                    multiple: true, accept: 'video/*', max: maxComposeVideos,
                    current: mediaFilesFromList(composeVideos),
                    setter: setComposeVideos, asList: true,
                  })} />
                <FilePickButton label="Audio" accept="audio/*,video/*" multiple
                  disabled={generating || composeAudioList.length >= maxComposeAudio}
                  onPick={() => pickFiles(fileRefs.composeAudios, {
                    multiple: true, accept: 'audio/*,video/*', max: maxComposeAudio,
                    current: mediaFilesFromList(composeAudios),
                    setter: setComposeAudios, asList: true,
                  })} />
              </>
            ) : null}
            {settingsOpen ? (
              <SettingsPopover values={output}
                onChange={(k, v) => setOutput((s) => ({
                  ...s,
                  [k]: k === 'duration' ? parseInt(v, 10) : v,
                }))}
                onClose={closeComposerPanels}
                showResolution={mode !== 'animate'}
                showDuration={mode === 'create' || mode === 'compose' || mode === 'marketing'} />
            ) : null}
          </div>
          <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
            <span style={{ fontSize: 11, color: '#9089A0' }}>Kimitai</span>
            <button type="button" title="Generate" disabled={!canSend()}
              onClick={handleGenerate}
              style={{ width: 36, height: 36, borderRadius: 10, border: 'none',
                cursor: canSend() ? 'pointer' : 'default',
                color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center',
                background: canSend()
                  ? 'linear-gradient(135deg,#C8B6FF 0%,#F5B8D0 50%,#C8B6FF 100%)'
                  : 'linear-gradient(135deg,#E8DFFA 0%,#F0E5F8 100%)',
                boxShadow: canSend() ? '0 4px 12px rgba(122,90,224,.3)' : 'none',
                opacity: canSend() ? 1 : 0.65, transition: 'opacity .15s' }}>{I.send}</button>
          </div>
        </div>
      </div>
    </div>
  );
};

// ─── Main view (authenticated workspace) ───────────────────────────────────
function DashboardWorkspace({ user, onSignOut }) {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [active, setActive] = React.useState('home');
  const [previewJob, setPreviewJob] = React.useState(null);
  const [previewAllowDownload, setPreviewAllowDownload] = React.useState(true);
  const [composerMode, setComposerMode] = React.useState('create');
  const [remixPrefill, setRemixPrefill] = React.useState(null);
  const [plansRequest, setPlansRequest] = React.useState(0);
  const credits = useDashboardCredits();
  const library = useDashboardLibrary(active);
  const discover = useDashboardDiscover(active);

  React.useEffect(() => {
    if (typeof window === 'undefined') return;
    const params = new URLSearchParams(window.location.search);
    if (params.get('purchase') !== 'success') return;
    let tries = 0;
    const poll = setInterval(() => {
      tries += 1;
      credits.refresh();
      if (tries >= 8) clearInterval(poll);
    }, 2000);
    window.history.replaceState({}, '', window.location.pathname);
    return () => clearInterval(poll);
  }, [credits.refresh]);

  React.useEffect(() => {
    if (typeof window === 'undefined') return;
    const params = new URLSearchParams(window.location.search);
    const packId = params.get('pack');
    if (!packId) return;
    const valid = CREDIT_PACKS.some((p) => p.id === packId);
    if (!valid) return;
    setPlansRequest((n) => n + 1);
    const next = new URLSearchParams(window.location.search);
    next.delete('pack');
    const qs = next.toString();
    const path = window.location.pathname;
    window.history.replaceState({}, '', qs ? `${path}?${qs}` : path);
  }, []);

  const greetingFont = t.greetingStyle === 'serif-italic' ?
  '"Instrument Serif", "Fraunces", serif' :
  'inherit';
  const greetingStyle = t.greetingStyle === 'serif-italic' ? 'italic' : 'normal';
  const displayName = user?.email
    ? user.email.split('@')[0].replace(/[._]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase())
    : 'there';

  const onPlayLibrary = (job) => {
    setPreviewJob(job);
    setPreviewAllowDownload(true);
  };

  const onPlayDiscover = (job) => {
    setPreviewJob(job);
    setPreviewAllowDownload(false);
  };

  const onRemixFromPreview = (job) => {
    const remix = fallbackRemixFromJob(job);
    setPreviewJob(null);
    setComposerMode(remix.seedanceMode || 'create');
    setRemixPrefill({ ...remix, _ts: Date.now() });
    setActive('home');
  };

  return (
    <div className="dash-shell" style={{ display: 'flex', height: '100vh', background: '#FAF7FE',
      fontFamily: '"Inter","SF Pro Text",ui-sans-serif,system-ui,sans-serif', color: '#1c1c1c' }}>

      <Sidebar active={active} setActive={setActive} />

      <main className="dash-main" style={{ flex: 1, display: 'flex', flexDirection: 'column', position: 'relative', overflow: 'hidden' }}>

        {/* Ambient background blobs */}
        {t.showAmbientBlobs &&
        <>
            <div style={{ position: 'absolute', top: -120, right: -80, width: 380, height: 380, borderRadius: '50%',
            background: 'radial-gradient(circle,#E8DFFA 0%,transparent 65%)', pointerEvents: 'none' }} />
            <div style={{ position: 'absolute', top: 60, left: '40%', width: 300, height: 300, borderRadius: '50%',
            background: 'radial-gradient(circle,#FCE0EC 0%,transparent 65%)', pointerEvents: 'none', opacity: .7 }} />
            <div style={{ position: 'absolute', top: -40, right: '30%', width: 260, height: 260, borderRadius: '50%',
            background: 'radial-gradient(circle,#FFE8DC 0%,transparent 65%)', pointerEvents: 'none', opacity: .6 }} />
          </>
        }

        <div style={{ position: 'relative', zIndex: 1 }}>
          <TopBar
            onSignOut={onSignOut}
            balance={credits.balance}
            creditsLoading={credits.loading}
            onRefreshCredits={credits.refresh}
            plansRequest={plansRequest}
          />
        </div>

        <div style={{ flex: 1, overflow: 'auto', position: 'relative', zIndex: 1 }}>
          <div className="dash-main-inner" style={{ maxWidth: 980, margin: '0 auto', padding: '40px 32px 40px' }}>
            {active === 'work' ? (
              <MyVideosView library={library} onPlay={onPlayLibrary} />
            ) : active === 'discover' ? (
              <DiscoverView discover={discover} onPlayDiscover={onPlayDiscover} />
            ) : (
              <HomeView library={library} greetingFont={greetingFont} greetingStyle={greetingStyle}
                displayName={displayName} tweaks={t} onPlay={onPlayLibrary}
                composerMode={composerMode} setComposerMode={setComposerMode}
                remixPrefill={remixPrefill} onRemixApplied={() => setRemixPrefill(null)}
                onCreditsChange={credits.setBalance}
                onNeedCredits={() => setPlansRequest((n) => n + 1)} />
            )}
            <div style={{ height: 40 }} />
          </div>
        </div>
      </main>

      {previewJob && typeof document !== 'undefined'
        ? ReactDOM.createPortal(
            <VideoPlayerModal job={previewJob} onClose={() => setPreviewJob(null)}
              onRemix={onRemixFromPreview} allowDownload={previewAllowDownload} />,
            document.body,
          )
        : null}

      <TweaksPanel title="Tweaks">
        <TweakSection label="Greeting" />
        <TweakRadio label="Style" value={t.greetingStyle}
        options={['serif-italic', 'sans']}
        onChange={(v) => setTweak('greetingStyle', v)} />
        <TweakSection label="Atmosphere" />
        <TweakToggle label="Ambient blobs" value={t.showAmbientBlobs}
        onChange={(v) => setTweak('showAmbientBlobs', v)} />
        <TweakToggle label="AI input glow" value={t.inputGlow}
        onChange={(v) => setTweak('inputGlow', v)} />
        <TweakSection label="Starter cards" />
        <TweakRadio label="Density" value={t.starterDensity}
        options={['compact', 'regular', 'comfy']}
        onChange={(v) => setTweak('starterDensity', v)} />
      </TweaksPanel>
    </div>);

}

function App() {
  const { session, user, authLoading, authBootError, signOut } = useDashboardAuth();

  if (authLoading) {
    return (
      <div style={{
        minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center',
        background: '#FAF7FE', fontFamily: '"Inter",sans-serif', color: '#6b6377', fontSize: 14,
      }}>
        Loading…
      </div>
    );
  }

  if (!session) {
    return <SignInScreen bootError={authBootError} />;
  }

  return <DashboardWorkspace user={user} onSignOut={signOut} />;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);