/* Mission Completion Flow — 3 steps plus submit countdown. */

function StepHeader({ step, total, title, onBack }) {
  return (
    <div style={{padding:'4px 20px 14px', borderBottom:'1px solid var(--line)'}}>
      <div style={{display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:10}}>
        <button onClick={onBack} style={{
          background:'transparent', border:0, padding:0, cursor:'pointer',
          color:'var(--ink)', display:'inline-flex', alignItems:'center',
        }}>
          <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
            <path d="M12 4 L6 10 L12 16" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
        </button>
        <span style={{fontSize:12, color:'var(--ink-3)', fontWeight:500}}>{step} of {total}</span>
        <span style={{width:20}}/>
      </div>
      {/* progress segments */}
      <div style={{display:'flex', gap:4, marginBottom:14}}>
        {Array.from({length: total}).map((_, i) => (
          <div key={i} style={{
            flex:1, height:3, borderRadius:2,
            background: i < step ? 'var(--teal)' : 'var(--bg-sunk)',
          }}/>
        ))}
      </div>
      <div style={{fontSize:20, fontWeight:700, letterSpacing:'-0.02em', lineHeight:1.2}}>
        {title}
      </div>
    </div>
  );
}

/* ---- Step 1: Route confirmation ---- */
function StepRoute({ mission, onNext, onBack }) {
  const fallbackMission = {
    type:'verify',
    title:'Mori Tea House',
    distance:'120m',
    cp:40,
    daysAgo:243,
  };
  const m = mission || fallbackMission;
  const rawType = String(m.type || 'verify').toLowerCase();
  const typeKey = rawType.includes('add-place') ? 'add-place'
    : rawType.includes('update-place') ? 'update-place'
    : rawType.includes('add-review') ? 'add-review'
    : rawType.includes('add-photo') ? 'add-photo'
    : rawType.includes('update-road') ? 'update-road'
    : rawType.includes('update-address') ? 'update-address'
    : rawType.includes('campaign') ? 'campaign'
    : rawType.includes('first') ? 'first-look'
    : rawType.includes('refresh') ? 'refresh'
    : rawType.includes('angle') ? 'angle'
    : rawType.includes('route') ? 'route'
    : 'verify';
  const typeInfo = {
    verify: {
      label:'Verify mission',
      title:'Confirm a place is still accurate.',
      body:'Answer the current ground-truth question and capture proof if something changed.',
    },
    'first-look': {
      label:'First Look mission',
      title:'Be the first mapper for a new place.',
      body:'Capture enough context for others to understand what is here and why it matters.',
    },
    refresh: {
      label:'Refresh mission',
      title:'Update stale place data.',
      body:'Check hours, photos, menus, or details that may have changed since the last visit.',
    },
    angle: {
      label:'Angle mission',
      title:'Complete missing visual coverage.',
      body:'Capture the requested perspective so the POI has a clearer, more useful record.',
    },
    route: {
      label:'Route mission',
      title:'Complete a multi-stop route.',
      body:'Follow the planned stops and contribute a connected trail of local updates.',
    },
    'add-place': {
      label:'Add Place',
      title:'Create a new POI record.',
      body:'Set the exact location, add the name/category, then capture required photo evidence.',
    },
    'update-place': {
      label:'Update Place',
      title:'Correct stale POI data.',
      body:'Change hours, closure, signage, or details and attach photo evidence for AI comparison.',
    },
    'add-review': {
      label:'Add Review',
      title:'Turn a visit into structured data.',
      body:'Rate the place, write a concise review, and add amenity tags that improve the data layer.',
    },
    'add-photo': {
      label:'Add Photo',
      title:'Improve visual coverage.',
      body:'Use the AI framing guide to capture storefront, signage, menu, or interior context.',
    },
    'update-road': {
      label:'Update Road',
      title:'Report a road condition change.',
      body:'Tap the affected segment, describe the issue, and verify it with timestamped evidence.',
    },
    'update-address': {
      label:'Update Address',
      title:'Fix address or pin accuracy.',
      body:'Correct the address, street number, unit, or pin and capture signage proof.',
    },
    campaign: {
      label:'Campaign mission',
      title:'Complete a paid survey request.',
      body:'Enterprise-funded missions pay higher GP after location and evidence checks pass.',
    },
  }[typeKey];
  const dismissKey = `govibe.missionTypeTip.v2.${typeKey}`;
  const [showTypeInfo, setShowTypeInfo] = React.useState(() => {
    try { return window.localStorage.getItem(dismissKey) !== '1'; }
    catch (e) { return true; }
  });
  const distance = m.distance || fallbackMission.distance;
  const cp = m.cp ?? fallbackMission.cp;
  const title = m.title || fallbackMission.title;
  const daysAgo = m.daysAgo ?? fallbackMission.daysAgo;

  function dismissTypeInfo() {
    try { window.localStorage.setItem(dismissKey, '1'); } catch (e) {}
    setShowTypeInfo(false);
  }

  return (
    <>
      <StatusBar/>
      <div className="scroll">
        <StepHeader step={1} total={3} title={`${distance} to ${title}`} onBack={onBack}/>

        <div style={{padding:'16px 20px'}}>
          {/* route map */}
          <div style={{
            position:'relative', height:280, borderRadius:16,
            border:'1px solid var(--line)', background:'var(--bg-elev)', overflow:'hidden',
            marginBottom:16,
          }}>
            <svg viewBox="0 0 350 280" width="100%" height="100%" preserveAspectRatio="none" style={{position:'absolute', inset:0}}>
              <defs>
                <pattern id="routegrid" width="20" height="20" patternUnits="userSpaceOnUse">
                  <path d="M 20 0 L 0 0 0 20" fill="none" stroke="var(--line)" strokeWidth="0.5"/>
                </pattern>
              </defs>
              <rect width="350" height="280" fill="url(#routegrid)"/>
              <path d="M 0 200 Q 100 180 175 150 T 350 100" stroke="var(--line-2)" strokeWidth="1.5" fill="none"/>
              <path d="M 100 0 L 130 280" stroke="var(--line-2)" strokeWidth="1.5" fill="none"/>
              <path d="M 220 0 L 240 280" stroke="var(--line-2)" strokeWidth="1.5" fill="none"/>
              {/* park */}
              <path d="M 150 120 Q 200 100 230 140 Q 235 180 185 185 Q 145 180 150 120Z"
                fill="var(--status-fresh)" opacity="0.12"/>
              {/* route path */}
              <path d="M 80 220 Q 130 200 175 165 T 250 90" stroke="var(--ink)" strokeWidth="2.5" fill="none"
                strokeLinecap="round"/>
            </svg>
            {/* current location */}
            <div style={{
              position:'absolute', left:'23%', top:'79%',
              width:12, height:12, borderRadius:'50%', background:'var(--teal)',
              boxShadow:'0 0 0 5px rgba(0,0,0,0.06), 0 0 0 10px var(--teal-soft)',
              transform:'translate(-50%,-50%)',
            }}/>
            {/* destination */}
            <div style={{
              position:'absolute', left:'71%', top:'32%',
              transform:'translate(-50%,-50%)',
            }}>
              <div style={{
                background:'var(--ink)', color:'var(--bg)',
                padding:'7px 12px', borderRadius:10,
                fontFamily:'var(--font-sans)', fontSize:12, fontWeight:600,
                whiteSpace:'nowrap', boxShadow:'0 4px 16px rgba(0,0,0,0.14)',
              }}>
                {title}
              </div>
              <div style={{
                width:10, height:10, borderRadius:'50%',
                background:'var(--bg-elev)', border:'2px solid var(--ink)',
                margin:'4px auto 0',
              }}/>
            </div>
            {/* compass */}
            <div style={{
              position:'absolute', top:12, right:12,
              width:34, height:34, borderRadius:'50%',
              background:'var(--bg-elev)', border:'1px solid var(--line)',
              display:'flex', alignItems:'center', justifyContent:'center',
              fontFamily:'var(--font-mono)', fontSize:11, fontWeight:700,
              color:'var(--ink)',
            }}>NE</div>
          </div>

          <div style={{
            display:'grid', gridTemplateColumns:'1fr 1fr 1fr',
            background:'var(--bg-elev)', border:'1px solid var(--line)', borderRadius:12,
            padding:'12px 0',
          }}>
            <div style={{textAlign:'center', borderRight:'1px solid var(--line)'}}>
              <div style={{fontSize:11, color:'var(--ink-3)', fontWeight:500}}>Distance</div>
              <div style={{fontFamily:'var(--font-mono)', fontWeight:700, fontSize:17, marginTop:3}}>{distance}</div>
            </div>
            <div style={{textAlign:'center', borderRight:'1px solid var(--line)'}}>
              <div style={{fontSize:11, color:'var(--ink-3)', fontWeight:500}}>Walk</div>
              <div style={{fontFamily:'var(--font-mono)', fontWeight:700, fontSize:17, marginTop:3}}>2 min</div>
            </div>
            <div style={{textAlign:'center'}}>
              <div style={{fontSize:11, color:'var(--ink-3)', fontWeight:500}}>Reward</div>
              <div style={{fontFamily:'var(--font-mono)', fontWeight:700, fontSize:17, marginTop:3, color:'var(--teal-ink)'}}>+{cp} GP</div>
            </div>
          </div>

          <div style={{
            marginTop:14, padding:14,
            background:'var(--teal-soft)', borderRadius:12,
            display:'flex', gap:10, alignItems:'flex-start',
          }}>
            <div style={{
              width:18, height:18, borderRadius:'50%', background:'var(--teal)',
              color:'#fff', display:'flex', alignItems:'center', justifyContent:'center',
              flexShrink:0, fontSize:11, fontWeight:700, marginTop:1,
            }}>i</div>
            <div style={{fontSize:13, color:'var(--teal-ink)', lineHeight:1.5}}>
              Last verified {daysAgo} days ago. Your update keeps city data fresh for the next explorer.
            </div>
          </div>

          {showTypeInfo && (
            <div style={{
              marginTop:12, padding:14,
              background:'var(--bg-elev)', border:'1px solid var(--line)', borderRadius:12,
              boxShadow:'0 4px 16px -8px rgba(20,32,31,0.16)',
            }}>
              <div style={{display:'flex', alignItems:'flex-start', justifyContent:'space-between', gap:12, marginBottom:6}}>
                <div className="mono" style={{fontSize:9, color:'var(--teal-ink)'}}>{typeInfo.label}</div>
                <button onClick={dismissTypeInfo} aria-label={`Dismiss ${typeInfo.label}`} style={{
                  width:22, height:22, borderRadius:6,
                  background:'transparent', border:'1px solid var(--line)',
                  color:'var(--ink-3)', cursor:'pointer',
                  display:'flex', alignItems:'center', justifyContent:'center',
                  fontSize:14, lineHeight:1, padding:0,
                }}>×</button>
              </div>
              <div style={{fontSize:14, fontWeight:700, letterSpacing:'-0.01em', marginBottom:4}}>
                {typeInfo.title}
              </div>
              <div style={{fontSize:12, color:'var(--ink-3)', lineHeight:1.45}}>
                {typeInfo.body}
              </div>
            </div>
          )}
        </div>
      </div>
      <div style={{padding:'12px 20px 24px', borderTop:'1px solid var(--line)', background:'var(--bg-elev)'}}>
        <button className="btn-primary" onClick={onNext}>I'm here →</button>
      </div>
    </>
  );
}

/* ---- Step 2: Capture ---- */
function StepCapture({ mission, onNext, onBack }) {
  const capturePhoto = 'https://images.unsplash.com/photo-1445116572660-236099ec97a0?auto=format&fit=crop&w=900&h=1200&q=82';
  const detections = [
    { label:'SIGNAGE · 91%', x:'18%', y:'14%', w:'56%', h:'13%' },
    { label:'ENTRY · 88%', x:'27%', y:'38%', w:'46%', h:'34%' },
    { label:'MENU BOARD · 76%', x:'59%', y:'30%', w:'24%', h:'18%' },
  ];

  return (
    <>
      <StatusBar/>
      <div style={{flex:1, display:'flex', flexDirection:'column', background:'#080d0d', color:'#fff'}}>
        <div style={{padding:'4px 20px 12px', display:'flex', justifyContent:'space-between', alignItems:'center'}}>
          <button onClick={onBack} style={{background:'transparent', border:0, color:'#fff', cursor:'pointer'}}>
            <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
              <path d="M14 5 L7 11 L14 17" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"/>
            </svg>
          </button>
          <span style={{fontFamily:'var(--font-mono)', fontSize:10, color:'rgba(255,255,255,0.6)', textTransform:'uppercase', letterSpacing:'0.05em'}}>
            Step 2 of 3 · Capture evidence
          </span>
          <button style={{background:'transparent', border:0, color:'rgba(255,255,255,0.5)', cursor:'pointer', fontSize:13}}>?</button>
        </div>

        {/* viewfinder */}
        <div style={{
          flex:1,
          margin:'0 16px',
          borderRadius:16,
          background:'#131818',
          position:'relative',
          overflow:'hidden',
        }}>
          <img src={capturePhoto} alt="Coffee storefront capture" className="photo-cover" style={{
            position:'absolute', inset:0, opacity:0.86, filter:'saturate(0.96) contrast(0.98)',
          }}/>
          <div style={{
            position:'absolute', inset:0,
            background:'linear-gradient(to bottom, rgba(3,8,8,0.28), rgba(3,8,8,0.08) 42%, rgba(3,8,8,0.45))',
          }}/>

          {/* corner crosshairs */}
          {[
            {top:14, left:14, rot:0},
            {top:14, right:14, rot:90},
            {bottom:14, right:14, rot:180},
            {bottom:14, left:14, rot:270},
          ].map((p, i) => (
            <svg key={i} width="22" height="22" viewBox="0 0 22 22" style={{position:'absolute', ...p, transform:`rotate(${p.rot}deg)`}}>
              <path d="M 0 0 L 14 0 M 0 0 L 0 14" stroke="rgba(255,255,255,0.7)" strokeWidth="1.5" fill="none"/>
            </svg>
          ))}

          {detections.map((d, i) => (
            <div key={d.label} style={{
              position:'absolute', left:d.x, top:d.y, width:d.w, height:d.h,
              border:'1.5px solid rgba(94,234,212,0.72)',
              borderRadius:10,
              boxShadow:'0 0 0 1px rgba(94,234,212,0.18), 0 0 24px rgba(94,234,212,0.16)',
              animation:`ai-detect-pulse 2.4s ease-in-out ${i * 0.28}s infinite`,
            }}>
              <span style={{
                position:'absolute', left:8, top:-11,
                background:'rgba(4,16,16,0.78)', color:'oklch(0.86 0.11 185)',
                border:'1px solid rgba(94,234,212,0.34)',
                padding:'3px 7px', borderRadius:999,
                fontFamily:'var(--font-mono)', fontSize:8.5, letterSpacing:'0.04em',
                textTransform:'uppercase', whiteSpace:'nowrap',
                backdropFilter:'blur(8px)',
              }}>{d.label}</span>
              <span style={{
                position:'absolute', left:0, right:0, top:0, height:1,
                background:'linear-gradient(90deg, transparent, rgba(94,234,212,0.9), transparent)',
                animation:`ai-scan-line 1.9s ease-in-out ${i * 0.22}s infinite`,
              }}/>
            </div>
          ))}

          {/* AI status banner */}
          <div style={{
            position:'absolute', top:14, left:14, right:14,
            display:'flex', alignItems:'center', gap:8,
            background:'rgba(0,0,0,0.5)', backdropFilter:'blur(8px)',
            border:'1px solid rgba(255,255,255,0.1)',
            padding:'8px 12px', borderRadius:8,
          }}>
            <div style={{
              width:7, height:7, borderRadius:'50%',
              background:'oklch(0.72 0.14 185)',
              boxShadow:'0 0 8px oklch(0.72 0.14 185)',
            }}/>
            <span style={{fontFamily:'var(--font-mono)', color:'#fff', fontSize:9.5, letterSpacing:'0.04em', textTransform:'uppercase', opacity:0.9}}>
              AI Guide · Detecting evidence
            </span>
          </div>
        </div>

        {/* shutter row */}
        <div style={{padding:'18px 20px 24px', display:'flex', alignItems:'center', justifyContent:'space-between'}}>
          <button style={{
            width:48, height:48, borderRadius:10,
            background:'#1a1f1f', border:'1px solid #2a3635',
            color:'#fff', cursor:'pointer',
            display:'flex', alignItems:'center', justifyContent:'center',
          }}>
            <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
              <rect x="3" y="6" width="14" height="11" rx="2" stroke="currentColor" strokeWidth="1.5"/>
              <circle cx="10" cy="11.5" r="3" stroke="currentColor" strokeWidth="1.5"/>
            </svg>
          </button>
          <button onClick={onNext} style={{
            width:72, height:72, borderRadius:'50%',
            background:'#fff', border:'4px solid #1a1f1f',
            cursor:'pointer', position:'relative',
          }}>
            <span style={{
              position:'absolute', inset:6, borderRadius:'50%',
              background:'#fff', border:'2px solid #0a0d0d',
            }}/>
          </button>
          <button style={{
            width:48, height:48, borderRadius:10,
            background:'#1a1f1f', border:'1px solid #2a3635',
            color:'rgba(255,255,255,0.6)', cursor:'pointer',
            display:'flex', alignItems:'center', justifyContent:'center',
          }} aria-label="Camera options">
            <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
              <path d="M5 10H15M10 5V15" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/>
            </svg>
          </button>
        </div>
      </div>
    </>
  );
}

/* ---- Step 3: AI prefill ---- */
function StepConfirm({ mission, onNext, onBack }) {
  const [name, setName] = React.useState(mission?.title || 'Mori Tea House');
  const [category, setCategory] = React.useState('Tea house · Café');
  const [hours, setHours] = React.useState('11:00 – 19:00');
  const [moreOpen, setMoreOpen] = React.useState(false);
  const photo = poiPhoto('moriteahouse', 800, 400);

  return (
    <>
      <StatusBar/>
      <div className="scroll">
        <StepHeader step={3} total={3} title="Confirm what we detected" onBack={onBack}/>

        <div style={{padding:'16px 20px'}}>
          {/* photo + AI detection summary */}
          <div style={{
            border:'1px solid var(--line)', borderRadius:14,
            background:'var(--bg-elev)', overflow:'hidden', marginBottom:16,
          }}>
            <div style={{position:'relative', height:160}}>
              <img src={photo} alt="Captured storefront" className="photo-cover" style={{position:'absolute', inset:0}}/>
              <div style={{
                position:'absolute', inset:0,
                background:'linear-gradient(to top, rgba(10,16,16,0.3) 0%, transparent 60%)',
              }}/>
            </div>
            <div style={{padding:'10px 14px', display:'flex', alignItems:'center', gap:8, borderTop:'1px solid var(--line)'}}>
              <div style={{width:6, height:6, borderRadius:'50%', background:'var(--status-fresh)'}}/>
              <span style={{fontSize:12, fontWeight:500, color:'var(--ink-2)'}}>AI detected · 92% confidence</span>
              <span style={{flex:1}}/>
              <button style={{
                background:'transparent', border:0, color:'var(--teal-ink)',
                fontFamily:'var(--font-sans)', fontSize:12, fontWeight:600,
                cursor:'pointer',
              }}>Retake</button>
            </div>
          </div>

          {/* fields */}
          <Field label="POI name" value={name} onChange={setName} aiTouched/>
          <Field label="Category" value={category} onChange={setCategory} aiTouched/>
          <Field label="Hours · Tuesday" value={hours} onChange={setHours} aiTouched/>
          <Field label="Address" value="14-23 Sarugakucho, Daikanyama" onChange={()=>{}} aiTouched readOnly/>

          <button type="button" onClick={() => setMoreOpen(o => !o)} aria-expanded={moreOpen} style={{
            width:'100%',
            marginTop:6, padding:'12px 14px',
            border:'1px dashed var(--line-2)', borderRadius:10,
            display:'flex', alignItems:'center', gap:10,
            background:'var(--bg-sunk)',
            cursor:'pointer', textAlign:'left', fontFamily:'var(--font-sans)',
          }}>
            <span style={{fontSize:18, color:'var(--ink-3)'}}>+</span>
            <div style={{flex:1, fontSize:13, color:'var(--ink-3)'}}>
              Plus more information ·{' '}
              <strong style={{color:'var(--ink-2)', fontWeight:600}}>+20 GP each</strong>
            </div>
            <span style={{fontSize:13, color:'var(--ink-3)', transform:moreOpen ? 'rotate(180deg)' : 'rotate(0deg)', transition:'transform .18s'}}>⌄</span>
          </button>

          {moreOpen && <PlusInfoPanel/>}
        </div>
      </div>
      <div style={{padding:'12px 20px 24px', borderTop:'1px solid var(--line)', background:'var(--bg-elev)'}}>
        <button className="btn-primary" onClick={onNext}>Submit contribution</button>
      </div>
    </>
  );
}

function PlusInfoPanel() {
  const [active, setActive] = React.useState({
    menu: true,
    sign: false,
    interior: false,
  });

  function toggle(key) {
    setActive(v => ({ ...v, [key]: !v[key] }));
  }

  const uploadStyle = {
    height:74,
    border:'1px dashed var(--line-2)',
    borderRadius:10,
    background:'var(--bg-sunk)',
    display:'flex',
    flexDirection:'column',
    alignItems:'center',
    justifyContent:'center',
    gap:4,
    color:'var(--ink-3)',
    cursor:'pointer',
  };

  return (
    <div style={{
      marginTop:8, padding:12, border:'1px solid var(--line)', borderRadius:12,
      background:'var(--bg-elev)', display:'grid', gap:10,
      animation:'card-rise .2s ease both',
    }}>
      <div style={{
        display:'flex', alignItems:'center', gap:8,
        padding:'9px 10px', borderRadius:10, background:'var(--teal-soft)',
        color:'var(--teal-ink)', fontSize:12, lineHeight:1.4,
      }}>
        <span style={{width:7, height:7, borderRadius:'50%', background:'var(--teal)', flexShrink:0}}/>
        Extra photos and notes are reviewed before publishing. Unsafe or illegal content is blocked.
      </div>

      <PlusInfoSection
        checked={active.menu}
        onToggle={() => toggle('menu')}
        title="Menu photo"
        reward="+20 GP"
        help="Upload one clear photo of the latest menu or price board."
      >
        <button type="button" style={uploadStyle}>
          <span style={{fontSize:18}}>+</span>
          <span className="mono" style={{fontSize:9}}>ADD MENU PHOTO</span>
        </button>
      </PlusInfoSection>

      <PlusInfoSection
        checked={active.sign}
        onToggle={() => toggle('sign')}
        title="Exterior signage"
        reward="+20 GP"
        help="Capture the outside sign and type the visible sign text."
      >
        <button type="button" style={uploadStyle}>
          <span style={{fontSize:18}}>+</span>
          <span className="mono" style={{fontSize:9}}>ADD SIGN PHOTO</span>
        </button>
        <input placeholder="Sign text, e.g. MORI TEA HOUSE" style={{
          width:'100%', padding:'10px 12px', border:'1px solid var(--line)',
          borderRadius:10, background:'var(--bg-sunk)', color:'var(--ink)',
          fontFamily:'var(--font-sans)', fontSize:13, outline:'none',
        }}/>
      </PlusInfoSection>

      <PlusInfoSection
        checked={active.interior}
        onToggle={() => toggle('interior')}
        title="Interior context"
        reward="+20 GP"
        help="Add an interior photo and choose what it helps verify."
      >
        <button type="button" style={uploadStyle}>
          <span style={{fontSize:18}}>+</span>
          <span className="mono" style={{fontSize:9}}>ADD INTERIOR PHOTO</span>
        </button>
        <select defaultValue="seating" style={{
          width:'100%', padding:'10px 12px', border:'1px solid var(--line)',
          borderRadius:10, background:'var(--bg-sunk)', color:'var(--ink)',
          fontFamily:'var(--font-sans)', fontSize:13, outline:'none',
        }}>
          <option value="seating">Seating layout</option>
          <option value="queue">Queue / ordering area</option>
          <option value="accessibility">Accessibility detail</option>
        </select>
      </PlusInfoSection>

      <label style={{display:'grid', gap:5}}>
        <span style={{fontSize:12, fontWeight:600, color:'var(--ink-3)'}}>Reviewer note</span>
        <textarea placeholder="Optional context for moderators. This note is checked before publish." style={{
          width:'100%', minHeight:62, resize:'none', border:'1px solid var(--line)',
          borderRadius:10, background:'var(--bg-sunk)', color:'var(--ink)',
          fontFamily:'var(--font-sans)', fontSize:13, padding:10, outline:'none',
        }}/>
      </label>
    </div>
  );
}

function PlusInfoSection({ checked, onToggle, title, reward, help, children }) {
  return (
    <div style={{
      border:'1px solid var(--line)', borderRadius:12, overflow:'hidden',
      background: checked ? 'var(--bg-elev)' : 'var(--bg-sunk)',
    }}>
      <label style={{
        display:'flex', alignItems:'flex-start', gap:10, padding:11,
        cursor:'pointer',
      }}>
        <input type="checkbox" checked={checked} onChange={onToggle} style={{accentColor:'var(--teal)', marginTop:2}}/>
        <span style={{flex:1}}>
          <span style={{display:'flex', justifyContent:'space-between', gap:8, marginBottom:2}}>
            <span style={{fontSize:13, fontWeight:700, color:'var(--ink)'}}>{title}</span>
            <span className="mono" style={{fontSize:9, color:'var(--teal-ink)'}}>{reward}</span>
          </span>
          <span style={{fontSize:12, color:'var(--ink-3)', lineHeight:1.35}}>{help}</span>
        </span>
      </label>
      {checked && (
        <div style={{padding:'0 11px 11px', display:'grid', gap:8}}>
          {children}
        </div>
      )}
    </div>
  );
}

function Field({ label, value, onChange, aiTouched, readOnly }) {
  return (
    <div style={{marginBottom:12}}>
      <div style={{display:'flex', alignItems:'center', gap:6, marginBottom:5}}>
        <span style={{fontSize:12, fontWeight:500, color:'var(--ink-3)'}}>{label}</span>
        {aiTouched && (
          <span style={{
            fontSize:9.5, color:'var(--teal-ink)', fontWeight:700,
            background:'var(--teal-soft)', padding:'1px 6px', borderRadius:4,
            fontFamily:'var(--font-mono)', letterSpacing:'0.03em', textTransform:'uppercase',
          }}>AI</span>
        )}
      </div>
      <input
        value={value}
        onChange={e => onChange(e.target.value)}
        readOnly={readOnly}
        style={{
          width:'100%', padding:'10px 13px',
          border:'1px solid var(--line)', borderRadius:10,
          background: readOnly ? 'var(--bg-sunk)' : 'var(--bg-elev)',
          fontFamily:'var(--font-sans)', fontSize:14, color:'var(--ink)', outline:'none',
        }}
      />
    </div>
  );
}

/* ---- Step 4: Submitting ---- */
function StepSubmit({ mission, onNext }) {
  const [progress, setProgress] = React.useState(1);
  const completedRef = React.useRef(false);

  React.useEffect(() => {
    const start = performance.now();
    let frame;
    function tick(now) {
      const elapsed = Math.min(3000, now - start);
      const nextProgress = 1 - elapsed / 3000;
      setProgress(nextProgress);
      if (elapsed >= 3000) {
        if (!completedRef.current) {
          completedRef.current = true;
          onNext && onNext();
        }
        return;
      }
      frame = requestAnimationFrame(tick);
    }
    frame = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(frame);
  }, [onNext]);

  return (
    <>
      <StatusBar/>
      <div style={{flex:1, display:'flex', flexDirection:'column', justifyContent:'center', alignItems:'center', padding:24}}>
        <div style={{position:'relative', marginBottom:32}}>
          <CoordGlyph seed="mori-tea-house-daikanyama" size={140} accent="var(--teal)"/>
          <div style={{
            position:'absolute', inset:-10,
            border:'1px dashed var(--teal)',
            borderRadius:18,
            animation:'spin 4s linear infinite',
          }}/>
          <div style={{
            position:'absolute', left:'50%', top:'50%',
            transform:'translate(-50%, -50%)',
            width:44, height:44, borderRadius:'50%',
            background:`conic-gradient(color-mix(in oklch, var(--teal), transparent 42%) ${Math.max(progress, 0) * 360}deg, rgba(255,255,255,0.08) 0deg)`,
            display:'grid', placeItems:'center',
            boxShadow:'0 8px 26px rgba(20,32,31,0.08)',
            opacity:0.72,
          }}>
            <div style={{
              width:34, height:34, borderRadius:'50%', background:'color-mix(in oklch, var(--bg-elev), transparent 14%)',
            }}/>
          </div>
        </div>
        <div style={{
          display:'inline-flex', alignItems:'center', gap:6,
          fontSize:11, color:'var(--teal-ink)', fontFamily:'var(--font-mono)',
          letterSpacing:'0.04em', textTransform:'uppercase', marginBottom:12,
        }}>
          <span>⟳</span> Validating contribution
        </div>
        <div style={{fontSize:20, fontWeight:700, letterSpacing:'-0.02em', textAlign:'center', marginBottom:6}}>
          Verifying evidence and metadata
        </div>
        <div style={{fontSize:13, color:'var(--ink-3)', textAlign:'center', maxWidth:260, lineHeight:1.55}}>
          Cross-checking against last verified record. This usually takes a moment.
        </div>
        <div style={{marginTop:32, width:'100%', maxWidth:260, display:'flex', flexDirection:'column', gap:8}}>
          <CheckRow label="Photo received" done/>
          <CheckRow label="Location matches POI" done/>
          <CheckRow label="Awaiting cross-verification" pending/>
        </div>
      </div>
      <style>{`
        @keyframes spin { to { transform: rotate(360deg); } }
        @keyframes ai-detect-pulse {
          0%, 100% { opacity:0.72; transform:scale(1); }
          50% { opacity:1; transform:scale(1.012); }
        }
        @keyframes ai-scan-line {
          0% { transform:translateY(0); opacity:0; }
          18% { opacity:1; }
          82% { opacity:1; }
          100% { transform:translateY(100px); opacity:0; }
        }
      `}</style>
    </>
  );
}

function CheckRow({ label, done }) {
  return (
    <div style={{display:'flex', alignItems:'center', gap:10, fontSize:13}}>
      <div style={{
        width:18, height:18, borderRadius:'50%',
        background: done ? 'var(--status-done)' : 'transparent',
        border: done ? '0' : '1.5px dashed var(--ink-4)',
        display:'flex', alignItems:'center', justifyContent:'center',
        flexShrink:0,
      }}>
        {done && (
          <svg width="10" height="10" viewBox="0 0 10 10" fill="none">
            <path d="M 2 5 L 4 7 L 8 3" stroke="white" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
        )}
      </div>
      <span style={{color: done ? 'var(--ink)' : 'var(--ink-3)'}}>{label}</span>
    </div>
  );
}

/* ---- Step 5: Reward — animated card reveal ---- */
function StepReward({ mission, onContinue, onViewCard, cardStyle }) {
  const [revealed, setRevealed] = React.useState(false);
  const rewardTitle = mission?.title || 'Mori Tea House';
  const rewardGp = mission?.cp || 30;
  const rewardRole = mission?.rareLabel || mission?.type || 'Photo Update';
  React.useEffect(() => {
    const t = setTimeout(() => setRevealed(true), 120);
    return () => clearTimeout(t);
  }, []);

  return (
    <>
      <StatusBar/>
      <div className="scroll" style={{display:'flex', flexDirection:'column'}}>
        <div style={{padding:'24px 20px 0'}}>
          <div style={{
            display:'inline-flex', alignItems:'center', gap:6,
            fontSize:12, fontWeight:600, color:'var(--status-done)', marginBottom:8,
          }}>
            <span style={{width:6, height:6, borderRadius:'50%', background:'var(--status-done)', display:'inline-block'}}/>
            Contribution stamped · 09:43 JST
          </div>
          <div style={{fontSize:27, fontWeight:700, letterSpacing:'-0.025em', lineHeight:1.15, marginBottom:6}}>
            {rewardTitle}<br/>accepted
          </div>
          <div style={{fontSize:14, color:'var(--ink-3)', marginBottom:24, lineHeight:1.55}}>
            AI verified your contribution and added it to the live city data layer.
          </div>

          {/* Animated card reveal */}
          <div style={{
            display:'flex', justifyContent:'center', marginBottom:24,
            position:'relative',
          }}>
            {/* Glow behind card */}
            <div style={{
              position:'absolute',
              width:200, height:200,
              borderRadius:'50%',
              background:'var(--teal)',
              filter:'blur(56px)',
              opacity: revealed ? 0.18 : 0,
              transition:'opacity 0.9s ease',
              top:'50%', left:'50%', transform:'translate(-50%,-50%)',
              pointerEvents:'none',
            }}/>
            <div style={{
              opacity: revealed ? 1 : 0,
              transform: revealed ? 'scale(1) translateY(0)' : 'scale(0.84) translateY(22px)',
              transition:'opacity 0.55s cubic-bezier(0.34, 1.56, 0.64, 1), transform 0.55s cubic-bezier(0.34, 1.56, 0.64, 1)',
            }}>
              <PoiCard
                name={rewardTitle}
                place="Daikanyama · Tokyo"
                rarity="first-refresh"
                seed={mission?.seed || "mori-tea-house-daikanyama"}
                role={rewardRole}
                date="2026.05.09"
                cardStyle={cardStyle}
              />
            </div>
          </div>

          {/* Reward stats */}
          <div style={{
            border:'1px solid var(--line)', borderRadius:14, background:'var(--bg-elev)',
            padding:'14px 16px', marginBottom:14,
          }}>
            <div style={{fontSize:12, fontWeight:500, color:'var(--ink-3)', marginBottom:10}}>Earned</div>
            <RewardRow label="General Points" value={`+${rewardGp} GP`} delta="AI verified"/>
            <RewardRow label="Reputation Score" value="+0.8 RS" delta="quality verified"/>
            <RewardRow label="Streak" value="13 days" delta="personal best" last/>
          </div>

          {/* Level unlock */}
          <div style={{
            border:'1px solid var(--line)', borderRadius:14,
            padding:'14px 16px', marginBottom:28,
            background:'var(--bg-elev)',
            display:'flex', gap:12, alignItems:'center',
          }}>
            <div style={{
              width:40, height:40, borderRadius:10,
              background:'var(--teal-soft)',
              display:'flex', alignItems:'center', justifyContent:'center',
              flexShrink:0,
            }}>
              <span style={{fontSize:20}}>↑</span>
            </div>
            <div style={{flex:1}}>
              <div style={{fontSize:14, fontWeight:700, letterSpacing:'-0.01em'}}>Scout → Mapper unlocked</div>
              <div style={{fontSize:12, color:'var(--ink-3)', marginTop:2, lineHeight:1.4}}>
                You can now take on verification missions and rare opportunities.
              </div>
            </div>
          </div>
        </div>
      </div>
      <div style={{padding:'12px 20px 24px', borderTop:'1px solid var(--line)', background:'var(--bg-elev)', display:'flex', gap:10}}>
        <button className="btn-secondary" onClick={onViewCard} style={{width:'auto', flex:'1 1 40%'}}>
          View card
        </button>
        <button className="btn-primary" onClick={onContinue} style={{flex:'1 1 60%'}}>
          Continue nearby →
        </button>
      </div>
    </>
  );
}

function RewardRow({ label, value, delta, last }) {
  return (
    <div style={{
      display:'flex', alignItems:'baseline', justifyContent:'space-between',
      padding:'8px 0', borderBottom: last ? 'none' : '1px dashed var(--line)',
    }}>
      <div>
        <div style={{fontSize:13, fontWeight:500}}>{label}</div>
        {delta && <div style={{fontSize:11, color:'var(--ink-3)', marginTop:1}}>{delta}</div>}
      </div>
      <div style={{fontFamily:'var(--font-mono)', fontWeight:700, fontSize:14, color:'var(--teal-ink)'}}>{value}</div>
    </div>
  );
}

const CONTRIBUTION_FLOW_TYPES = [
  'add-place',
  'update-place',
  'add-review',
  'add-photo',
  'update-road',
  'update-address',
];

function normalizeContributionFlowType(mission) {
  const raw = String(mission?.type || mission?.id || mission?.title || '').toLowerCase();
  if (raw.includes('add-place') || raw.includes('first') || raw.includes('new poi')) return 'add-place';
  if (raw.includes('update-road') || raw.includes('road')) return 'update-road';
  if (raw.includes('update-address') || raw.includes('address')) return 'update-address';
  if (raw.includes('add-review') || raw.includes('review')) return 'add-review';
  if (raw.includes('add-photo') || raw.includes('photo') || raw.includes('angle')) return 'add-photo';
  if (raw.includes('update-place') || raw.includes('refresh') || raw.includes('verify') || raw.includes('campaign')) return 'update-place';
  return 'update-place';
}

const CONTRIBUTION_FLOW_DEFS = {
  'add-place': {
    label:'Add Place',
    accent:'var(--status-rare)',
    reward:'+50 GP',
    title:'Create a missing place.',
    object:'Unmarked storefront',
    stages:[
      { kind:'form', title:'Place details', note:'Required fields gate the submit state.', fields:['Place name', 'Category', 'Address'], chips:['Business profile check', 'Public place only'] },
      { kind:'map', title:'Edit map location', note:'Drop the pin on the exact entrance or storefront.', target:'Missing POI pin', cta:'Use this location' },
      { kind:'media', title:'Place photos', note:'Storefront, sign, notice, or entrance photos help review.', required:'1 required', source:'Camera or gallery' },
      { kind:'review', title:'Submit new POI', note:'The place appears after AI and reviewer approval.', checks:['Name/category complete', 'Pin inside district', 'Photo evidence attached'] },
    ],
  },
  'update-place': {
    label:'Update Place',
    accent:'var(--teal)',
    reward:'+30 GP',
    title:'Correct an existing place.',
    object:'Mori Tea House',
    stages:[
      { kind:'map', title:'Fix a place', note:'Tap a POI pin, then continue once the place is selected.', target:'Selected POI', cta:'Next' },
      { kind:'choice', title:'Suggest an edit', note:'Choose which attributes changed.', options:['Place name', 'Category', 'Address', 'Hours', 'Phone', 'Website', 'Closed or not here'] },
      { kind:'form', title:'Edit information', note:'Update one or more fields, or mark incorrect if unknown.', fields:['Place name', 'Hours', 'Operating status'], chips:['Do not know but incorrect', 'Add storefront proof'] },
      { kind:'review', title:'Submit edit', note:'Edits are compared with prior records and nearby signals.', checks:['Changed field selected', 'Reasonable value format', 'Optional proof attached'] },
    ],
  },
  'add-review': {
    label:'Add Review',
    accent:'var(--status-stale)',
    reward:'+30 GP',
    title:'Share visit context.',
    object:'Onibus Coffee',
    stages:[
      { kind:'place-list', title:'Choose place', note:'Search or select from nearby places visited recently.', places:['Onibus Coffee', 'Mori Tea House', 'Ivy Place'] },
      { kind:'rating', title:'Rate and review', note:'A star rating is required; text and tags add data value.', tags:['Outdoor seating', 'Quiet', 'Good for work'] },
      { kind:'media', title:'Add photos and videos', note:'Optional media makes the review more useful.', required:'Optional', source:'Camera or gallery' },
      { kind:'review', title:'Post review', note:'Reviews publish publicly with profile identity.', checks:['Rating selected', 'Policy scan passed', 'Media linked to POI'] },
    ],
  },
  'add-photo': {
    label:'Add Photo',
    accent:'var(--teal)',
    reward:'+30 GP',
    title:'Attach visual evidence.',
    object:'Select a place',
    stages:[
      { kind:'media', title:'Select media', note:'Start from camera or phone gallery.', required:'1 selected', source:'Camera or gallery' },
      { kind:'place-list', title:'Select a place', note:'Choose the POI this media belongs to.', places:['Mori Tea House', 'Cosme Kitchen', 'Saturdays NYC'] },
      { kind:'photo-edit', title:'Edit attachment', note:'Review crop, caption, place match, and public posting identity.', chips:['Storefront', 'Menu', 'Interior'] },
      { kind:'review', title:'Post photo', note:'Photos are checked for place relevance and safety.', checks:['Media selected', 'POI attached', 'Public posting confirmed'] },
    ],
  },
  'update-road': {
    label:'Update Road',
    accent:'var(--warm)',
    reward:'+30 GP',
    title:'Fix road network data.',
    object:'Bay Street segment',
    stages:[
      { kind:'choice', title:'Add or fix a road', note:'Select the road issue type before editing geometry.', options:['Missing road', 'Road name', 'One or two way', 'Drawn incorrectly', 'Road closed', 'Road does not exist'] },
      { kind:'map', title:'Select road segment', note:'Tap a road, confirm the candidate, then select a section.', target:'Highlighted road', cta:'Edit entire road' },
      { kind:'form', title:'Road details', note:'Enter the corrected road data and optional context.', fields:['Correct road name', 'Further details'], chips:['Segment selected', 'Entire road option'] },
      { kind:'review', title:'Send road fix', note:'Road edits go through review before map network changes.', checks:['Issue type selected', 'Road segment selected', 'Correction provided'] },
    ],
  },
  'update-address': {
    label:'Update Address',
    accent:'var(--status-done)',
    reward:'+30 GP',
    title:'Fix address or pin accuracy.',
    object:'Building entrance',
    stages:[
      { kind:'map', title:'Position marker', note:'Drag the map so the marker sits on the building or delivery point.', target:'Address marker', cta:'Next' },
      { kind:'map-confirm', title:'Confirm pin shift', note:'Compare original reference point with the corrected pin.', target:'Updated pin location' },
      { kind:'form', title:'Address details', note:'Confirm the street address and edit city/province/postal fields if needed.', fields:['Street address', 'City', 'Postal code'], chips:['Edit components', 'Delivery point'] },
      { kind:'review', title:'Submit address fix', note:'Address updates may take several days to approve.', checks:['Marker positioned', 'Street address complete', 'Region fields valid'] },
    ],
  },
};

function ContributionFlow({ mission, flowType, onBack, onComplete, onViewCard, cardStyle }) {
  const type = flowType || normalizeContributionFlowType(mission);
  const def = CONTRIBUTION_FLOW_DEFS[type] || CONTRIBUTION_FLOW_DEFS['update-place'];
  const [step, setStep] = React.useState(0);
  const finalStep = def.stages.length + 1;
  const total = finalStep + 1;

  function next() {
    setStep(s => Math.min(s + 1, finalStep));
  }

  function back() {
    if (step === 0) onBack && onBack();
    else setStep(s => Math.max(0, s - 1));
  }

  if (step === finalStep) {
    return (
      <ContributionAccomplishment
        def={def}
        mission={mission}
        cardStyle={cardStyle}
        onContinue={onComplete}
        onViewCard={onViewCard}
      />
    );
  }

  if (step === def.stages.length) {
    return <ContributionSubmit def={def} step={step + 1} total={total} onNext={next}/>;
  }

  const stage = def.stages[step];
  return (
    <>
      <StatusBar/>
      <div className="scroll">
        <StepHeader step={step + 1} total={total} title={stage.title} onBack={back}/>
        <div style={{padding:'16px 20px 22px'}}>
          <FlowIntro def={def} stage={stage}/>
          <ContributionStageBody def={def} stage={stage}/>
        </div>
      </div>
      <div style={{padding:'12px 20px 24px', borderTop:'1px solid var(--line)', background:'var(--bg-elev)', display:'flex', gap:10}}>
        <button className="btn-secondary" style={{flex:'0 0 34%'}} onClick={back}>Back</button>
        <button className="btn-primary" style={{flex:1}} onClick={next}>
          {step === def.stages.length - 1 ? 'Review submit' : (stage.cta || 'Next')}
        </button>
      </div>
    </>
  );
}

function FlowIntro({ def, stage }) {
  return (
    <div style={{
      border:'1px solid var(--line)', borderRadius:14, background:'var(--bg-elev)',
      padding:14, marginBottom:14, display:'grid', gap:8,
    }}>
      <div style={{display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap:12}}>
        <div>
          <div className="mono" style={{fontSize:9, color:def.accent}}>{def.label}</div>
          <div style={{fontSize:18, fontWeight:800, letterSpacing:'-0.02em', marginTop:4}}>{def.title}</div>
        </div>
        <Pill tone="teal">{def.reward}</Pill>
      </div>
      <div style={{fontSize:12.5, color:'var(--ink-3)', lineHeight:1.45}}>{stage.note}</div>
    </div>
  );
}

function ContributionStageBody({ def, stage }) {
  if (stage.kind === 'map') return <SharedMapSelector def={def} stage={stage}/>;
  if (stage.kind === 'map-confirm') return <SharedMapSelector def={def} stage={stage} confirm/>;
  if (stage.kind === 'media') return <SharedMediaUpload def={def} stage={stage}/>;
  if (stage.kind === 'choice') return <SharedChoiceList def={def} stage={stage}/>;
  if (stage.kind === 'place-list') return <SharedPlacePicker def={def} stage={stage}/>;
  if (stage.kind === 'rating') return <SharedReviewEditor def={def} stage={stage}/>;
  if (stage.kind === 'photo-edit') return <SharedPhotoEditor def={def} stage={stage}/>;
  if (stage.kind === 'review') return <SharedSubmitReview def={def} stage={stage}/>;
  return <SharedFormStage def={def} stage={stage}/>;
}

function SharedMapSelector({ def, stage, confirm }) {
  return (
    <div style={{
      border:'1px solid var(--line)', borderRadius:16, overflow:'hidden',
      background:'var(--bg-elev)', marginBottom:14,
    }}>
      <div style={{height:265, position:'relative', background:'var(--bg-sunk)'}}>
        <svg viewBox="0 0 350 265" width="100%" height="100%" preserveAspectRatio="none" style={{position:'absolute', inset:0}}>
          <defs>
            <pattern id={`flow-grid-${def.label}`} width="22" height="22" patternUnits="userSpaceOnUse">
              <path d="M 22 0 L 0 0 0 22" fill="none" stroke="var(--line)" strokeWidth="0.5"/>
            </pattern>
          </defs>
          <rect width="350" height="265" fill={`url(#flow-grid-${def.label})`}/>
          <path d="M -20 86 Q 80 60 168 96 T 370 78" stroke="var(--line-2)" strokeWidth="2" fill="none"/>
          <path d="M 74 -20 Q 92 92 112 186 T 128 285" stroke="var(--line-2)" strokeWidth="2" fill="none"/>
          <path d="M 232 -20 Q 238 88 224 176 T 256 285" stroke={def.accent} strokeWidth={confirm ? 4 : 2} fill="none" opacity={confirm ? 0.8 : 0.42}/>
          {confirm && <path d="M 164 150 L 226 112" stroke="var(--ink-3)" strokeWidth="1.5" strokeDasharray="5 5"/>}
        </svg>
        <div style={{
          position:'absolute', left:confirm ? '64%' : '48%', top:confirm ? '42%' : '56%',
          transform:'translate(-50%,-100%)',
        }}>
          <div style={{
            width:28, height:28, borderRadius:'50% 50% 50% 4px',
            background:def.accent, transform:'rotate(-45deg)',
            boxShadow:'0 8px 20px rgba(20,32,31,0.16)',
          }}/>
          <div style={{
            position:'absolute', left:9, top:7, width:10, height:10, borderRadius:'50%',
            background:'var(--bg-elev)',
          }}/>
        </div>
        {confirm && (
          <div style={{
            position:'absolute', left:'45%', top:'58%', width:14, height:14,
            borderRadius:'50%', background:'var(--teal)',
            boxShadow:'0 0 0 5px var(--teal-soft)',
          }}/>
        )}
        <div style={{
          position:'absolute', left:12, right:12, bottom:12,
          border:'1px solid var(--line)', borderRadius:12,
          background:'color-mix(in oklch, var(--bg-elev), transparent 5%)',
          padding:12, display:'flex', alignItems:'center', justifyContent:'space-between', gap:10,
        }}>
          <div>
            <div style={{fontSize:13, fontWeight:800}}>{stage.target}</div>
            <div style={{fontSize:11.5, color:'var(--ink-3)', marginTop:2}}>{def.object}</div>
          </div>
          <Pill>{confirm ? 'Compare' : 'Selected'}</Pill>
        </div>
      </div>
    </div>
  );
}

function SharedFormStage({ def, stage }) {
  const fields = stage.fields || ['Field'];
  return (
    <div style={{display:'grid', gap:10}}>
      {fields.map((field, i) => (
        <label key={field} style={{display:'grid', gap:5}}>
          <span style={{fontSize:12, fontWeight:600, color:'var(--ink-3)'}}>{field}{i === 0 ? ' *' : ''}</span>
          <input defaultValue={i === 0 ? def.object : ''} placeholder={field} style={{
            width:'100%', height:44, border:'1px solid var(--line)', borderRadius:10,
            background:'var(--bg-elev)', color:'var(--ink)', padding:'0 12px',
            fontFamily:'var(--font-sans)', fontSize:14, outline:'none',
          }}/>
        </label>
      ))}
      <div style={{display:'flex', gap:6, flexWrap:'wrap', marginTop:2}}>
        {(stage.chips || []).map(chip => <Pill key={chip}>{chip}</Pill>)}
      </div>
    </div>
  );
}

function SharedMediaUpload({ def, stage }) {
  return (
    <div style={{display:'grid', gap:12}}>
      <div style={{
        height:190, border:'1px dashed var(--line-2)', borderRadius:16,
        background:'var(--bg-elev)', display:'grid', placeItems:'center', textAlign:'center',
        padding:18,
      }}>
        <div>
          <div style={{
            width:58, height:58, borderRadius:16, background:'var(--teal-soft)',
            color:'var(--teal-ink)', display:'grid', placeItems:'center',
            fontSize:28, margin:'0 auto 12px',
          }}>+</div>
          <div style={{fontSize:16, fontWeight:800, marginBottom:4}}>{stage.source}</div>
          <div style={{fontSize:12, color:'var(--ink-3)'}}>{stage.required}</div>
        </div>
      </div>
      <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:8}}>
        {['Camera', 'Phone gallery'].map(label => (
          <button key={label} className="btn-secondary" style={{height:42}}>{label}</button>
        ))}
      </div>
    </div>
  );
}

function SharedChoiceList({ def, stage }) {
  return (
    <div style={{border:'1px solid var(--line)', borderRadius:14, background:'var(--bg-elev)', overflow:'hidden'}}>
      {(stage.options || []).map((item, i) => (
        <button key={item} style={{
          width:'100%', minHeight:50, border:0,
          borderBottom:i === stage.options.length - 1 ? 0 : '1px solid var(--line)',
          background:'transparent', padding:'0 14px',
          display:'flex', alignItems:'center', justifyContent:'space-between',
          fontFamily:'var(--font-sans)', cursor:'pointer', color:'var(--ink)',
        }}>
          <span style={{fontSize:14, fontWeight:i === 0 ? 800 : 500}}>{item}</span>
          <span style={{color:'var(--ink-3)'}}>›</span>
        </button>
      ))}
    </div>
  );
}

function SharedPlacePicker({ def, stage }) {
  return (
    <div style={{display:'grid', gap:10}}>
      <div style={{
        height:44, border:'1px solid var(--line)', borderRadius:12, background:'var(--bg-elev)',
        display:'flex', alignItems:'center', gap:9, padding:'0 12px',
      }}>
        <span style={{color:'var(--ink-3)'}}>⌕</span>
        <span style={{fontSize:14, color:'var(--ink-3)'}}>Choose place...</span>
      </div>
      <div style={{border:'1px solid var(--line)', borderRadius:14, background:'var(--bg-elev)', overflow:'hidden'}}>
        {(stage.places || []).map((place, i) => (
          <button key={place} style={{
            width:'100%', minHeight:58, border:0,
            borderBottom:i === stage.places.length - 1 ? 0 : '1px solid var(--line)',
            background:'transparent', padding:'0 14px',
            display:'grid', gridTemplateColumns:'34px 1fr', gap:10, alignItems:'center',
            textAlign:'left', fontFamily:'var(--font-sans)', cursor:'pointer',
          }}>
            <CoordGlyph seed={place} size={34} accent={def.accent}/>
            <span>
              <span style={{display:'block', fontSize:14, fontWeight:800}}>{place}</span>
              <span style={{display:'block', fontSize:11.5, color:'var(--ink-3)', marginTop:2}}>Nearby · Daikanyama</span>
            </span>
          </button>
        ))}
      </div>
    </div>
  );
}

function SharedReviewEditor({ stage }) {
  return (
    <div style={{border:'1px solid var(--line)', borderRadius:14, background:'var(--bg-elev)', padding:14}}>
      <div style={{display:'flex', gap:4, marginBottom:14}}>
        {[1,2,3,4,5].map(i => <span key={i} style={{fontSize:30, color:i <= 4 ? 'var(--status-stale)' : 'var(--line-2)'}}>★</span>)}
      </div>
      <textarea defaultValue="Clear signage, fast service, and reliable hours on weekday afternoons." style={{
        width:'100%', minHeight:104, resize:'none', border:'1px solid var(--line)',
        borderRadius:12, background:'var(--bg-sunk)', padding:12,
        fontFamily:'var(--font-sans)', fontSize:13, color:'var(--ink)', outline:'none',
      }}/>
      <div style={{display:'flex', gap:6, flexWrap:'wrap', marginTop:12}}>
        {(stage.tags || []).map(tag => <Pill key={tag}>{tag}</Pill>)}
      </div>
    </div>
  );
}

function SharedPhotoEditor({ stage }) {
  return (
    <div style={{display:'grid', gap:12}}>
      <div style={{height:210, borderRadius:16, overflow:'hidden', position:'relative', background:'var(--bg-sunk)'}}>
        <img src={poiPhoto('mori-tea-house-daikanyama', 800, 500)} alt="Selected media" className="photo-cover" style={{position:'absolute', inset:0}}/>
        <button style={{
          position:'absolute', right:12, top:12, border:0, borderRadius:999,
          background:'rgba(10,16,16,0.58)', color:'#fff', padding:'6px 10px',
          fontFamily:'var(--font-sans)', fontSize:12,
        }}>Edit</button>
      </div>
      <div style={{display:'flex', gap:6, flexWrap:'wrap'}}>
        {(stage.chips || []).map(chip => <Pill key={chip}>{chip}</Pill>)}
      </div>
    </div>
  );
}

function SharedSubmitReview({ def, stage }) {
  return (
    <div style={{display:'grid', gap:10}}>
      {(stage.checks || []).map((check, i) => (
        <div key={check} style={{
          border:'1px solid var(--line)', borderRadius:12, background:'var(--bg-elev)',
          padding:12, display:'flex', alignItems:'center', gap:10,
        }}>
          <span style={{
            width:22, height:22, borderRadius:'50%', background:i < 2 ? 'var(--teal-soft)' : 'var(--bg-sunk)',
            color:i < 2 ? 'var(--teal-ink)' : 'var(--ink-3)', display:'grid', placeItems:'center',
            fontSize:12, fontWeight:800,
          }}>{i < 2 ? '✓' : '…'}</span>
          <span style={{fontSize:13, fontWeight:600}}>{check}</span>
        </div>
      ))}
      <div style={{
        marginTop:4, padding:12, borderRadius:12,
        background:'var(--teal-soft)', color:'var(--teal-ink)',
        fontSize:12.5, lineHeight:1.45,
      }}>{stage.note}</div>
    </div>
  );
}

function ContributionSubmit({ def, step, total, onNext }) {
  React.useEffect(() => {
    const t = window.setTimeout(() => onNext && onNext(), 1200);
    return () => window.clearTimeout(t);
  }, [onNext]);

  return (
    <>
      <StatusBar/>
      <div className="scroll">
        <StepHeader step={step} total={total} title="Sending contribution" onBack={()=>{}}/>
        <div style={{padding:'68px 20px 24px', textAlign:'center'}}>
          <div style={{position:'relative', width:132, height:132, margin:'0 auto 24px'}}>
            <CoordGlyph seed={def.label} size={132} accent={def.accent}/>
            <div style={{
              position:'absolute', inset:-10, borderRadius:22,
              border:'1px dashed var(--teal)', animation:'spin 4s linear infinite',
            }}/>
          </div>
          <div className="mono" style={{fontSize:10, color:def.accent, marginBottom:10}}>AI + reviewer queue</div>
          <div style={{fontSize:23, fontWeight:800, letterSpacing:'-0.025em', marginBottom:8}}>
            Packaging {def.label.toLowerCase()}
          </div>
          <div style={{fontSize:13, color:'var(--ink-3)', lineHeight:1.55}}>
            Checking location, evidence, identity, and public posting rules before the contribution is recorded.
          </div>
        </div>
      </div>
    </>
  );
}

function ContributionAccomplishment({ def, mission, onContinue, onViewCard, cardStyle }) {
  const title = mission?.title || def.object;
  return (
    <>
      <StatusBar/>
      <div className="scroll">
        <div style={{padding:'30px 20px 24px'}}>
          <div style={{
            minHeight:250, border:'1px solid var(--line)', borderRadius:18,
            background:'var(--bg-elev)', padding:20, textAlign:'center',
            display:'grid', placeItems:'center', marginBottom:18,
          }}>
            <div>
              <CoordGlyph seed={`${def.label}-${title}`} size={112} accent={def.accent}/>
              <div style={{fontSize:25, fontWeight:800, letterSpacing:'-0.025em', margin:'18px 0 6px'}}>
                Contribution received
              </div>
              <div style={{fontSize:13, color:'var(--ink-3)', lineHeight:1.5}}>
                Points are awarded after approval. Your update is now in the review queue.
              </div>
            </div>
          </div>
          <div style={{
            border:'1px solid var(--line)', borderRadius:14, background:'var(--bg-elev)',
            padding:'4px 14px', marginBottom:18,
          }}>
            <RewardRow label={def.label} value={def.reward} delta={title}/>
            <RewardRow label="Review status" value="Pending" delta="AI precheck passed"/>
            <RewardRow label="Public profile" value="Queued" delta="visible after approval" last/>
          </div>
          <div style={{
            border:'1px solid var(--line)', borderRadius:14, background:'var(--bg-elev)',
            padding:14,
          }}>
            <div style={{fontSize:15, fontWeight:800, marginBottom:4}}>Next useful action</div>
            <div style={{fontSize:12.5, color:'var(--ink-3)', lineHeight:1.45}}>
              Add another nearby contribution while the location context is still fresh.
            </div>
          </div>
        </div>
      </div>
      <div style={{padding:'12px 20px 24px', borderTop:'1px solid var(--line)', background:'var(--bg-elev)', display:'flex', gap:10}}>
        <button className="btn-secondary" style={{flex:'0 0 38%'}} onClick={onViewCard}>View card</button>
        <button className="btn-primary" style={{flex:1}} onClick={onContinue}>Continue</button>
      </div>
    </>
  );
}

function ContributionFlowPath({ type }) {
  const def = CONTRIBUTION_FLOW_DEFS[type] || CONTRIBUTION_FLOW_DEFS['add-place'];
  return (
    <>
      <StatusBar/>
      <div className="scroll">
        <div style={{padding:'4px 20px 24px'}}>
          <div style={{display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap:12, marginBottom:16}}>
            <div>
              <div className="mono" style={{fontSize:9, color:def.accent}}>FLOW PATH</div>
              <div style={{fontSize:25, fontWeight:800, letterSpacing:'-0.03em', lineHeight:1.08, marginTop:5}}>{def.label}</div>
              <div style={{fontSize:12.5, color:'var(--ink-3)', marginTop:5, lineHeight:1.4}}>{def.title}</div>
            </div>
            <Pill tone="teal">{def.reward}</Pill>
          </div>

          <div style={{display:'grid', gap:10}}>
            {def.stages.map((stage, i) => (
              <div key={stage.title} style={{
                border:'1px solid var(--line)', borderRadius:14, background:'var(--bg-elev)',
                padding:13, display:'grid', gridTemplateColumns:'34px 1fr', gap:11,
              }}>
                <div style={{
                  width:34, height:34, borderRadius:10, background:i === 0 ? def.accent : 'var(--bg-sunk)',
                  color:i === 0 ? '#fff' : 'var(--ink-3)', display:'grid', placeItems:'center',
                  fontFamily:'var(--font-mono)', fontWeight:800, fontSize:12,
                }}>{i + 1}</div>
                <div>
                  <div style={{fontSize:14.5, fontWeight:800, letterSpacing:'-0.01em'}}>{stage.title}</div>
                  <div style={{fontSize:12, color:'var(--ink-3)', lineHeight:1.38, marginTop:3}}>{stage.note}</div>
                  <div style={{display:'flex', gap:5, flexWrap:'wrap', marginTop:8}}>
                    <Pill>{stage.kind}</Pill>
                    {(stage.fields || stage.options || stage.places || stage.chips || []).slice(0, 2).map(item => <Pill key={item}>{item}</Pill>)}
                  </div>
                </div>
              </div>
            ))}
            <div style={{
              border:'1.5px solid var(--teal)', borderRadius:14, background:'var(--teal-soft)',
              padding:14, display:'grid', gridTemplateColumns:'34px 1fr', gap:11,
            }}>
              <div style={{
                width:34, height:34, borderRadius:10, background:'var(--teal)',
                color:'#fff', display:'grid', placeItems:'center', fontWeight:800,
              }}>✓</div>
              <div>
                <div style={{fontSize:14.5, fontWeight:800, color:'var(--teal-ink)'}}>Shared accomplishment page</div>
                <div style={{fontSize:12, color:'var(--teal-ink)', lineHeight:1.38, marginTop:3}}>
                  Review status, pending points, public profile queue, and next contribution prompt.
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </>
  );
}

Object.assign(window, {
  StepRoute,
  StepCapture,
  StepConfirm,
  StepSubmit,
  StepReward,
  ContributionFlow,
  ContributionFlowPath,
  CONTRIBUTION_FLOW_TYPES,
  CONTRIBUTION_FLOW_DEFS,
  normalizeContributionFlowType,
});
