// Naviyell — Screens A-01 through A-05 (intro flow) — INTERACTIVE

// ── Intro-flow animations (splash reveal + onboarding idle motion) ──
// Injected once. All decorative loops are disabled under prefers-reduced-motion.
if (typeof document !== 'undefined' && !document.getElementById('ac-intro-anim')) {
  const s = document.createElement('style');
  s.id = 'ac-intro-anim';
  s.textContent = `
    @keyframes ac-splash-mark { from { opacity: 0; transform: translateY(14px); letter-spacing: 0.42em } to { opacity: 1; transform: translateY(0); letter-spacing: 0.2em } }
    @keyframes ac-splash-kicker { from { opacity: 0; transform: translateY(10px) } to { opacity: 0.85; transform: translateY(0) } }
    @keyframes ac-splash-rule { from { transform: scaleX(0); opacity: 0 } to { transform: scaleX(1); opacity: 0.6 } }
    @keyframes ac-splash-tag { from { opacity: 0; transform: translateY(8px) } to { opacity: 0.9; transform: translateY(0) } }
    @keyframes ac-splash-hint { from { opacity: 0 } to { opacity: 1 } }
    @keyframes ac-hint-bob { 0%,100% { transform: translateY(0); opacity: 0.55 } 50% { transform: translateY(7px); opacity: 1 } }
    @keyframes ac-twinkle { 0%,100% { opacity: 0.25; transform: scale(0.85) } 50% { opacity: 1; transform: scale(1.15) } }
    @keyframes ac-art-float { 0%,100% { transform: translateY(0) } 50% { transform: translateY(-9px) } }
    @keyframes ac-art-glow  { 0%,100% { opacity: 0.45; transform: scale(1) } 50% { opacity: 0.85; transform: scale(1.07) } }
    @keyframes ac-art-orbit { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }
    @keyframes ac-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }
    .ac-art-float { animation: ac-art-float 5.5s ease-in-out infinite }
    .ac-art-glow  { animation: ac-art-glow 4.5s ease-in-out infinite }
    .ac-tw { animation: ac-twinkle 3.2s ease-in-out infinite }
    @media (prefers-reduced-motion: reduce) {
      .ac-art-float, .ac-art-glow, .ac-tw,
      [data-splash-hint] { animation: none !important }
    }
  `;
  document.head.appendChild(s);
}

// ═══════════════════════════════════════════════════════════════════
// A-01 Splash — a deliberate brand moment. User-paced: tap or swipe up to
// continue (long auto-advance as a fallback only, per reviewer feedback
// that the previous auto-flip felt too fast / like noise).
// ═══════════════════════════════════════════════════════════════════
function A01Splash() {
  const nav = useNav();
  const startY = React.useRef(null);
  const proceed = React.useCallback(() => {
    // Pre-login is a single page: the splash goes straight to login.
    nav && nav.go && nav.go('login', { replace: true });
  }, [nav]);

  React.useEffect(() => {
    // Generous fallback only — primary interaction is tap / swipe-up.
    const t = setTimeout(proceed, 7000);
    return () => clearTimeout(t);
  }, [proceed]);

  const onDown = (e) => {
    startY.current = e.clientY;
  };
  const onUp = (e) => {
    const dy = startY.current == null ? 0 : e.clientY - startY.current;
    startY.current = null;
    if (dy < -36 || Math.abs(dy) < 8) proceed(); // swipe up, or tap
  };

  return (
    <div
      className='ac-screen'
      style={{
        width: '100%',
        height: '100%',
        background: `radial-gradient(ellipse at 50% 38%, ${A.ink700} 0%, ${A.ink900} 72%)`,
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center',
        paddingBottom: 76,
        position: 'relative',
        overflow: 'hidden',
        cursor: 'pointer',
        touchAction: 'none',
      }}
      onPointerDown={onDown}
      onPointerUp={onUp}>
      <ACStar x='18%' y='15%' size={6} opacity={0.5} />
      <ACStar x='78%' y='22%' size={8} opacity={0.7} />
      <ACStar x='12%' y='78%' size={5} opacity={0.4} />
      <ACStar x='85%' y='70%' size={7} opacity={0.6} />
      <ACStar x='50%' y='88%' size={5} opacity={0.5} />
      <ACStar x='30%' y='35%' size={4} opacity={0.3} />
      <ACStar x='65%' y='45%' size={4} opacity={0.3} />

      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
        <span
          className='ac-serif'
          style={{
            fontSize: 12,
            fontWeight: 400,
            color: A.gold300 || A.gold500,
            letterSpacing: '0.3em',
            marginLeft: '0.3em',
            animation: 'ac-splash-kicker 1100ms 300ms ease both',
          }}>
          アンコード
        </span>
        <span
          style={{
            fontSize: 40,
            fontWeight: 400,
            color: A.white,
            lineHeight: 1,
            marginTop: 8,
            marginLeft: '0.2em',
            fontFamily: '"Cormorant Garamond", "EB Garamond", "Noto Serif JP", Georgia, serif',
            animation: 'ac-splash-mark 1500ms 500ms cubic-bezier(.22,.61,.36,1) both',
          }}>
          NAVIYELL
        </span>
        <div
          style={{
            width: 46,
            height: 1,
            background: A.gold500,
            marginTop: 22,
            transformOrigin: 'center',
            animation: 'ac-splash-rule 900ms 1500ms ease both',
          }}
        />
      </div>
      <div
        className='ac-serif'
        style={{
          marginTop: 22,
          fontSize: 14,
          color: A.gold100,
          letterSpacing: 2,
          animation: 'ac-splash-tag 1200ms 1800ms ease both',
        }}>
        あなたの人生に　揺るぎない軸を
      </div>

      {/* swipe-up / tap affordance — makes the dwell feel intentional */}
      <div
        data-splash-hint
        style={{
          position: 'absolute',
          bottom: 46,
          left: 0,
          right: 0,
          display: 'flex',
          flexDirection: 'column',
          alignItems: 'center',
          gap: 8,
          animation: 'ac-splash-hint 900ms 3200ms ease both',
        }}>
        <svg width='22' height='13' viewBox='0 0 22 13' fill='none' style={{ animation: 'ac-hint-bob 2.2s 3600ms ease-in-out infinite' }}>
          <path d='M2 11L11 2l9 9' stroke={A.gold100} strokeWidth='1.6' strokeLinecap='round' strokeLinejoin='round' opacity='0.9' />
        </svg>
        <span style={{ fontSize: 11.5, color: A.gold100, opacity: 0.7, letterSpacing: 2 }}>スワイプして始める</span>
      </div>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// A-02 Onboarding — swipeable carousel. Drag/swipe horizontally, tap dots,
// or use the button. Every slide's illustration has gentle idle motion.
// ═══════════════════════════════════════════════════════════════════
const ONB_SLIDES = [
  { art: <OnbMoon />, heading: '自分を知る', sub: 'まずは今のあなたの状態を\n知ることから' },
  { art: <OnbConvo />, heading: '相談で道を見つける', sub: '経験豊富な専門家が\nあなたの意思決定をサポートします' },
  { art: <OnbWalk />, heading: '行動で変わる', sub: '助言を実行に移し\n振り返りで成長を実感しましょう' },
];

function A02Onboarding({ slide: slideProp }) {
  const nav = useNav();
  const { state, patch } = useAppState();
  // In canvas mode a fixed slide prop is passed; in the prototype it's live state.
  const isCanvas = slideProp !== undefined;
  const slide = isCanvas ? slideProp : state.onbSlide;
  const last = ONB_SLIDES.length - 1;

  const [drag, setDrag] = React.useState(0);
  const [dragging, setDragging] = React.useState(false);
  const startX = React.useRef(null);
  const widthRef = React.useRef(390);

  const setSlide = (i) => {
    if (!isCanvas) patch({ onbSlide: Math.max(0, Math.min(last, i)) });
  };
  // Onboarding now runs AFTER login, so finishing it continues into the hearing.
  const finish = () => {
    if (!isCanvas) {
      patch({ onbSlide: 0 });
      nav.go('hearing', { replace: true });
    }
  };
  const next = () => {
    if (slide >= last) finish();
    else setSlide(slide + 1);
  };

  // ── swipe handling (pointer = touch + mouse) ──
  const onDown = (e) => {
    if (isCanvas) return;
    startX.current = e.clientX;
    widthRef.current = e.currentTarget.offsetWidth || 390;
    setDragging(true);
    e.currentTarget.setPointerCapture?.(e.pointerId);
  };
  const onMove = (e) => {
    if (startX.current == null) return;
    let dx = e.clientX - startX.current;
    // rubber-band resistance at the ends
    if ((slide === 0 && dx > 0) || (slide === last && dx < 0)) dx *= 0.32;
    setDrag(dx);
  };
  const onUp = () => {
    if (startX.current == null) return;
    const dx = drag,
      th = widthRef.current * 0.16;
    startX.current = null;
    setDragging(false);
    setDrag(0);
    if (dx <= -th) {
      slide >= last ? finish() : setSlide(slide + 1);
    } else if (dx >= th && slide > 0) {
      setSlide(slide - 1);
    }
  };

  const trackTransform = `translateX(calc(${-slide * 100}% + ${drag}px))`;

  return (
    <div className='ac-screen' style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column' }}>
      <div style={{ paddingTop: 47, padding: '47px 20px 0', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <button
          onClick={() => slide > 0 && setSlide(slide - 1)}
          style={{
            width: 44,
            height: 44,
            border: 'none',
            background: 'transparent',
            cursor: slide > 0 ? 'pointer' : 'default',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            opacity: slide > 0 ? 1 : 0,
            transition: 'opacity 0.2s',
          }}>
          <svg width='11' height='18' viewBox='0 0 11 18' fill='none'>
            <path d='M9 1L1 9l8 8' stroke={A.ink900} strokeWidth='2' strokeLinecap='round' strokeLinejoin='round' />
          </svg>
        </button>
        <button
          onClick={finish}
          style={{
            border: 'none',
            background: 'transparent',
            color: A.ink500,
            fontSize: 14,
            cursor: 'pointer',
            fontFamily: 'inherit',
            padding: '8px 4px',
          }}>
          スキップ
        </button>
      </div>

      {/* swipeable track */}
      <div
        onPointerDown={onDown}
        onPointerMove={onMove}
        onPointerUp={onUp}
        onPointerCancel={onUp}
        style={{
          flex: 1,
          overflow: 'hidden',
          touchAction: isCanvas ? 'auto' : 'pan-y',
          cursor: isCanvas ? 'default' : dragging ? 'grabbing' : 'grab',
        }}>
        <div
          style={{
            display: 'flex',
            height: '100%',
            transform: trackTransform,
            transition: dragging ? 'none' : 'transform 0.4s cubic-bezier(.22,.61,.36,1)',
          }}>
          {ONB_SLIDES.map((s, i) => (
            <div
              key={i}
              style={{
                flex: '0 0 100%',
                width: '100%',
                height: '100%',
                boxSizing: 'border-box',
                display: 'flex',
                flexDirection: 'column',
                alignItems: 'center',
                justifyContent: 'center',
                padding: '0 32px',
              }}>
              <div className={i === slide ? 'ac-art-float' : undefined} style={{ marginBottom: 48 }}>
                {s.art}
              </div>
              <h1
                className='ac-serif'
                style={{ margin: 0, fontSize: 28, fontWeight: 600, color: A.ink900, textAlign: 'center', letterSpacing: 1 }}>
                {s.heading}
              </h1>
              <p style={{ marginTop: 16, fontSize: 15, lineHeight: 1.7, color: A.ink500, textAlign: 'center', whiteSpace: 'pre-line' }}>
                {s.sub}
              </p>
            </div>
          ))}
        </div>
      </div>

      <div style={{ padding: '0 32px 40px' }}>
        <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', gap: 10, marginBottom: 30 }}>
          {ONB_SLIDES.map((_, i) => (
            <button
              key={i}
              onClick={() => setSlide(i)}
              aria-label={`slide ${i + 1}`}
              style={{
                width: 8,
                height: 8,
                borderRadius: '50%',
                padding: 0,
                border: 'none',
                cursor: 'pointer',
                background: i === slide ? A.gold500 : A.ink100,
                boxShadow: i === slide ? `0 0 0 4px ${A.gold100}` : 'none',
                transform: i === slide ? 'scale(1.05)' : 'scale(1)',
                transition: 'background 0.2s, box-shadow 0.2s, transform 0.2s',
              }}
            />
          ))}
        </div>
        <ACButton size='lg' onClick={next}>
          {slide >= last ? 'はじめる' : '次へ'}
        </ACButton>
      </div>
    </div>
  );
}

function OnbMoon() {
  return (
    <div style={{ position: 'relative', width: 180, height: 180, display: 'grid', placeItems: 'center' }}>
      <div
        className='ac-art-glow'
        style={{
          position: 'absolute',
          width: 168,
          height: 168,
          borderRadius: '50%',
          background: `radial-gradient(circle at 42% 40%, ${A.gold100} 0%, ${A.gold100} 38%, transparent 72%)`,
          pointerEvents: 'none',
        }}
      />
      <svg width='180' height='180' viewBox='0 0 180 180' style={{ position: 'relative' }}>
        <circle cx='90' cy='90' r='80' fill={A.gold100} />
        <path d='M120 50 Q70 70 70 110 Q70 140 110 145 Q85 130 85 100 Q85 70 120 50 Z' fill={A.gold500} />
        <circle className='ac-tw' cx='40' cy='50' r='2.4' fill={A.gold600} style={{ transformOrigin: '40px 50px', animationDelay: '0s' }} />
        <circle
          className='ac-tw'
          cx='150'
          cy='60'
          r='2.4'
          fill={A.gold600}
          style={{ transformOrigin: '150px 60px', animationDelay: '1.1s' }}
        />
        <circle
          className='ac-tw'
          cx='35'
          cy='130'
          r='2.4'
          fill={A.gold600}
          style={{ transformOrigin: '35px 130px', animationDelay: '2s' }}
        />
      </svg>
    </div>
  );
}
function OnbConvo() {
  return (
    <div style={{ position: 'relative', width: 240, height: 200, display: 'grid', placeItems: 'center' }}>
      {/* soft moon halo behind */}
      <div
        className='ac-art-glow'
        style={{
          position: 'absolute',
          top: '8%',
          left: '50%',
          transform: 'translateX(-50%)',
          width: 170,
          height: 170,
          borderRadius: '50%',
          background: `radial-gradient(circle at 40% 40%, ${A.gold100} 0%, ${A.gold100} 30%, transparent 70%)`,
          pointerEvents: 'none',
        }}
      />
      {/* tiny scattered stars */}
      <span className='ac-tw' style={{ position: 'absolute', left: '6%', top: '10%' }}>
        <ACStar x='0' y='0' size={5} opacity={0.6} />
      </span>
      <span className='ac-tw' style={{ position: 'absolute', left: '88%', top: '18%', animationDelay: '1.2s' }}>
        <ACStar x='0' y='0' size={6} opacity={0.7} />
      </span>
      <span className='ac-tw' style={{ position: 'absolute', left: '14%', top: '78%', animationDelay: '0.6s' }}>
        <ACStar x='0' y='0' size={5} opacity={0.5} />
      </span>
      <span className='ac-tw' style={{ position: 'absolute', left: '86%', top: '72%', animationDelay: '1.8s' }}>
        <ACStar x='0' y='0' size={5} opacity={0.6} />
      </span>
      <svg width='200' height='160' viewBox='0 0 200 160' style={{ position: 'relative', zIndex: 1 }}>
        <ellipse cx='100' cy='140' rx='90' ry='6' fill={A.ink100} opacity='0.5' />
        <circle cx='65' cy='55' r='20' fill={A.ink700} />
        <path d='M40 130 Q40 90 65 90 Q90 90 90 130' fill={A.ink700} />
        <circle cx='135' cy='60' r='22' fill={A.gold500} />
        <path d='M108 135 Q108 92 135 92 Q162 92 162 135' fill={A.gold500} />
        <path d='M88 70 L108 75' stroke={A.ink300} strokeWidth='2' strokeDasharray='3 3' />
      </svg>
    </div>
  );
}
function OnbWalk() {
  return (
    <svg width='200' height='160' viewBox='0 0 200 160'>
      <circle className='ac-art-glow' cx='155' cy='55' r='25' fill={A.gold500} style={{ transformOrigin: '155px 55px' }} />
      <circle cx='155' cy='55' r='25' fill={A.gold500} />
      <path d='M120 50 L155 50 M125 60 L165 55 M130 70 L160 65' stroke={A.gold500} strokeWidth='2' opacity='0.4' />
      <path d='M10 130 Q100 130 190 100' stroke={A.ink700} strokeWidth='2.5' fill='none' />
      <circle cx='65' cy='80' r='15' fill={A.ink700} />
      <path d='M50 130 L55 100 L65 90 L75 100 L80 130' fill={A.ink700} />
    </svg>
  );
}

// ═══════════════════════════════════════════════════════════════════
// A-03 Login — interactive form
// ═══════════════════════════════════════════════════════════════════
function A03Login() {
  const nav = useNav();
  const { state, patch } = useAppState();
  const tab = state.auth.tab;
  const [errors, setErrors] = React.useState({});
  const [loading, setLoading] = React.useState(false);
  const [successMsg, setSuccessMsg] = React.useState('');
  const containerRef = React.useRef(null);
  const [scrollable, setScrollable] = React.useState(false);
  const isMounted = React.useRef(true);
  
  React.useEffect(() => {
    isMounted.current = true;
    return () => { isMounted.current = false; };
  }, []);

  React.useLayoutEffect(() => {
    const check = () => {
      const el = containerRef.current;
      if (!el) return;
      const contentH = el.scrollHeight;
      const clientH = el.clientHeight;
      setScrollable(contentH > clientH + 2);
    };
    check();
    const el = containerRef.current;
    if (el) {
      const ro = new ResizeObserver(check);
      ro.observe(el);
      window.addEventListener('resize', check);
      return () => { ro.disconnect(); window.removeEventListener('resize', check); };
    }
  }, [tab, successMsg, errors]);

  const setField = (field, val) => {
    patch({ auth: { ...state.auth, [field]: val } });
    if (errors[field]) setErrors((e) => ({ ...e, [field]: null }));
    if (successMsg) setSuccessMsg('');
  };

  const submit = async () => {
    const newErrors = {};
    if (tab === 'signup') {
      if (!state.auth.firstName.trim()) newErrors.firstName = '名を入力してください';
      if (!state.auth.lastName.trim()) newErrors.lastName = '姓を入力してください';
    }
    if (!state.auth.email.includes('@')) newErrors.email = 'メールアドレスを正しく入力してください';
    if (state.auth.password.length < 8) newErrors.password = 'パスワードは8文字以上で入力してください';
    setErrors(newErrors);
    if (Object.keys(newErrors).length > 0) return;

    setLoading(true);
    setSuccessMsg('');
    try {
      if (tab === 'login') {
        await DB.signIn(state.auth.email, state.auth.password);
        // Navigation is handled by onAuthStateChange → routeForUser(),
        // which checks onboarding_complete and skips onboarding for returning users.
      } else {
        // Check if current user is a guest - if so, upgrade instead of creating new account
        const isGuest = state.currentUser?.user_metadata?.is_guest === true;
        if (isGuest) {
          try {
            // Show upgrading message
            setSuccessMsg('アカウントを作成中...');
            
            // Upgrade guest to full account - preserves all data!
            await DB.upgradeGuestToAccount(
              state.auth.email,
              state.auth.password,
              state.auth.firstName,
              state.auth.lastName
            );
            
            // Sign out so user can log in manually with new credentials
            await DB.signOut();
            
            // Success!
            setSuccessMsg('アカウントを作成しました。ログインしてください。');
            
            // Switch to Login tab, keep email, clear password & name fields
            patch({ 
              auth: { 
                ...state.auth, 
                tab: 'login', 
                password: '',      // Clear password for security
                firstName: '',     // Clear form fields
                lastName: ''       // Clear form fields
              } 
            });
          } catch (err) {
            console.error('Upgrade failed:', err);
            const code = err.code || '';
            let msg;
            if (code === 'email_exists' || code === 'user_already_exists') {
              msg = 'このメールアドレスはすでに登録されています。';
            } else {
              msg = err.message || 'アカウント作成に失敗しました';
            }
            setErrors({ email: msg });
          }
        } else {
          // Regular signup for non-guest users
          await DB.signUp(state.auth.email, state.auth.password, {
            firstName: state.auth.firstName,
            lastName: state.auth.lastName,
          });
          setSuccessMsg('確認メールを送信しました。メールをご確認ください。');
          patch({ auth: { ...state.auth, tab: 'login' } });
        }
      }
    } catch (err) {
      const code = err.code || '';
      const status = err.status || 0;
      let msg;
      if (code === 'over_email_send_rate_limit' || status === 429) {
        msg = 'メール送信の上限に達しました。しばらくしてからお試しください。';
      } else if (code === 'user_already_exists' || code === 'email_exists') {
        msg = 'このメールアドレスはすでに登録されています。';
      } else if (code === 'invalid_credentials') {
        msg = 'メールアドレスまたはパスワードが正しくありません。';
      } else {
        msg = err.message || 'エラーが発生しました';
      }
      if (tab === 'login') {
        setErrors({ password: msg });
      } else {
        setErrors({ email: msg });
      }
    } finally {
      if (isMounted.current) {
        setLoading(false);
      }
    }
  };

  const handleGoogleSignIn = async () => {
    setLoading(true);
    try {
      await DB.signInWithGoogle();
      // Native: handled by appUrlOpen deep link. Web: onAuthStateChange fires.
    } catch (err) {
      if (isMounted.current) {
        setErrors({ email: err.message || 'Googleログインに失敗しました' });
        setLoading(false);
      }
    }
  };

  const handleAppleSignIn = () => {
    patch({ toastMessage: '近日公開予定' });
  };

  const handleGuestSignIn = async () => {
    setLoading(true);
    try {
      const result = await DB.signInAsGuest();
      // Manually update state with the updated user data (includes guest name and is_guest flag)
      if (result?.user) {
        patch({ currentUser: result.user });
      }
      // Navigation is handled by onAuthStateChange → routeForUser()
    } catch (err) {
      if (isMounted.current) {
        setErrors({ email: err.message || 'ゲストログインに失敗しました' });
        setLoading(false);
      }
    }
  };

  const handleForgotPassword = async () => {
    if (!state.auth.email.includes('@')) {
      setErrors({ email: 'メールアドレスを入力してください' });
      return;
    }
    setLoading(true);
    try {
      await DB.sendPasswordReset(state.auth.email);
      if (isMounted.current) {
        setSuccessMsg('パスワードリセットメールを送信しました。');
      }
    } catch (err) {
      if (isMounted.current) {
        setErrors({ email: err.message || 'エラーが発生しました' });
      }
    } finally {
      if (isMounted.current) {
        setLoading(false);
      }
    }
  };

  

  return (
    <div
      ref={containerRef}
      className={'ac-screen' + (scrollable ? ' ac-scroll' : '')}
      style={{
        width: '100%',
        height: '100%',
        display: 'flex',
        flexDirection: 'column',
        padding: '0 28px',
        background: A.paper,
        boxSizing: 'border-box',
        overflow: scrollable ? 'auto' : 'hidden',
        position: 'relative',
      }}>
      <div style={{ flex: 2, minHeight: 16 }} />
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6, marginBottom: 'min(24px, 3vh)' }}>
        <span
          className='ac-serif'
          style={{ fontSize: 10, fontWeight: 400, color: A.ink500, letterSpacing: '0.25em', marginLeft: '0.25em' }}>
          アンコード
        </span>
        <span
          style={{
            fontSize: 22,
            fontWeight: 400,
            color: A.ink700,
            letterSpacing: '0.2em',
            lineHeight: 1,
            marginLeft: '0.2em',
            fontFamily: '"Cormorant Garamond", "EB Garamond", "Noto Serif JP", Georgia, serif',
          }}>
          NAVIYELL
        </span>
      </div>

      <div style={{ display: 'flex', borderBottom: `1px solid ${A.ink100}`, marginBottom: 'min(22px, 2.5vh)' }}>
        {[
          { id: 'login', label: 'ログイン' },
          { id: 'signup', label: '新規登録' },
        ].map((t) => (
          <button
            key={t.id}
            onClick={() => patch({ auth: { ...state.auth, tab: t.id } })}
            style={{
              flex: 1,
              padding: '10px 0',
              border: 'none',
              background: 'transparent',
              fontSize: 15,
              fontWeight: 600,
              fontFamily: 'inherit',
              color: tab === t.id ? A.ink900 : A.ink500,
              borderBottom: tab === t.id ? `2px solid ${A.gold500}` : '2px solid transparent',
              marginBottom: -1,
              cursor: 'pointer',
            }}>
            {t.label}
          </button>
        ))}
      </div>

      {successMsg && (
        <div
          style={{
            background: A.gold100,
            border: `1px solid ${A.gold300}`,
            borderRadius: 10,
            padding: '10px 14px',
            marginBottom: 12,
            fontSize: 13,
            color: A.gold700 || A.gold600,
          }}>
          {successMsg}
        </div>
      )}

      <div style={{ display: 'flex', flexDirection: 'column', gap: 'min(16px, 2vh)' }}>
        {tab === 'signup' && (
          <div style={{ display: 'flex', gap: 10 }}>
            <div style={{ flex: 1, minWidth: 0 }}>
              <ACInput label="姓" placeholder="山田"
              value={state.auth.lastName} onChange={(v) => setField('lastName', v)} error={errors.lastName} />
            </div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <ACInput label="名" placeholder="太郎"
              value={state.auth.firstName} onChange={(v) => setField('firstName', v)} error={errors.firstName} />
            </div>
          </div>
        )}
        <ACInput label="メールアドレス" placeholder="example@naviyell.jp" type="email"
        value={state.auth.email} onChange={(v) => setField('email', v)} error={errors.email} />
        <ACInput label="パスワード" type="password" placeholder="8文字以上"
        value={state.auth.password} onChange={(v) => setField('password', v)} error={errors.password} />
        <div style={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center', marginTop: -4 }}>
          <button
            onClick={handleForgotPassword}
            style={{
              border: 'none',
              background: 'transparent',
              color: A.gold600,
              fontSize: 13,
              cursor: 'pointer',
              fontFamily: 'inherit',
              padding: 0,
            }}>
            パスワードを忘れた方
          </button>
          
        </div>
        <ACButton size='lg' style={{ marginTop: 8, opacity: loading ? 0.6 : 1 }} onClick={loading ? undefined : submit}>
          {loading ? (
            <span style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
              <svg width='20' height='20' viewBox='0 0 24 24' style={{ animation: 'ac-spin 1s linear infinite' }}>
                <circle cx='12' cy='12' r='10' stroke='currentColor' strokeWidth='3' fill='none' strokeDasharray='31.4' strokeLinecap='round' />
              </svg>
            </span>
          ) : tab === 'login' ? 'ログイン' : '登録する'}
        </ACButton>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 12, margin: 'min(20px, 2.5vh) 0' }}>
        <div style={{ flex: 1, height: 1, background: A.ink100 }} />
        <span style={{ fontSize: 12, color: A.ink500 }}>または</span>
        <div style={{ flex: 1, height: 1, background: A.ink100 }} />
      </div>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 12, marginBottom: 16 }}>
        <SocialBtn brand='google' onClick={loading ? undefined : handleGoogleSignIn} />
        <SocialBtn brand='apple' onClick={loading ? undefined : handleAppleSignIn} />
      </div>

      <button
        onClick={loading ? undefined : handleGuestSignIn}
        style={{
          width: '100%',
          height: 52,
          borderRadius: 14,
          border: `1.5px solid ${A.ink100}`,
          background: 'transparent',
          color: A.ink700,
          fontSize: 15,
          fontWeight: 600,
          cursor: loading ? 'default' : 'pointer',
          fontFamily: 'inherit',
          transition: 'all 0.15s',
          opacity: loading ? 0.6 : 1,
          marginTop: 16,
        }}>
        ゲストとして続ける
      </button>

      <div style={{ flex: 1, minHeight: 8 }} />
      <div style={{ textAlign: 'center', fontSize: 11, color: A.ink500, lineHeight: 1.8, marginBottom: 16 }}>
        <button
          onClick={() => nav.go('terms-of-service')}
          style={{
            background: 'transparent',
            border: 'none',
            color: A.ink500,
            fontSize: 11,
            textDecoration: 'underline',
            cursor: 'pointer',
            fontFamily: 'inherit',
            padding: 0,
          }}>
          利用規約
        </button>
        <span style={{ margin: '0 8px' }}>·</span>
        <button
          onClick={() => nav.go('privacy-policy')}
          style={{
            background: 'transparent',
            border: 'none',
            color: A.ink500,
            fontSize: 11,
            textDecoration: 'underline',
            cursor: 'pointer',
            fontFamily: 'inherit',
            padding: 0,
          }}>
          プライバシーポリシー
        </button>
      </div>
      <Toast />
    </div>
  );
}

function SocialBtn({ brand, onClick }) {
  const labels = { apple: 'Appleで続ける', google: 'Googleで続ける' };
  return (
    <button
      onClick={onClick}
      style={{
        height: 52,
        borderRadius: 14,
        border: `1.5px solid ${A.ink100}`,
        background: A.white,
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        gap: 10,
        fontSize: 15,
        fontWeight: 600,
        color: A.ink900,
        cursor: onClick ? 'pointer' : 'default',
        fontFamily: 'inherit',
      }}>
      {brand === 'apple' ? (
        <svg width='18' height='18' viewBox='0 0 24 24' fill={A.ink900}>
          <path d='M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z' />
        </svg>
      ) : (
        <svg width='18' height='18' viewBox='0 0 24 24'>
          <path
            d='M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z'
            fill='#4285f4'
          />
          <path
            d='M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z'
            fill='#34a853'
          />
          <path
            d='M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z'
            fill='#fbbc05'
          />
          <path
            d='M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z'
            fill='#ea4335'
          />
        </svg>
      )}
      {labels[brand]}
    </button>
  );
}

// ═══════════════════════════════════════════════════════════════════
// A-04 Initial Hearing — picks an option, advances
// ═══════════════════════════════════════════════════════════════════
function A04Hearing({ step: stepProp }) {
  const nav = useNav();
  const { state, patch } = useAppState();
  const step = stepProp !== undefined ? stepProp : state.hearingStep;

  const allSteps = [
    {
      q: '今、一番気になっていることは？',
      opts: [
        { id: 'work', label: '仕事・キャリア', icon: '💼' },
        { id: 'rel', label: '人間関係', icon: '👥' },
        { id: 'hth', label: '健康・体調', icon: '🌱' },
        { id: 'mny', label: 'お金・将来', icon: '💰' },
        { id: 'self', label: '自分自身のこと', icon: '🪷' },
      ],
    },
    {
      q: 'どんなサポートを求めていますか？',
      opts: [
        { id: 'org', label: 'まず気持ちを整理したい', icon: '📝' },
        { id: 'adv', label: '具体的なアドバイスがほしい', icon: '🧭' },
        { id: 'acc', label: '行動を継続できるよう伴走してほしい', icon: '🤝' },
        { id: 'typ', label: '今はわからない', icon: '✨' },
      ],
    },
    {
      q: '相談の頻度はどのくらいを希望しますか？',
      opts: [
        { id: 'one', label: 'まずは1回試したい', icon: '☘️' },
        { id: 'mon', label: '月に1〜2回', icon: '🗓️' },
        { id: 'wk', label: '週1回ペースで継続', icon: '⏱️' },
        { id: 'asn', label: '必要なときだけ', icon: '🌙' },
      ],
    },
    {
      q: 'どの形式が一番話しやすいですか？',
      opts: [
        { id: 'chat', label: 'チャット（テキスト）', icon: '💬' },
        { id: 'voice', label: '音声通話', icon: '🎙️' },
        { id: 'video', label: 'ビデオ通話', icon: '📹' },
      ],
    },
  ];

  const s = allSteps[step - 1];
  const selected = state.hearing[step];

  const choose = (id) => patch({ hearing: { ...state.hearing, [step]: id } });
  const next = () => {
    if (step >= 4) {
      patch({ hearingStep: 1 });
      nav.go('hearing-transition', { replace: true });
    } else {
      patch({ hearingStep: step + 1 });
    }
  };

  return (
    <div className='ac-screen' style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column' }}>
      <div style={{ paddingTop: 47, padding: '47px 8px 0', flexShrink: 0 }}>
        <ACBack
          onClick={() => {
            if (step > 1) patch({ hearingStep: step - 1 });
            else nav.go('onboarding', { replace: true, dir: 'back' });
          }}
        />
      </div>
      <div style={{ padding: '4px 20px 16px' }}>
        <div style={{ height: 4, background: A.ink100, borderRadius: 2, overflow: 'hidden' }}>
          <div style={{ height: '100%', width: `${(step / 4) * 100}%`, background: A.gold500, transition: 'width 0.3s' }} />
        </div>
        <div style={{ marginTop: 8, fontSize: 12, color: A.ink500 }}>ステップ {step} / 4</div>
      </div>

      <div style={{ padding: '20px 24px 0' }}>
        <h1 className='ac-serif' style={{ margin: 0, fontSize: 22, fontWeight: 600, color: A.ink900, lineHeight: 1.5 }}>
          あなたについて
          <br />
          教えてください
        </h1>
        <p style={{ marginTop: 12, fontSize: 14, color: A.ink500 }}>{s.q}</p>
      </div>

      <div
        key={step}
        style={{
          flex: 1,
          padding: '24px 20px',
          display: 'flex',
          flexDirection: 'column',
          gap: 10,
          overflow: 'auto',
          animation: 'ac-fade-in 240ms ease both',
        }}
        className='ac-scroll'>
        {s.opts.map((o) => {
          const sel = selected === o.id;
          return (
            <button
              key={o.id}
              onClick={() => choose(o.id)}
              style={{
                background: A.white,
                border: `1.5px solid ${sel ? A.gold500 : A.ink100}`,
                borderRadius: 14,
                padding: '16px 18px',
                display: 'flex',
                alignItems: 'center',
                gap: 14,
                cursor: 'pointer',
                boxShadow: sel ? `0 0 0 3px ${A.gold100}` : 'none',
                fontFamily: 'inherit',
                textAlign: 'left',
                transition: 'all 0.15s',
              }}>
              <span style={{ fontSize: 22 }}>{o.icon}</span>
              <span style={{ fontSize: 15, fontWeight: 500, color: A.ink900 }}>{o.label}</span>
            </button>
          );
        })}
      </div>

      <ACStickyCTA>
        <ACButton size='lg' disabled={!selected} onClick={next}>
          次へ
        </ACButton>
      </ACStickyCTA>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// A-04c Transition — buffer between hearing and birthday
// ═══════════════════════════════════════════════════════════════════
function A04Transition() {
  const nav = useNav();
  const [cycleIdx, setCycleIdx] = React.useState(0);
  const [showText, setShowText] = React.useState(false);
  const elements = ['wind', 'fire', 'earth', 'water'];
  const el = ELEMENTS[elements[cycleIdx]];

  React.useEffect(() => {
    const t1 = setTimeout(() => setShowText(true), 100);
    const t2 = setTimeout(() => {
      nav.go('birthday', { replace: true });
    }, 2800);
    return () => {
      clearTimeout(t1);
      clearTimeout(t2);
    };
  }, [nav]);

  React.useEffect(() => {
    let interval;
    const startCycle = setTimeout(() => {
      interval = setInterval(() => {
        setCycleIdx((i) => (i + 1) % 4);
      }, 700);
    }, 1000);
    return () => {
      clearTimeout(startCycle);
      if (interval) clearInterval(interval);
    };
  }, []);

  return (
    <div
      className='ac-screen'
      style={{
        width: '100%',
        height: '100%',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center',
        background: `linear-gradient(180deg, ${A.ink700} 0%, ${A.ink900} 72%)`,
        position: 'relative',
        overflow: 'hidden',
      }}>
      <ACStar x='18%' y='15%' size={6} opacity={0.5} />
      <ACStar x='78%' y='22%' size={8} opacity={0.7} />
      <ACStar x='12%' y='78%' size={5} opacity={0.4} />
      <ACStar x='85%' y='70%' size={7} opacity={0.6} />
      <ACStar x='50%' y='88%' size={5} opacity={0.5} />

      <div style={{ textAlign: 'center' }}>
        <div
          style={{
            position: 'relative',
            width: 92,
            height: 92,
            margin: '0 auto 24px',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            opacity: showText ? 1 : 0,
            transform: showText ? 'translateY(0) scale(1)' : 'translateY(10px) scale(0.86)',
            transition: 'opacity 900ms 160ms ease, transform 1100ms 160ms cubic-bezier(.22,.61,.36,1)',
          }}>
          <div
            style={{
              position: 'absolute',
              inset: 0,
              borderRadius: '50%',
              background: el.color,
              opacity: showText ? 0.28 : 0,
              filter: 'blur(18px)',
              transform: showText ? 'scale(1.12)' : 'scale(0.75)',
              transition: 'background 320ms ease, opacity 1000ms 260ms ease, transform 1200ms 260ms cubic-bezier(.22,.61,.36,1)',
            }}
          />
          <div
            className='ac-serif'
            style={{
              position: 'relative',
              width: 72,
              height: 72,
              borderRadius: '50%',
              background: el.color,
              color: A.white,
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              fontSize: 30,
              fontWeight: 500,
              boxShadow: `0 4px 22px ${el.color}66`,
              transition: 'background 320ms ease, box-shadow 320ms ease',
            }}>
            {el.label}
          </div>
        </div>
        <div
          className='ac-serif'
          style={{
            fontSize: 13,
            color: A.gold500,
            fontWeight: 500,
            opacity: showText ? 1 : 0,
            transform: showText ? 'translateY(0)' : 'translateY(14px)',
            letterSpacing: showText ? '0.2em' : '0.42em',
            transition: 'all 1100ms 200ms cubic-bezier(.22,.61,.36,1)',
          }}>
          あなたのタイプを調べていきましょう
        </div>
        <div
          style={{
            display: 'flex',
            justifyContent: 'center',
            gap: 6,
            marginTop: 16,
            opacity: showText ? 1 : 0,
            transform: showText ? 'translateY(0)' : 'translateY(10px)',
            transition: 'all 800ms 600ms ease both',
          }}>
          {[0, 1, 2].map((i) => (
            <span
              key={i}
              style={{
                width: 6,
                height: 6,
                borderRadius: '50%',
                background: A.gold500,
                animation: showText ? `ac-twinkle 1.4s ${i * 0.2}s ease-in-out infinite` : 'none',
              }}
            />
          ))}
        </div>
      </div>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// A-04b Birthday — determines Born Inner Type (4 elements)
// ═══════════════════════════════════════════════════════════════════
function A04Birthday() {
  const nav = useNav();
  const { state, patch } = useAppState();
  const [year, setYear] = React.useState(state.birthYear ?? 1985);
  const [month, setMonth] = React.useState(state.birthMonth ?? 7);
  const [day, setDay] = React.useState(state.birthDay ?? 15);

  const Field = ({ label, value, onChange, min, max, digits, flex = 1 }) => {
    const [text, setText] = React.useState(String(value));
    React.useEffect(() => {
      setText(String(value));
    }, [value]);
    const commit = (raw) => {
      const n = parseInt(raw, 10);
      if (isNaN(n)) {
        setText(String(value));
        return;
      }
      const clamped = Math.max(min, Math.min(max, n));
      onChange(clamped);
      setText(String(clamped));
    };
    return (
      <div style={{ flex, minWidth: 0, display: 'flex', flexDirection: 'column' }}>
        <div style={{ fontSize: 10, color: A.ink500, fontWeight: 600, letterSpacing: 1, marginBottom: 6, textAlign: 'center' }}>
          {label}
        </div>
        <div
          style={{
            background: A.white,
            borderRadius: 12,
            border: `1px solid ${A.ink100}`,
            display: 'flex',
            alignItems: 'center',
            height: 64,
            padding: '0 4px',
            transition: 'border-color 120ms',
          }}>
          <button
            onClick={() => onChange(Math.max(min, value - 1))}
            aria-label='decrease'
            style={{
              width: 28,
              height: 28,
              borderRadius: 8,
              border: 'none',
              background: 'transparent',
              color: A.ink500,
              fontSize: 16,
              cursor: 'pointer',
              padding: 0,
              lineHeight: 1,
              flexShrink: 0,
            }}>
            −
          </button>
          <input
            type='text'
            inputMode='numeric'
            pattern='[0-9]*'
            value={text}
            maxLength={digits}
            onChange={(e) => setText(e.target.value.replace(/[^0-9]/g, ''))}
            onBlur={(e) => commit(e.target.value)}
            onKeyDown={(e) => {
              if (e.key === 'Enter') e.currentTarget.blur();
              else if (e.key === 'ArrowUp') {
                e.preventDefault();
                onChange(Math.min(max, value + 1));
              } else if (e.key === 'ArrowDown') {
                e.preventDefault();
                onChange(Math.max(min, value - 1));
              }
            }}
            onFocus={(e) => e.currentTarget.select()}
            className='ac-serif'
            style={{
              flex: 1,
              minWidth: 0,
              height: '100%',
              background: 'transparent',
              border: 'none',
              outline: 'none',
              fontSize: 24,
              fontWeight: 500,
              color: A.ink900,
              textAlign: 'center',
              padding: 0,
              fontFamily: A.serif,
            }}
          />

          <button
            onClick={() => onChange(Math.min(max, value + 1))}
            aria-label='increase'
            style={{
              width: 28,
              height: 28,
              borderRadius: 8,
              border: 'none',
              background: 'transparent',
              color: A.ink500,
              fontSize: 16,
              cursor: 'pointer',
              padding: 0,
              lineHeight: 1,
              flexShrink: 0,
            }}>
            +
          </button>
        </div>
      </div>
    );
  };

  const next = async () => {
    const bornType = bornTypeFromBirthday(month, day);
    patch({ birthYear: year, birthMonth: month, birthDay: day, bornType });
    
    // Save bornType to database so it persists after refresh
    const user = state.currentUser;
    if (user?.id && typeof DB !== 'undefined' && DB.updateProfile) {
      try {
        await DB.updateProfile({ born_type: bornType });
      } catch (err) {
        console.warn('Failed to save bornType:', err);
        // Non-critical - appState still has it for this session
      }
    }
    
    nav.go('type-reveal', { replace: true });
  };

  return (
    <div className='ac-screen' style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column' }}>
      <div style={{ paddingTop: 47, padding: '47px 8px 0', flexShrink: 0 }}>
        <ACBack fallback='hearing' />
      </div>
      <div style={{ padding: '4px 20px 16px' }}>
        <div style={{ height: 4, background: A.ink100, borderRadius: 2, overflow: 'hidden' }}>
          <div style={{ height: '100%', width: '20%', background: A.gold500 }} />
        </div>
        <div style={{ marginTop: 8, fontSize: 12, color: A.ink500 }}>STEP 1 · 生まれながらのサイン</div>
      </div>

      <div style={{ padding: '20px 24px 0' }}>
        <h1 className='ac-serif' style={{ margin: 0, fontSize: 24, fontWeight: 600, color: A.ink900, lineHeight: 1.5 }}>
          あなたが
          <br />
          生まれた日を
          <br />
          教えてください
        </h1>
        <p style={{ marginTop: 12, fontSize: 13, color: A.ink500, lineHeight: 1.7 }}>
          生まれた日には　あなた本来の
          <br />
          「インナータイプ」が宿っています
        </p>
      </div>

      <div style={{ padding: '24px 24px 0', display: 'flex', gap: 8 }}>
        <Field label='年 YEAR' value={year} onChange={setYear} min={1940} max={2010} digits={4} flex={1.4} />
        <Field label='月 MONTH' value={month} onChange={setMonth} min={1} max={12} digits={2} flex={1} />
        <Field label='日 DAY' value={day} onChange={setDay} min={1} max={31} digits={2} flex={1} />
      </div>
      <div style={{ padding: '8px 24px 0', fontSize: 11, color: A.ink500, lineHeight: 1.5 }}>
        数字をタップして直接入力、または <strong style={{ color: A.ink700, fontWeight: 600 }}>− / +</strong> ボタンで調整できます
      </div>

      <div style={{ flex: 1 }} />

      <ACStickyCTA>
        <ACButton size='lg' onClick={next}>
          次へ
        </ACButton>
      </ACStickyCTA>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// A-04d Type Reveal — dramatic reveal of Born Inner Type after birthday
// ═══════════════════════════════════════════════════════════════════
function A04TypeReveal() {
  const nav = useNav();
  const { state } = useAppState();
  const bornType = state.bornType || bornTypeFromBirthday(state.birthMonth || 7, state.birthDay || 15);
  const el = ELEMENTS[bornType];
  const [showLabel, setShowLabel] = React.useState(false);
  const [showCircle, setShowCircle] = React.useState(false);
  const [showTitle, setShowTitle] = React.useState(false);
  const [showSub, setShowSub] = React.useState(false);
  const [showDots, setShowDots] = React.useState(false);
  const [showBtn, setShowBtn] = React.useState(false);

  React.useEffect(() => {
    const t1 = setTimeout(() => setShowLabel(true), 200);
    const t2 = setTimeout(() => setShowCircle(true), 1200);
    const t3 = setTimeout(() => setShowTitle(true), 2200);
    const t4 = setTimeout(() => setShowSub(true), 3000);
    const t5 = setTimeout(() => setShowDots(true), 3800);
    const t6 = setTimeout(() => setShowBtn(true), 4200);
    return () => {
      clearTimeout(t1);
      clearTimeout(t2);
      clearTimeout(t3);
      clearTimeout(t4);
      clearTimeout(t5);
      clearTimeout(t6);
    };
  }, []);

  const s = (v) => (v ? '1' : '0');

  return (
    <div
      className='ac-screen'
      style={{
        width: '100%',
        height: '100%',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center',
        background: `radial-gradient(ellipse at 50% 38%, ${A.ink700} 0%, ${A.ink900} 72%)`,
        position: 'relative',
        overflow: 'hidden',
      }}>
      <ACStar x='18%' y='15%' size={6} opacity={0.5} />
      <ACStar x='78%' y='22%' size={8} opacity={0.7} />
      <ACStar x='12%' y='78%' size={5} opacity={0.4} />
      <ACStar x='85%' y='70%' size={7} opacity={0.6} />
      <ACStar x='50%' y='88%' size={5} opacity={0.5} />
      <ACStar x='30%' y='35%' size={4} opacity={0.3} />
      <ACStar x='65%' y='45%' size={4} opacity={0.3} />

      <div style={{ textAlign: 'center' }}>
        <div
          className='ac-serif'
          style={{
            fontSize: 11,
            color: A.gold500,
            marginBottom: 22,
            fontWeight: 500,
            opacity: s(showLabel),
            transform: showLabel ? 'translateY(0)' : 'translateY(14px)',
            letterSpacing: showLabel ? '0.2em' : '0.42em',
            transition: 'all 1100ms 200ms cubic-bezier(.22,.61,.36,1)',
          }}>
          あなたの生まれながらのサイン
        </div>

        <div
          style={{
            width: 96,
            height: 96,
            borderRadius: '50%',
            margin: '0 auto 28px',
            background: el.color,
            color: A.white,
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            fontSize: 38,
            fontWeight: 500,
            transform: showCircle ? 'scale(1)' : 'scale(0.4)',
            opacity: s(showCircle),
            transition: 'all 1200ms 200ms cubic-bezier(.22,.61,.36,1)',
          }}>
          {el.label}
        </div>

        <div
          className='ac-serif'
          style={{
            fontSize: 22,
            color: A.white,
            fontWeight: 600,
            letterSpacing: 2,
            marginBottom: 10,
            opacity: s(showTitle),
            transform: showTitle ? 'translateY(0)' : 'translateY(14px)',
            transition: 'all 1000ms 200ms cubic-bezier(.22,.61,.36,1)',
          }}>
          {el.label}のサイン
        </div>

        <div
          className='ac-serif'
          style={{
            fontSize: 13,
            color: A.gold500,
            letterSpacing: 4,
            marginBottom: 28,
            opacity: s(showSub),
            transform: showSub ? 'translateY(0)' : 'translateY(12px)',
            transition: 'all 1000ms 200ms cubic-bezier(.22,.61,.36,1)',
          }}>
          {el.labelEn}
        </div>

        <div
          style={{
            display: 'flex',
            justifyContent: 'center',
            gap: 6,
            opacity: s(showDots),
            transform: showDots ? 'translateY(0)' : 'translateY(10px)',
            transition: 'all 800ms 200ms ease both',
          }}>
          {[0, 1, 2].map((i) => (
            <span
              key={i}
              style={{
                width: 6,
                height: 6,
                borderRadius: '50%',
                background: A.gold500,
                animation: showDots ? `ac-twinkle 1.4s ${i * 0.2}s ease-in-out infinite` : 'none',
              }}
            />
          ))}
        </div>
      </div>

      <div
        style={{
          position: 'absolute',
          bottom: 40,
          left: 0,
          right: 0,
          display: 'flex',
          justifyContent: 'center',
          padding: '0 24px',
          opacity: s(showBtn),
          transform: showBtn ? 'translateY(0)' : 'translateY(12px)',
          pointerEvents: showBtn ? 'auto' : 'none',
          transition: 'all 800ms 200ms ease both',
        }}>
        <button
          onClick={() => nav.go('diagnosis-intro', { replace: true })}
          style={{
            width: '100%',
            maxWidth: 320,
            height: 50,
            borderRadius: 12,
            border: `1.5px solid ${A.gold500}`,
            background: 'transparent',
            color: A.gold500,
            fontSize: 16,
            fontWeight: 600,
            cursor: 'pointer',
            fontFamily: 'inherit',
            letterSpacing: 2,
            transition: 'all 0.15s',
          }}>
          次へ
        </button>
      </div>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// A-04e Diagnosis Intro — buffer between type reveal and 30 questions
// ═══════════════════════════════════════════════════════════════════
function A04DiagnosisIntro() {
  const nav = useNav();
  const [show, setShow] = React.useState(false);

  React.useEffect(() => {
    const t1 = setTimeout(() => setShow(true), 100);
    const t2 = setTimeout(() => {
      nav.go('diagnosis', { replace: true });
    }, 2800);
    return () => {
      clearTimeout(t1);
      clearTimeout(t2);
    };
  }, [nav]);

  return (
    <div
      className='ac-screen'
      style={{
        width: '100%',
        height: '100%',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center',
        background: `linear-gradient(180deg, ${A.ink700} 0%, ${A.ink900} 72%)`,
        position: 'relative',
        overflow: 'hidden',
      }}>
      <ACStar x='18%' y='15%' size={6} opacity={0.5} />
      <ACStar x='78%' y='22%' size={8} opacity={0.7} />
      <ACStar x='12%' y='78%' size={5} opacity={0.4} />
      <ACStar x='85%' y='70%' size={7} opacity={0.6} />
      <ACStar x='50%' y='88%' size={5} opacity={0.5} />

      <div style={{ textAlign: 'center' }}>
        <div
          className='ac-serif'
          style={{
            fontSize: 20,
            color: A.white,
            fontWeight: 600,
            marginBottom: 16,
            lineHeight: 1.6,
            opacity: show ? 1 : 0,
            transform: show ? 'translateY(0)' : 'translateY(14px)',
            letterSpacing: show ? '0.04em' : '0.18em',
            transition: 'all 1100ms 200ms cubic-bezier(.22,.61,.36,1)',
          }}>
          30個の簡単な質問に答えると
          <br />
          あなたのインナータイプが出ます
        </div>
        <div
          style={{
            display: 'flex',
            justifyContent: 'center',
            gap: 6,
            marginTop: 8,
            opacity: show ? 1 : 0,
            transition: 'opacity 800ms 1200ms ease both',
          }}>
          {[0, 1, 2].map((i) => (
            <span
              key={i}
              style={{
                width: 6,
                height: 6,
                borderRadius: '50%',
                background: A.gold500,
                animation: show ? `ac-twinkle 1.4s ${i * 0.2}s ease-in-out infinite` : 'none',
              }}
            />
          ))}
        </div>
      </div>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// A-04f Result Intro — buffer between 30 questions and result
// ═══════════════════════════════════════════════════════════════════
function A05ResultIntro() {
  const nav = useNav();
  const [show, setShow] = React.useState(false);

  React.useEffect(() => {
    const t1 = setTimeout(() => setShow(true), 100);
    const t2 = setTimeout(() => {
      nav.go('result', { replace: true });
    }, 2800);
    return () => {
      clearTimeout(t1);
      clearTimeout(t2);
    };
  }, [nav]);

  return (
    <div
      className='ac-screen'
      style={{
        width: '100%',
        height: '100%',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center',
        background: `linear-gradient(180deg, ${A.ink700} 0%, ${A.ink900} 72%)`,
        position: 'relative',
        overflow: 'hidden',
      }}>
      <ACStar x='18%' y='15%' size={6} opacity={0.5} />
      <ACStar x='78%' y='22%' size={8} opacity={0.7} />
      <ACStar x='12%' y='78%' size={5} opacity={0.4} />
      <ACStar x='85%' y='70%' size={7} opacity={0.6} />
      <ACStar x='50%' y='88%' size={5} opacity={0.5} />

      <div style={{ textAlign: 'center' }}>
        <div
          className='ac-serif'
          style={{
            fontSize: 15,
            color: A.gold500,
            fontWeight: 500,
            lineHeight: 1.7,
            margin: 0,
            opacity: show ? 1 : 0,
            transform: show ? 'translateY(0)' : 'translateY(14px)',
            letterSpacing: show ? '0.15em' : '0.35em',
            transition: 'all 1100ms 200ms cubic-bezier(.22,.61,.36,1)',
          }}>
          ここからあなたのタイプに
          <br />
          合う専門家を見つけていきましょう
        </div>
        <div
          style={{
            display: 'flex',
            justifyContent: 'center',
            gap: 6,
            marginTop: 24,
            opacity: show ? 1 : 0,
            transition: 'opacity 800ms 1200ms ease both',
          }}>
          {[0, 1, 2].map((i) => (
            <span
              key={i}
              style={{
                width: 6,
                height: 6,
                borderRadius: '50%',
                background: A.gold500,
                animation: show ? `ac-twinkle 1.4s ${i * 0.2}s ease-in-out infinite` : 'none',
              }}
            />
          ))}
        </div>
      </div>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// A-05 Diagnosis — 30-question Inner Type Guide (Wind / Fire / Earth)
// ═══════════════════════════════════════════════════════════════════
function A05Diagnosis() {
  const nav = useNav();
  const { state, patch } = useAppState();
  const q = state.diagQ;
  const total = DIAG_QUESTIONS.length; // 30
  const item = DIAG_QUESTIONS[q - 1];

  const selected = state.diagnosis[q];
  const choose = (i) => {
    const next = { ...state.diagnosis, [q]: i };
    patch({ diagnosis: next });
    setTimeout(() => {
      if (q >= total) {
        patch({ diagQ: 1 });
        nav.go('result-intro', { replace: true });
      } else {
        patch({ diagQ: q + 1 });
      }
    }, 280);
  };

  // Back: step through previous questions; from Q1, back to Birthday
  const goBack = () => {
    if (q > 1) {
      patch({ diagQ: q - 1 });
    } else {
      nav.go('birthday', { replace: true, dir: 'back' });
    }
  };

  // Tap on progress to skip ahead with random answers (demo helper)
  const skipAhead = (e) => {
    e.stopPropagation();
    const filled = { ...state.diagnosis };
    for (let i = 1; i <= total; i++) {
      if (filled[i] == null) filled[i] = Math.floor(Math.random() * 3);
    }
    patch({ diagnosis: filled, diagQ: 1 });
    nav.go('result-intro', { replace: true });
  };

  return (
    <div className='ac-screen' style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column' }}>
      <ACTopBar
        leading={<ACBack onClick={goBack} />}
        title='インナータイプガイド'
        trailing={
          <div style={{ fontSize: 13, color: A.ink500, paddingRight: 8 }}>
            {q} / {total}
          </div>
        }
      />

      <div style={{ padding: '16px 24px 0' }}>
        <div style={{ height: 3, background: A.ink100, borderRadius: 2, overflow: 'hidden' }}>
          <div style={{ height: '100%', width: `${(q / total) * 100}%`, background: A.gold500, transition: 'width 0.3s' }} />
        </div>
      </div>

      <div
        key={q}
        style={{
          flex: 1,
          padding: '28px 24px 16px',
          display: 'flex',
          flexDirection: 'column',
          overflow: 'auto',
          animation: 'ac-fade-in 240ms ease both',
        }}
        className='ac-scroll'>
        <p style={{ fontSize: 11, color: A.gold600, fontWeight: 700, letterSpacing: 2, margin: 0 }}>
          QUESTION {String(q).padStart(2, '0')}
        </p>
        <h2 className='ac-serif' style={{ margin: '8px 0 0', fontSize: 21, fontWeight: 500, color: A.ink900, lineHeight: 1.6 }}>
          {item.q}
        </h2>

        <div style={{ marginTop: 28, display: 'flex', flexDirection: 'column', gap: 10 }}>
          {item.opts.map((label, i) => {
            const sel = selected === i;
            return (
              <button
                key={i}
                onClick={() => choose(i)}
                style={{
                  background: A.white,
                  border: `1.5px solid ${sel ? A.gold500 : A.ink100}`,
                  borderRadius: 14,
                  padding: '16px 18px',
                  fontSize: 14.5,
                  color: A.ink900,
                  fontWeight: sel ? 600 : 500,
                  boxShadow: sel ? `0 0 0 3px ${A.gold100}` : 'none',
                  cursor: 'pointer',
                  fontFamily: 'inherit',
                  textAlign: 'left',
                  transition: 'all 0.15s',
                  lineHeight: 1.5,
                }}>
                {label}
              </button>
            );
          })}
        </div>

        {/* Neutral choice — for questions where none of the three fit */}
        <button
          onClick={() => choose('none')}
          style={{
            marginTop: 14,
            alignSelf: 'center',
            background: selected === 'none' ? A.gold100 : 'transparent',
            border: `1.5px ${selected === 'none' ? 'solid' : 'dashed'} ${selected === 'none' ? A.gold500 : A.ink300}`,
            borderRadius: 12,
            padding: '11px 22px',
            fontSize: 13.5,
            color: selected === 'none' ? A.ink900 : A.ink500,
            fontWeight: selected === 'none' ? 600 : 500,
            cursor: 'pointer',
            fontFamily: 'inherit',
            transition: 'all 0.15s',
          }}>
          どちらでもない / 選びにくい
        </button>

        <button
          onClick={skipAhead}
          style={{
            marginTop: 24,
            alignSelf: 'center',
            background: 'transparent',
            border: 'none',
            color: A.ink500,
            fontSize: 12,
            cursor: 'pointer',
            fontFamily: 'inherit',
            textDecoration: 'underline',
            textUnderlineOffset: 3,
          }}>
          デモ用：残りをランダム回答してスキップ
        </button>
      </div>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// A-05 Result — Born × Current dual-sign comparison + AI message
// ═══════════════════════════════════════════════════════════════════
function A05Result() {
  const nav = useNav();
  const { state, patch } = useAppState();

  // Born type from birthday (fall back to mock default)
  const bornType = state.bornType || bornTypeFromBirthday(state.birthMonth || 7, state.birthDay || 15);
  const born = ELEMENTS[bornType];

  // Current sign from 30Q answers
  const sign = computeCurrentSign(state.diagnosis || {});
  const currentDom = sign.dominant;

  // Save to user state on first render
  React.useEffect(() => {
    patch({
      bornType,
      currentDom,
      currentMix: { wind: sign.wind, fire: sign.fire, earth: sign.earth },
      // Update displayed type label everywhere else (home, etc.)
      type: bornType,
      typeLabel: `${born.label}のサイン`,
      typeDesc: born.desc,
    });
    // eslint-disable-next-line
  }, []);

  const message = pairMessage(bornType, currentDom);

  // Pretty label for the current sign — uses サイン (not タイプ) per design spec
  const currentLabel =
    currentDom === 'trinity'
      ? '風・火・土の調和'
      : currentDom
          .split('+')
          .map((k) => ELEMENTS[k].label)
          .join('・');
  // Short label inside the circle (no spaces, no plus)
  const currentGlyph =
    currentDom === 'trinity'
      ? '調和'
      : currentDom
          .split('+')
          .map((k) => ELEMENTS[k].label)
          .join('');
  const currentSignLabel = currentDom === 'trinity' ? '調和のサイン' : `${currentLabel}のサイン`;

  // Recommended categories based on born type
  const RECS = {
    wind: ['心のアンカー', '未来のアンカー', '社会的なアンカー'],
    fire: ['未来のアンカー', '社会的なアンカー', '体のアンカー'],
    earth: ['社会的なアンカー', '体のアンカー', '心のアンカー'],
    water: ['心のアンカー', '体のアンカー', '未来のアンカー'],
  };

  return (
    <div className='ac-screen' style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column' }}>
      {/* Hero */}
      <div
        style={{
          position: 'relative',
          background: `linear-gradient(180deg, ${A.ink700} 0%, ${A.ink900} 100%)`,
          padding: '60px 24px 36px',
          textAlign: 'center',
          overflow: 'hidden',
        }}>
        <ACStar x='14%' y='22%' size={5} />
        <ACStar x='82%' y='18%' size={6} />
        <ACStar x='20%' y='78%' size={4} />
        <ACStar x='86%' y='62%' size={5} />
        <div className='ac-serif' style={{ fontSize: 13, color: A.gold500, letterSpacing: 6, marginBottom: 22, fontWeight: 500 }}>
          あなたのインナータイプ
        </div>

        {/* Two element circles */}
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 14 }}>
          {/* Born */}
          <div style={{ textAlign: 'center', animation: 'ac-fade-in 600ms ease both', flex: '0 0 auto' }}>
            <div style={{ fontSize: 10.5, color: A.gold100, opacity: 0.9, letterSpacing: 1.5, marginBottom: 12, fontWeight: 500 }}>
              生まれながらのサイン
            </div>
            <div
              className='ac-serif'
              style={{
                width: 68,
                height: 68,
                borderRadius: '50%',
                margin: '0 auto',
                background: born.color,
                color: A.white,
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                fontSize: 30,
                fontWeight: 500,
                boxShadow: `0 4px 22px ${born.color}66`,
              }}>
              {born.label}
            </div>
          </div>

          {/* connector */}
          <div className='ac-serif' style={{ color: A.gold500, fontSize: 18, opacity: 0.9, padding: '0 2px', marginTop: 26 }}>
            ×
          </div>

          {/* Current */}
          <div style={{ textAlign: 'center', animation: 'ac-fade-in 800ms 200ms ease both', flex: '0 0 auto' }}>
            <div style={{ fontSize: 10.5, color: A.gold100, opacity: 0.9, letterSpacing: 1.5, marginBottom: 12, fontWeight: 500 }}>
              今のインナーサイン
            </div>
            <div
              className='ac-serif'
              style={{
                width: 68,
                height: 68,
                borderRadius: '50%',
                margin: '0 auto',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                background:
                  currentDom === 'trinity'
                    ? `conic-gradient(${ELEMENTS.wind.color} 0 33%, ${ELEMENTS.fire.color} 33% 66%, ${ELEMENTS.earth.color} 66%)`
                    : currentDom.includes('+')
                      ? `linear-gradient(135deg, ${ELEMENTS[currentDom.split('+')[0]].color}, ${ELEMENTS[currentDom.split('+')[1]].color})`
                      : ELEMENTS[currentDom].color,
                color: A.white,
                fontSize: currentGlyph.length >= 3 ? 16 : currentGlyph.length === 2 ? 22 : 30,
                fontWeight: 500,
                boxShadow: '0 4px 22px rgba(0,0,0,0.3)',
                letterSpacing: currentGlyph.length >= 3 ? 0 : 1,
              }}>
              {currentGlyph}
            </div>
          </div>
        </div>
      </div>

      {/* Body */}
      <div style={{ flex: 1, padding: '24px 24px 0', overflow: 'auto' }} className='ac-scroll'>
        {/* Current energy bar */}
        <div style={{ marginBottom: 24 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 10 }}>
            <h3 style={{ fontSize: 11, fontWeight: 700, color: A.ink500, letterSpacing: 1.5, margin: 0 }}>いまのエネルギーバランス</h3>
            <span style={{ fontSize: 10, color: A.ink500 }}>30問の回答より</span>
          </div>
          <div style={{ display: 'flex', height: 12, borderRadius: 6, overflow: 'hidden', background: A.ink100 }}>
            <div style={{ width: `${sign.wind}%`, background: ELEMENTS.wind.color, transition: 'width 0.6s' }} />
            <div style={{ width: `${sign.fire}%`, background: ELEMENTS.fire.color, transition: 'width 0.6s' }} />
            <div style={{ width: `${sign.earth}%`, background: ELEMENTS.earth.color, transition: 'width 0.6s' }} />
          </div>
          <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 8, fontSize: 11, color: A.ink700 }}>
            {['wind', 'fire', 'earth'].map((k) => (
              <div key={k} style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
                <span style={{ width: 8, height: 8, borderRadius: '50%', background: ELEMENTS[k].color }} />
                <span style={{ fontWeight: 500 }}>{ELEMENTS[k].label}</span>
                <span style={{ color: A.ink500 }}>{sign[k]}%</span>
              </div>
            ))}
          </div>
        </div>

        {/* AI personalized message */}
        <div
          style={{
            background: A.paper2,
            borderRadius: 14,
            padding: 16,
            borderLeft: `3px solid ${A.gold500}`,
          }}>
          <div style={{ fontSize: 10, color: A.gold600, fontWeight: 700, letterSpacing: 1.5, marginBottom: 8 }}>
            NAVIYELL AI からのメッセージ
          </div>
          <p style={{ margin: 0, fontSize: 13.5, lineHeight: 1.85, color: A.ink700 }}>{message}</p>
        </div>

        {/* Born type detail */}
        <div style={{ marginTop: 22, display: 'flex', gap: 12, alignItems: 'flex-start' }}>
          <div
            className='ac-serif'
            style={{
              flex: '0 0 auto',
              width: 36,
              height: 36,
              borderRadius: '50%',
              background: born.color,
              color: A.white,
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              fontSize: 17,
              fontWeight: 500,
              marginTop: 2,
            }}>
            {born.label}
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <h3 style={{ fontSize: 11, fontWeight: 700, color: A.ink500, letterSpacing: 1.5, margin: '0 0 6px' }}>
              生まれながらの「{born.label}のサイン」
            </h3>
            <p style={{ margin: 0, fontSize: 13, lineHeight: 1.85, color: A.ink700 }}>{born.desc}</p>
          </div>
        </div>

        {/* Current sign detail */}
        {currentDom !== 'trinity' && !currentDom.includes('+') && (
          <div style={{ marginTop: 16, display: 'flex', gap: 12, alignItems: 'flex-start' }}>
            <div
              className='ac-serif'
              style={{
                flex: '0 0 auto',
                width: 36,
                height: 36,
                borderRadius: '50%',
                background: ELEMENTS[currentDom].color,
                color: A.white,
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                fontSize: 17,
                fontWeight: 500,
                marginTop: 2,
              }}>
              {ELEMENTS[currentDom].label}
            </div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <h3 style={{ fontSize: 11, fontWeight: 700, color: A.ink500, letterSpacing: 1.5, margin: '0 0 6px' }}>
                いまの「{ELEMENTS[currentDom].label}のサイン」
              </h3>
              <p style={{ margin: 0, fontSize: 13, lineHeight: 1.85, color: A.ink700 }}>{ELEMENTS[currentDom].desc}</p>
            </div>
          </div>
        )}

        {/* Recommended categories */}
        <div style={{ marginTop: 22 }}>
          <h3 style={{ fontSize: 11, fontWeight: 700, color: A.ink500, letterSpacing: 1.5, margin: '0 0 10px' }}>おすすめのカテゴリー</h3>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
            {(RECS[bornType] || RECS.wind).map((c) => (
              <span
                key={c}
                style={{
                  padding: '8px 14px',
                  borderRadius: 20,
                  background: A.gold100,
                  color: A.gold600,
                  fontSize: 12.5,
                  fontWeight: 600,
                }}>
                {c}
              </span>
            ))}
          </div>
        </div>

        <div style={{ height: 16 }} />
      </div>

      <ACStickyCTA>
        <ACButton
          size='lg'
          onClick={async () => {
          const uid = state.currentUser?.id;
          if (uid) localStorage.setItem('ancord_onb_' + uid, '1');
          try {
          // ✅ PASS THE BIRTHDAY DATA HERE!
          await DB.completeOnboarding(
          state.birthYear,
          state.birthMonth,
          state.birthDay,
          state.bornType
          );
        } catch (e) {
        console.warn('[onboarding] metadata save failed:', e.message);
        }
        nav.go('home', { replace: true });
        }}>
        ホームへ
      </ACButton>
      </ACStickyCTA>
    </div>
  );
}

Object.assign(window, {
  A01Splash,
  A02Onboarding,
  A03Login,
  A04Hearing,
  A04Transition,
  A04Birthday,
  A04TypeReveal,
  A04DiagnosisIntro,
  A05ResultIntro,
  A05Diagnosis,
  A05Result,
});
