/* =========================================================================
   MIZUONE — LANDING, HUB & ALUR TOPUP DIGITAL
   ========================================================================= */
/* hooks & komponen bersama berasal dari aq-core.jsx (scope global) */

/* Format no HP: 08xx xxxx xxxx */
const formatPhone = (v) => {
  const d = v.replace(/\D/g,'').slice(0,13);
  if(d.length<=4) return d;
  if(d.length<=8) return d.slice(0,4)+' '+d.slice(4);
  return d.slice(0,4)+' '+d.slice(4,8)+' '+d.slice(8);
};

/* =========================================================================
   MAP PICKER — Leaflet + Nominatim reverse geocode
   ========================================================================= */
function reverseGeocode(lat, lng, cb) {
  fetch('https://nominatim.openstreetmap.org/reverse?lat='+lat+'&lon='+lng+'&format=json&accept-language=id')
    .then(r=>r.json())
    .then(d=>{
      const a = d.address || {};
      cb({
        lat, lng,
        display_name: d.display_name || '',
        kelurahan: a.village || a.suburb || a.quarter || '',
        kecamatan: (a.county || a.city_district || '').replace(/^Kecamatan\s+/i,'').replace(/^Kec\.\s+/i,''),
        kota: a.city || a.town || a.municipality || '',
      });
    })
    .catch(()=>cb({ lat, lng, display_name:'', kelurahan:'', kecamatan:'', kota:'' }));
}

function MapPicker({ initLat, initLng, onPick }) {
  const mapRef = useRef(null);
  const leafRef = useRef(null);
  const markerRef = useRef(null);

  useEffect(()=>{
    if (!window.L || !mapRef.current || leafRef.current) return;
    const cLat = initLat || -7.9325;
    const cLng = initLng || 112.6282;
    const map = window.L.map(mapRef.current).setView([cLat, cLng], 14);
    window.L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      attribution:'© OpenStreetMap'
    }).addTo(map);
    if (initLat && initLng) {
      markerRef.current = window.L.marker([initLat, initLng]).addTo(map);
    }
    map.on('click', (e)=>{
      const { lat, lng } = e.latlng;
      if (markerRef.current) markerRef.current.setLatLng([lat, lng]);
      else markerRef.current = window.L.marker([lat, lng]).addTo(map);
      reverseGeocode(lat, lng, onPick);
    });
    leafRef.current = map;
    setTimeout(()=>map.invalidateSize(), 120);
    return ()=>{ try { map.remove(); } catch(e){} leafRef.current = null; };
  }, []);

  const doGps = ()=>{
    if (!navigator.geolocation || !leafRef.current) return;
    navigator.geolocation.getCurrentPosition(({ coords:{ latitude:lat, longitude:lng } })=>{
      leafRef.current.setView([lat, lng], 16);
      if (markerRef.current) markerRef.current.setLatLng([lat, lng]);
      else markerRef.current = window.L.marker([lat, lng]).addTo(leafRef.current);
      reverseGeocode(lat, lng, onPick);
    });
  };

  return (
    <div className="relative mb-3">
      <div ref={mapRef} style={{ height:'200px', borderRadius:'12px', border:'1.5px solid #e2e8f0', overflow:'hidden' }}></div>
      <button type="button" onClick={doGps}
        style={{ position:'absolute', bottom:'10px', right:'10px', zIndex:1000 }}
        className="bg-white rounded-xl shadow-soft px-3 py-1.5 text-xs font-bold text-mizu border border-slate-200 flex items-center gap-1.5 active:scale-95 transition">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-3.5 h-3.5"><circle cx="12" cy="12" r="3"/><path d="M12 1v4M12 19v4M1 12h4M19 12h4"/></svg>
        GPS
      </button>
    </div>
  );
}

/* ---- footer lengkap (detail seperti versi awal) ---- */
function Footer({ onSK }){
  return (
    <footer className="border-t border-slate-200/70 dark:border-slate-800 mt-auto">
      <div className={wrapWide+" px-4 py-8 grid gap-6 sm:grid-cols-2 text-sm"}>
        <div>
          <Logo className="h-8"/>
          <p className="mt-3 text-slate-500 dark:text-slate-400 max-w-xs sub">Air bersih, isi ulang sendiri, bayar QRIS — sejak 2025.</p>
          <p className="mt-3 text-xs text-slate-500 dark:text-slate-400 sub">Dikelola oleh <b className="text-slate-700 dark:text-slate-200">PT. Dinasty Sahabat Mulia</b></p>
          <p className="text-xs text-slate-400 sub">Jl. Raya Randu Agung No. 246, Singosari, Malang</p>
        </div>
        <div className="sm:text-right">
          <div className="flex flex-wrap sm:justify-end items-center gap-x-4 gap-y-2 text-slate-500 dark:text-slate-400">
            <a href={LINKS.ig} target="_blank" rel="noopener" aria-label="Instagram MizuOne" className="inline-flex items-center gap-1.5 hover:text-mizu">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" className="w-5 h-5"><rect x="3.5" y="3.5" width="17" height="17" rx="5"/><circle cx="12" cy="12" r="4"/><circle cx="17.2" cy="6.8" r="1" fill="currentColor" stroke="none"/></svg> Instagram
            </a>
            <a href="https://wa.me/6285105840777" target="_blank" rel="noopener" aria-label="WhatsApp MizuOne" className="inline-flex items-center gap-1.5 hover:text-success">
              <svg viewBox="0 0 24 24" fill="currentColor" className="w-5 h-5"><path d="M12 2a10 10 0 0 0-8.6 15l-1.3 4.7L7 20.4A10 10 0 1 0 12 2Zm5.3 14.1c-.2.6-1.3 1.2-1.8 1.2-.5.1-1 .1-1.7-.1-.4-.1-.9-.3-1.6-.6-2.8-1.2-4.6-4-4.7-4.2-.1-.2-1.1-1.5-1.1-2.8 0-1.3.7-2 .9-2.2.2-.3.5-.3.7-.3h.5c.2 0 .4 0 .6.5l.8 1.9c.1.2.1.4 0 .6l-.4.6c-.2.2-.3.3-.1.6.2.3.8 1.3 1.7 2.1 1.2 1 2.1 1.4 2.4 1.5.2.1.4.1.6-.1l.7-.9c.2-.2.4-.2.6-.1l1.8.9c.3.1.4.2.5.3 0 .2 0 .8-.2 1.3Z"/></svg> +62 851-0584-0777
            </a>
            <button onClick={onSK} className="hover:text-mizu font-inherit">S&amp;K</button>
          </div>
          <p className="mt-4 text-xs text-slate-400 sub">© 2025 MizuOne · PT. Dinasty Sahabat Mulia</p>
        </div>
      </div>
    </footer>
  );
}

/* =========================================================================
   LANDING
   ========================================================================= */
function Landing({ go }){
  const promos = [
    { badge:'PROMO PEMBUKAAN', title:'Bonus Saldo Rp 5.000', sub:'Daftar member baru & langsung dapat saldo.', cta:'Daftar Sekarang', to:'digital-daftar', img:imgSrc('mascot-salam.png'),          grad:'linear-gradient(135deg,#003D99 0%,#0066CC 58%,#1E8BE0 100%)' },
    { badge:'KHUSUS KARTU',    title:'Cashback 10%',          sub:'Tiap topup kartu fisik MizuOne kamu.',      cta:'Punya Kartu?',    to:'kartu-login',    img:imgSrc('mascot-mizuki-shoes.png'), grad:'linear-gradient(135deg,#0E7490 0%,#0891B2 50%,#21C7E8 100%)' },
    { badge:'TERBATAS',        title:'Gratis Isi Ulang',      sub:'Untuk 20 pendaftar pertama tiap hari.',    cta:'Ikut Promo',      to:'digital-daftar', img:imgSrc('mascot-mizu-shoes.png'),   grad:'linear-gradient(135deg,#312E81 0%,#4338CA 55%,#0066CC 100%)' },
  ];
  const [pi, setPi] = useState(0);
  useEffect(()=>{ const t=setInterval(()=>setPi(p=>(p+1)%promos.length), 4500); return ()=>clearInterval(t); },[]);

  return (
    <div className="fade-in" data-screen-label="Landing">
      <div className="relative">
        <WaterHeader className="rounded-b-[2rem] min-h-[18rem] sm:min-h-[20rem] lg:min-h-[22rem]">
          <div className={wrapWide+" px-5 pt-12 pb-24 sm:pb-28 text-center"}>
            <div className="max-w-2xl mx-auto">
              <img src={imgSrc('logo-mizuone-color.png')} alt="MizuOne" className="h-12 sm:h-14 w-auto mx-auto mb-5" style={{ filter:'drop-shadow(0 0 16px rgba(255,255,255,.95)) drop-shadow(0 1px 2px rgba(0,0,0,.12))' }}/>
              <h1 className="text-3xl sm:text-5xl font-extrabold tracking-tight leading-[1.05]" style={{textWrap:'balance'}}>
                Air bersih, isi ulang sendiri, bayar <span className="text-cyan-100">QRIS</span>
              </h1>
              <p className="mt-4 text-white/85 max-w-md mx-auto text-[15px] sm:text-base">Scan mesin terdekat, atur jumlah, bayar. Tanpa antre, tanpa ribet.</p>
              <p className="mt-6 text-xs text-white/70">Ketuk area ini untuk efek riak air ✨</p>
            </div>
          </div>
        </WaterHeader>
      </div>

      {/* promo strip + 2 kartu CTA */}
      <div className={wrapWide+" px-4 -mt-14 sm:-mt-16 relative z-10"}>
        {/* PROMO SLIDER — crossfade (grid-stack, tinggi otomatis) */}
        <div className="mb-4 pop relative overflow-hidden rounded-3xl shadow-soft ring-1 ring-white/15 grid">
          {promos.map((p,i)=>(
            <div key={i} style={{background:p.grad}}
              className={"col-start-1 row-start-1 relative overflow-hidden aspect-[8/3] transition-opacity duration-700 ease-in-out "+(i===pi?'opacity-100 z-[1]':'opacity-0 pointer-events-none z-0')}>
              {imgOpt('promo-banner-'+(i+1)+'.jpeg') && <img src={imgOpt('promo-banner-'+(i+1)+'.jpeg')} alt="" className="absolute inset-0 w-full h-full object-cover"/>}
              {/* scrim agar tombol selalu terbaca di atas gambar apa pun */}
              <div className="absolute inset-0 bg-gradient-to-t from-black/35 via-transparent to-transparent pointer-events-none"></div>
              {/* CTA melayang di atas gambar — area klik aman, tidak terganggu placeholder */}
              <div className="absolute left-4 bottom-4 z-10">
                <Button variant="white" size="sm" onClick={()=>go(p.to)} iconRight={<Ic.arrow className="w-4 h-4"/>}>{p.cta}</Button>
              </div>
            </div>
          ))}
          <div className="absolute bottom-3.5 right-5 flex gap-1.5 z-[2]">
            {promos.map((_,i)=>(
              <button key={i} onClick={()=>setPi(i)} aria-label={"Promo "+(i+1)} className={"h-1.5 rounded-full transition-all "+(i===pi?'w-5 bg-white':'w-1.5 bg-white/50')}></button>
            ))}
          </div>
        </div>

        {/* MOBILE: aksi cepat berbentuk ikon (selain promo) — terlihat bersih di HP */}
        <div className="sm:hidden grid grid-cols-4 gap-2 mb-1">
          <button onClick={()=>go('digital-login')} className="flex flex-col items-center gap-1.5 active:scale-95 transition">
            <span className="w-16 h-16 rounded-2xl grid place-items-center shadow-soft text-white" style={{background:'linear-gradient(135deg,#0066CC,#1E8BE0)'}}><Ic.bolt className="w-7 h-7"/></span>
            <span className="text-[11px] font-semibold text-ink text-center leading-tight">Topup Digital</span>
          </button>
          <button onClick={()=>go('kartu-login')} className="flex flex-col items-center gap-1.5 active:scale-95 transition">
            <span className="w-16 h-16 rounded-2xl bg-mizu-light text-mizu grid place-items-center shadow-card"><Ic.card className="w-7 h-7"/></span>
            <span className="text-[11px] font-semibold text-ink text-center leading-tight">Topup Kartu</span>
          </button>
          <a href="https://wa.me/6285105840777" target="_blank" rel="noopener" className="flex flex-col items-center gap-1.5 active:scale-95 transition">
            <span className="w-16 h-16 rounded-2xl grid place-items-center shadow-soft text-white bg-[#25D366]"><svg viewBox="0 0 24 24" fill="currentColor" className="w-7 h-7"><path d="M12 2a10 10 0 0 0-8.6 15l-1.3 4.7L7 20.4A10 10 0 1 0 12 2Zm5.3 14.1c-.2.6-1.3 1.2-1.8 1.2-.5.1-1 .1-1.7-.1-.4-.1-.9-.3-1.6-.6-2.8-1.2-4.6-4-4.7-4.2-.1-.2-1.1-1.5-1.1-2.8 0-1.3.7-2 .9-2.2.2-.3.5-.3.7-.3h.5c.2 0 .4 0 .6.5l.8 1.9c.1.2.1.4 0 .6l-.4.6c-.2.2-.3.3-.1.6.2.3.8 1.3 1.7 2.1 1.2 1 2.1 1.4 2.4 1.5.2.1.4.1.6-.1l.7-.9c.2-.2.4-.2.6-.1l1.8.9c.3.1.4.2.5.3 0 .2 0 .8-.2 1.3Z"/></svg></span>
            <span className="text-[11px] font-semibold text-ink text-center leading-tight">WhatsApp</span>
          </a>
          <a href={LINKS.tg} target="_blank" rel="noopener" className="flex flex-col items-center gap-1.5 active:scale-95 transition">
            <span className="w-16 h-16 rounded-2xl grid place-items-center shadow-soft text-white bg-[#229ED9]"><svg viewBox="0 0 24 24" fill="currentColor" className="w-7 h-7"><path d="M21.9 4.5 18.7 19c-.2 1-.9 1.3-1.7.8l-4.6-3.4-2.2 2.1c-.2.2-.4.4-.9.4l.3-4.6 8.5-7.7c.4-.3-.1-.5-.6-.2L7.3 13.4l-4.5-1.4c-1-.3-1-1 .2-1.4l17.6-6.8c.8-.3 1.5.2 1.3 1.3Z"/></svg></span>
            <span className="text-[11px] font-semibold text-ink text-center leading-tight">Telegram</span>
          </a>
        </div>

        <div className="hidden sm:grid md:grid-cols-2 gap-4">
          {/* Topup Digital — promo member */}
          <div className="pop relative overflow-hidden rounded-3xl p-6 text-white shadow-soft ring-1 ring-white/15 flex flex-col" style={{background:'linear-gradient(135deg,#003D99 0%,#0066CC 60%,#1E8BE0 100%)'}}>
            <div className="wh-glint" aria-hidden="true"></div>
            <div className="relative">
              <div className="w-12 h-12 rounded-2xl bg-white/20 grid place-items-center mb-3"><Ic.bolt className="w-7 h-7"/></div>
              <h2 className="text-xl font-extrabold tracking-tight">Topup Digital</h2>
              <p className="text-white/85 text-sm mt-1">Isi saldo digital, pilih liter, bayar QRIS — tanpa kartu fisik.</p>
            </div>
            <div className="relative mt-auto pt-5"><Button variant="white" onClick={()=>go('digital-login')} iconRight={<Ic.arrow className="w-5 h-5"/>}>Mulai</Button></div>
          </div>

          {/* Topup Kartu — promo kartu */}
          <Card className="p-6 pop flex flex-col">
            <div className="w-12 h-12 rounded-2xl bg-mizu-light grid place-items-center text-mizu mb-3"><Ic.card className="w-7 h-7"/></div>
            <h2 className="text-xl font-extrabold tracking-tight">Topup Kartu</h2>
            <p className="text-slate-500 text-sm mt-1 sub">Punya kartu fisik MizuOne? Topup saldo &amp; tap di mesin untuk beli air.</p>
            <div className="mt-auto pt-5"><Button variant="secondary" onClick={()=>go('kartu-login')} iconRight={<Ic.arrow className="w-5 h-5"/>}>Mulai</Button></div>
          </Card>
        </div>
      </div>

      {/* ASISTEN CHAT — Mizu (WhatsApp, maskot kiri) & Mizuki (Telegram, maskot kanan) */}
      <div className={wrapWide+" px-4 mt-4 hidden sm:grid md:grid-cols-2 gap-4"}>
        {/* WhatsApp · Mizu — maskot kiri, teks kanan */}
        <Card className="relative overflow-hidden hover:border-[#25D366]/50 hover:shadow-soft transition p-4 sm:p-5">
          <div className="flex items-stretch gap-3 sm:gap-4 h-full">
            <div className="relative shrink-0 self-stretch flex items-end justify-center">
              <div className="absolute inset-x-0 bottom-1 h-16 bg-[#25D366]/12 rounded-full blur-xl" aria-hidden="true"></div>
              <img src={imgSrc('mascot-mizu-shoes.png')} alt="Maskot Mizu" className="relative h-48 sm:h-56 w-auto drop-shadow-md"/>
            </div>
            <div className="flex-1 min-w-0 pt-1">
              <div className="relative bg-[#E7FCE3] rounded-2xl rounded-bl-md px-4 py-3 shadow-sm">
                <span className="absolute -left-1.5 top-4 w-3 h-3 bg-[#E7FCE3] rotate-45 rounded-[2px]" aria-hidden="true"></span>
                <p className="relative text-[15px] leading-snug text-slate-700"><b>Kenalkan, saya Mizu</b> — Asisten AI yang membantu pembelian air Anda melalui WhatsApp. 💧</p>
                <span className="block text-[11px] text-[#128C3E] font-semibold mt-1.5">Mizu · online</span>
              </div>
              <a href="https://wa.me/6285105840777" target="_blank" rel="noopener" className="inline-flex items-center gap-2 mt-3 px-4 py-2.5 rounded-xl bg-[#25D366] text-white font-semibold shadow-soft hover:bg-[#1eb958] active:scale-[.98] transition">
                <svg viewBox="0 0 24 24" fill="currentColor" className="w-5 h-5"><path d="M12 2a10 10 0 0 0-8.6 15l-1.3 4.7L7 20.4A10 10 0 1 0 12 2Zm5.3 14.1c-.2.6-1.3 1.2-1.8 1.2-.5.1-1 .1-1.7-.1-.4-.1-.9-.3-1.6-.6-2.8-1.2-4.6-4-4.7-4.2-.1-.2-1.1-1.5-1.1-2.8 0-1.3.7-2 .9-2.2.2-.3.5-.3.7-.3h.5c.2 0 .4 0 .6.5l.8 1.9c.1.2.1.4 0 .6l-.4.6c-.2.2-.3.3-.1.6.2.3.8 1.3 1.7 2.1 1.2 1 2.1 1.4 2.4 1.5.2.1.4.1.6-.1l.7-.9c.2-.2.4-.2.6-.1l1.8.9c.3.1.4.2.5.3 0 .2 0 .8-.2 1.3Z"/></svg>
                Chat WhatsApp
              </a>
            </div>
          </div>
        </Card>

        {/* Telegram · Mizuki — teks kiri, maskot kanan (cermin) */}
        <Card className="relative overflow-hidden hover:border-[#229ED9]/50 hover:shadow-soft transition p-4 sm:p-5">
          <div className="flex items-stretch gap-3 sm:gap-4 h-full">
            <div className="flex-1 min-w-0 pt-1 text-right">
              <div className="relative bg-[#E7F3FB] rounded-2xl rounded-br-md px-4 py-3 shadow-sm">
                <span className="absolute -right-1.5 top-4 w-3 h-3 bg-[#E7F3FB] rotate-45 rounded-[2px]" aria-hidden="true"></span>
                <p className="relative text-[15px] leading-snug text-slate-700"><b>Hai, saya Mizuki</b> — Asisten AI yang membantu pembelian air Anda melalui Telegram. 💧</p>
                <span className="block text-[11px] text-[#1c7fb8] font-semibold mt-1.5">Mizuki · online</span>
              </div>
              <a href={LINKS.tg} target="_blank" rel="noopener" className="inline-flex items-center gap-2 mt-3 px-4 py-2.5 rounded-xl bg-[#229ED9] text-white font-semibold shadow-soft hover:bg-[#1c8cc2] active:scale-[.98] transition">
                <svg viewBox="0 0 24 24" fill="currentColor" className="w-5 h-5"><path d="M21.9 4.5 18.7 19c-.2 1-.9 1.3-1.7.8l-4.6-3.4-2.2 2.1c-.2.2-.4.4-.9.4l.3-4.6 8.5-7.7c.4-.3-.1-.5-.6-.2L7.3 13.4l-4.5-1.4c-1-.3-1-1 .2-1.4l17.6-6.8c.8-.3 1.5.2 1.3 1.3Z"/></svg>
                Chat Telegram
              </a>
            </div>
            <div className="relative shrink-0 self-stretch flex items-end justify-center">
              <div className="absolute inset-x-0 bottom-1 h-16 bg-[#229ED9]/12 rounded-full blur-xl" aria-hidden="true"></div>
              <img src={imgSrc('mascot-mizuki-shoes.png')} alt="Maskot Mizuki" className="relative h-44 sm:h-52 w-auto drop-shadow-md"/>
            </div>
          </div>
        </Card>
      </div>

      <div className="h-12"></div>
    </div>
  );
}

/* =========================================================================
   DIGITAL · LOGIN (HP + OTP inline)
   ========================================================================= */
function DigitalLogin({ go, back, onAuthed }){
  const [phone, setPhone] = useState('');
  const [sent, setSent] = useState(false);
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');
  const [otp, setOtp] = useState(['','','','']);
  const refs = useRef([]);
  const filledRef = useRef(false);
  const setDigit = (i,v)=>{ v=v.replace(/\D/g,'').slice(-1); let nf=false; setOtp(o=>{const n=[...o];n[i]=v;filledRef.current=n.every(d=>d!=='');nf=filledRef.current;return n;}); if(v&&i<3) refs.current[i+1]?.focus(); if(nf) setTimeout(doVerify,60); };
  const filled = otp.every(d=>d!=='');
  const phoneOk = phone.replace(/\D/g,'').length>=8;
  const doKirimOtp = async ()=>{ if(!phoneOk||busy) return; setErr(''); setBusy(true); try{ await window.AquAPI.requestOtp(phone); setSent(true); }catch(e){ setErr(e.message||'Gagal kirim OTP'); } setBusy(false); };
  const doVerify = async ()=>{ if(!filledRef.current||busy) return; setErr(''); setBusy(true); try{ const r=await window.AquAPI.verifyOtp(phone, otp.join('')); if(r.need_name){ window.__mzu_phone=phone; window.__mzu_otp=otp.join(''); go('digital-daftar'); } else { window.AquAPI.setToken(r.token); onAuthed(r); } }catch(e){ setErr(e.message||'OTP salah'); setBusy(false); } };
  const onKey = (i,e)=>{ if(e.key==='Backspace'&&!otp[i]&&i>0) refs.current[i-1]?.focus(); if(e.key==='Enter') doVerify(); };
  return (
    <div className="relative fade-in" data-screen-label="DigitalLogin">
      <WaterHeader className="absolute top-0 left-0 right-0 h-44 rounded-b-[2rem]"><div className="h-44"></div></WaterHeader>
      <div className={"relative "+wrapNarrow+" px-4 pt-5 pb-12"}>
        <div className="relative z-10"><ScreenTop onBack={back} title="Masuk Akun Digital" light/></div>
        <Card className="p-6 pop">
          <div className="w-12 h-12 rounded-2xl bg-mizu-light text-mizu grid place-items-center mb-3"><Ic.phone className="w-6 h-6"/></div>
          <h2 className="text-xl font-extrabold">Masuk dengan No HP</h2>
          <p className="text-slate-500 text-sm mt-1 mb-5 sub">Tanpa password. Kami kirim kode OTP ke WhatsApp kamu.</p>

          <label className="block">
            <span className="block text-sm font-semibold mb-1.5">Nomor HP</span>
            <div className="flex">
              <span className="inline-flex items-center px-3 rounded-l-xl border border-r-0 border-slate-300 dark:border-slate-600 bg-slate-50 dark:bg-slate-800 text-slate-500 text-sm font-semibold">+62</span>
              <input value={phone} onChange={e=>setPhone(formatPhone(e.target.value))} onKeyDown={e=>e.key==='Enter'&&doKirimOtp()} className={inputCls+" rounded-l-none"} inputMode="numeric" placeholder="812 3456 7890"/>
            </div>
          </label>

          {!sent ? (
            <Button size="lg" className="w-full mt-4" disabled={!phoneOk||busy} onClick={doKirimOtp} iconRight={<Ic.arrow className="w-5 h-5"/>}>{busy?'Mengirim…':'Kirim OTP'}</Button>
          ) : (
            <div className="mt-5 fade-in">
              <span className="block text-sm font-semibold mb-2">Kode OTP <span className="text-slate-400 font-normal">— dikirim ke WhatsApp</span></span>
              <div className="flex gap-2.5">
                {otp.map((d,i)=>(
                  <input key={i} ref={el=>refs.current[i]=el} value={d} onChange={e=>setDigit(i,e.target.value)} onKeyDown={e=>onKey(i,e)} autoFocus={i===0}
                    inputMode="numeric" maxLength="1" aria-label={"Digit OTP "+(i+1)}
                    className="w-full h-14 text-center text-2xl font-extrabold rounded-xl border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 outline-none focus:border-mizu focus:ring-4 focus:ring-mizu/15 transition"/>
                ))}
              </div>
              <Button size="lg" className="w-full mt-5" disabled={!filled||busy} onClick={doVerify} icon={<Ic.check className="w-5 h-5"/>}>{busy?'Memproses…':'Masuk'}</Button>
              {err && <p className="text-danger text-sm mt-3 text-center font-semibold">{err}</p>}
              <OtpResend seconds={60} onResend={()=>{ window.AquAPI.requestOtp(phone).catch(()=>{}); }}/>
              <button onClick={()=>{ setSent(false); setOtp(['','','','']); setErr(''); }} className="block mx-auto mt-2 text-sm font-semibold text-slate-400 hover:text-mizu">← Ubah nomor HP</button>
              <p className="text-center text-xs text-slate-400 mt-3 flex items-center justify-center gap-1.5"><Ic.shield className="w-4 h-4"/> Jangan berikan kode OTP kepada siapa pun.</p>
            </div>
          )}

          <hr className="my-5 border-slate-100 dark:border-slate-800"/>
          <p className="text-center text-sm text-slate-500 sub">Belum punya akun? <button onClick={()=>go('digital-daftar')} className="font-semibold text-mizu hover:underline">Daftar gratis</button></p>
        </Card>
      </div>
    </div>
  );
}

/* =========================================================================
   DIGITAL · DAFTAR (Nama + No HP → OTP → buat akun)
   ========================================================================= */
function DaftarDigital({ go, back, onAuthed }){
  const [nohp] = useState(()=>{ const p=window.__mzu_phone||''; window.__mzu_phone=''; return p; });
  const [savedOtp] = useState(()=>{ const o=window.__mzu_otp||''; window.__mzu_otp=''; return o; });
  const quickDaftar = savedOtp !== '';
  const [step, setStep] = useState(quickDaftar ? 3 : 1);
  const [nama, setNama] = useState('');
  const [nohpInput, setNohpInput] = useState(nohp);
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');
  const [otp, setOtp] = useState(['','','','']);
  const refs = useRef([]);
  const setDigit = (i,v)=>{ v=v.replace(/\D/g,'').slice(-1); setOtp(o=>{const n=[...o];n[i]=v;return n;}); if(v&&i<3) refs.current[i+1]?.focus(); };
  const onKey = (i,e)=>{ if(e.key==='Backspace'&&!otp[i]&&i>0) refs.current[i-1]?.focus(); };
  const formOk = nama.trim().length>=2 && nohpInput.replace(/\D/g,'').length>=8;
  const filled = otp.every(d=>d!=='');
  const filledRef2 = useRef(false);
  const setDigit2 = (i,v)=>{ v=v.replace(/\D/g,'').slice(-1); let nf=false; setOtp(o=>{const n=[...o];n[i]=v;filledRef2.current=n.every(d=>d!=='');nf=filledRef2.current;return n;}); if(v&&i<3) refs.current[i+1]?.focus(); if(nf) setTimeout(()=>doDaftar(),60); };
  const doKirimOtpDaftar = async ()=>{ if(!formOk||busy) return; setErr(''); setBusy(true); try{ await window.AquAPI.requestOtp(nohpInput); setStep(2); }catch(e){ setErr(e.message||'Gagal kirim OTP'); } setBusy(false); };
  const doDaftar = async (n=nama)=>{ if(!filledRef2.current||busy) return; setErr(''); setBusy(true); try{ const r=await window.AquAPI.verifyOtp(nohpInput, otp.join(''), {name: n}); window.AquAPI.setToken(r.token); onAuthed(r); }catch(e){ setErr(e.message||'OTP salah'); setBusy(false); } };
  const [alamatInput, setAlamatInput] = useState('');
  const [kelurahan, setKelurahan] = useState('');
  const [kecamatan, setKecamatan] = useState('');
  const [kota, setKota] = useState('');
  const [lat, setLat] = useState(null);
  const [lng, setLng] = useState(null);
  const [showMap, setShowMap] = useState(false);
  const [avatarFile, setAvatarFile] = useState(null);
  const pickAvatar = (e)=>{ const f=e.target.files?.[0]; if(!f) return; const rd=new FileReader(); rd.onload=ev=>setAvatarFile({preview:ev.target.result, base64:ev.target.result}); rd.readAsDataURL(f); };
  const onMapPick = (d)=>{ setLat(d.lat); setLng(d.lng); if(d.display_name && !alamatInput) setAlamatInput(d.display_name.split(',').slice(0,3).join(',').trim()); if(d.kelurahan) setKelurahan(d.kelurahan); if(d.kecamatan) setKecamatan(d.kecamatan); if(d.kota) setKota(d.kota); };
  const doQuickDaftar = async ()=>{ if(nama.trim().length<2||alamatInput.trim().length<3||busy) return; setErr(''); setBusy(true); try{ const r=await window.AquAPI.verifyOtp(nohp, savedOtp, {name: nama, alamat: alamatInput, kelurahan, kecamatan, kota, lat, lng}); window.AquAPI.setToken(r.token); if(avatarFile){ try{ const av=await window.AquAPI.uploadAvatar(avatarFile.base64); r.user.avatar_url=av.avatar_url; }catch(e){} } localStorage.setItem('mzu_new_user','1'); onAuthed(r); }catch(e){ setErr(e.message||'Gagal mendaftar'); setBusy(false); } };
  const onKeyDaftar = (i,e)=>{ if(e.key==='Backspace'&&!otp[i]&&i>0) refs.current[i-1]?.focus(); if(e.key==='Enter') doDaftar(); };
  const doBack = ()=>{
    if(step===1) back();
    else if(step===2) setStep(1);
    else { quickDaftar ? go('digital-login') : setStep(2); }
  };
  return (
    <div className="relative fade-in" data-screen-label="DaftarDigital">
      <WaterHeader className="absolute top-0 left-0 right-0 h-44 rounded-b-[2rem]"><div className="h-44"></div></WaterHeader>
      <div className={"relative "+wrapNarrow+" px-4 pt-5 pb-12"}>
        <div className="relative z-10"><ScreenTop onBack={doBack} title="Daftar Akun Digital" light/></div>
        <Card className="p-6 pop">
          {step===3 ? (
            <React.Fragment>
              <div className="w-12 h-12 rounded-2xl bg-success/12 text-success grid place-items-center mb-3"><Ic.check className="w-6 h-6"/></div>
              <h2 className="text-xl font-extrabold">Lengkapi profil kamu</h2>
              <p className="text-slate-500 text-sm mt-1 mb-4 sub">Nomor <b className="text-ink dark:text-slate-100">+62 {nohp}</b> terverifikasi.</p>

              {/* Foto profil */}
              <div className="flex flex-col items-center mb-5">
                <label className="cursor-pointer group relative">
                  <div className="w-20 h-20 rounded-full bg-slate-100 dark:bg-slate-800 border-2 border-dashed border-slate-300 dark:border-slate-600 group-hover:border-mizu overflow-hidden grid place-items-center transition">
                    {avatarFile
                      ? <img src={avatarFile.preview} className="w-full h-full object-cover" alt="foto"/>
                      : <Ic.user className="w-8 h-8 text-slate-400"/>}
                  </div>
                  <div className="absolute -bottom-1 -right-1 w-7 h-7 rounded-full bg-mizu text-white grid place-items-center shadow-soft"><Ic.plus className="w-4 h-4"/></div>
                  <input type="file" accept="image/*" className="sr-only" onChange={pickAvatar}/>
                </label>
                <span className="mt-2 text-xs text-slate-400">Foto profil (opsional)</span>
              </div>

              <label className="block mb-4">
                <span className="block text-sm font-semibold mb-1.5">Nama lengkap <span className="text-danger">*</span></span>
                <input autoFocus value={nama} onChange={e=>setNama(e.target.value)} onKeyDown={e=>e.key==='Enter'&&document.getElementById('inp-alamat-reg')?.focus()} className={inputCls} placeholder="cth. Siti Rahma"/>
              </label>
              <label className="block mb-3">
                <span className="block text-sm font-semibold mb-1.5">Alamat <span className="text-danger">*</span></span>
                <input id="inp-alamat-reg" value={alamatInput} onChange={e=>setAlamatInput(e.target.value)} onKeyDown={e=>e.key==='Enter'&&document.getElementById('inp-kel-reg')?.focus()} className={inputCls} placeholder="cth. Jl. Merdeka No. 1"/>
              </label>

              {/* Tombol peta */}
              <button type="button" onClick={()=>setShowMap(m=>!m)}
                className="mb-3 inline-flex items-center gap-1.5 text-xs font-semibold text-mizu hover:underline">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-4 h-4"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>
                {showMap ? 'Sembunyikan Peta' : 'Pilih Lokasi di Peta'}{lat ? ' ✓' : ''}
              </button>
              {showMap && <MapPicker initLat={lat} initLng={lng} onPick={onMapPick}/>}

              <div className="grid grid-cols-2 gap-3 mb-3">
                <label className="block">
                  <span className="block text-sm font-semibold mb-1.5">Kelurahan</span>
                  <input id="inp-kel-reg" value={kelurahan} onChange={e=>setKelurahan(e.target.value)} onKeyDown={e=>e.key==='Enter'&&document.getElementById('inp-kec-reg')?.focus()} className={inputCls} placeholder="Singosari"/>
                </label>
                <label className="block">
                  <span className="block text-sm font-semibold mb-1.5">Kecamatan</span>
                  <input id="inp-kec-reg" value={kecamatan} onChange={e=>setKecamatan(e.target.value)} onKeyDown={e=>e.key==='Enter'&&document.getElementById('inp-kota-reg')?.focus()} className={inputCls} placeholder="Singosari"/>
                </label>
              </div>
              <label className="block mb-5">
                <span className="block text-sm font-semibold mb-1.5">Kota / Kabupaten</span>
                <input id="inp-kota-reg" value={kota} onChange={e=>setKota(e.target.value)} onKeyDown={e=>e.key==='Enter'&&doQuickDaftar()} className={inputCls} placeholder="Malang"/>
              </label>

              <Button size="lg" className="w-full" disabled={nama.trim().length<2||alamatInput.trim().length<3||busy} onClick={doQuickDaftar} icon={<Ic.check className="w-5 h-5"/>}>{busy?'Mendaftar…':'Daftar & Masuk'}</Button>
              {err && <p className="text-danger text-sm mt-3 text-center font-semibold">{err}</p>}
              <p className="text-center text-xs text-slate-400 mt-3 sub">Dengan mendaftar kamu setuju S&amp;K &amp; Kebijakan Privasi.</p>
            </React.Fragment>
          ) : step===1 ? (
            <React.Fragment>
              <div className="flex items-center gap-3 mb-4">
                <img src={imgSrc('mascot-mizuki-shoes.png')} alt="" aria-hidden="true" className="h-16 w-auto -my-1"/>
                <div>
                  <h2 className="text-xl font-extrabold">Buat akun — gratis</h2>
                  <p className="text-slate-500 text-sm mt-0.5 sub">Bonus saldo Rp 5.000 untuk member baru 🎁</p>
                </div>
              </div>

              <label className="block mb-4">
                <span className="block text-sm font-semibold mb-1.5">Nama lengkap</span>
                <input value={nama} onChange={e=>setNama(e.target.value)} className={inputCls} placeholder="cth. Siti Rahma"/>
              </label>
              <label className="block">
                <span className="block text-sm font-semibold mb-1.5">Nomor HP (WhatsApp)</span>
                <div className="flex">
                  <span className="inline-flex items-center px-3 rounded-l-xl border border-r-0 border-slate-300 dark:border-slate-600 bg-slate-50 dark:bg-slate-800 text-slate-500 text-sm font-semibold">+62</span>
                  <input value={nohpInput} onChange={e=>setNohpInput(formatPhone(e.target.value))} onKeyDown={e=>e.key==='Enter'&&doKirimOtpDaftar()} className={inputCls+" rounded-l-none"} inputMode="numeric" placeholder="812 3456 7890"/>
                </div>
              </label>
              <Button size="lg" className="w-full mt-4" disabled={!formOk||busy} onClick={doKirimOtpDaftar} iconRight={<Ic.arrow className="w-5 h-5"/>}>{busy?'Mengirim…':'Kirim OTP'}</Button>
              {err && <p className="text-danger text-sm mt-2 text-center font-semibold">{err}</p>}
              <p className="text-center text-xs text-slate-400 mt-3 sub">Dengan mendaftar kamu setuju S&amp;K &amp; Kebijakan Privasi.</p>
            </React.Fragment>
          ) : (
            <React.Fragment>
              <div className="w-12 h-12 rounded-2xl bg-success/12 text-success grid place-items-center mb-3"><Ic.shield className="w-6 h-6"/></div>
              <h2 className="text-xl font-extrabold">Verifikasi nomor</h2>
              <p className="text-slate-500 text-sm mt-1 mb-5 sub">Kode OTP dikirim ke WhatsApp <b className="text-ink dark:text-slate-100">+62 {nohpInput||'812 3456 7890'}</b></p>
              <div className="flex gap-2.5">
                {otp.map((d,i)=>(
                  <input key={i} ref={el=>refs.current[i]=el} value={d} onChange={e=>setDigit2(i,e.target.value)} onKeyDown={e=>onKeyDaftar(i,e)} autoFocus={i===0}
                    inputMode="numeric" maxLength="1" aria-label={"Digit OTP "+(i+1)}
                    className="w-full h-14 text-center text-2xl font-extrabold rounded-xl border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 outline-none focus:border-mizu focus:ring-4 focus:ring-mizu/15 transition"/>
                ))}
              </div>
              <Button size="lg" className="w-full mt-5" disabled={!filled||busy} onClick={()=>doDaftar()} icon={<Ic.check className="w-5 h-5"/>}>{busy?'Memproses…':'Daftar & Masuk'}</Button>
              {err && <p className="text-danger text-sm mt-3 text-center font-semibold">{err}</p>}
              <OtpResend seconds={60} onResend={()=>{ window.AquAPI.requestOtp(nohpInput).catch(()=>{}); }}/>
              <p className="text-center text-sm mt-3 text-slate-500 sub">Sudah punya akun? <button onClick={()=>go('digital-login')} className="font-semibold text-mizu hover:underline">Masuk</button></p>
            </React.Fragment>
          )}
        </Card>
      </div>
    </div>
  );
}

/* =========================================================================
   DIGITAL · HOME
   ========================================================================= */
/* Modal transfer saldo digital → kartu fisik */
function TransferKeKartuModal({ open, kartu, saldo, onClose, onDone, notice }){
  const [sel, setSel] = useState(null);
  const [custom, setCustom] = useState('');
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');
  const [askPin, setAskPin] = useState(false);
  const presets = [10000,25000,50000,100000];
  const activeCard = sel || (kartu && kartu[0]);
  const amount = custom ? (parseInt(custom)||0) : 25000;
  const [selAmt, setSelAmt] = useState(25000);
  const finalAmt = custom ? (parseInt(custom)||0) : selAmt;

  useEffect(()=>{ if(open){ setCustom(''); setSelAmt(25000); setErr(''); setBusy(false); setSel(kartu&&kartu[0]||null); setAskPin(false); } },[open]);

  const doTransfer = async()=>{
    if(!activeCard){ setErr('Pilih kartu tujuan'); return; }
    if(finalAmt < 10000){ setErr('Minimal transfer Rp 10.000'); return; }
    if(finalAmt > saldo){ setErr('Saldo digital tidak cukup'); return; }
    setBusy(true); setErr('');
    try {
      const r = await window.AquAPI.transferToCard(activeCard.nokartu, finalAmt);
      if(r.status === 'success'){
        notice && notice('Transfer berhasil! Tap kartu ke mesin untuk aktifkan.');
        onDone && onDone(r.balance);
      } else { setErr(r.message || 'Transfer gagal'); }
    } catch(e){ setErr(e.message || 'Gagal transfer'); }
    setBusy(false);
  };

  if(!open) return null;
  return (
    <React.Fragment>
    <div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center p-0 sm:p-4">
      <div className="absolute inset-0 bg-slate-900/55 backdrop-blur-sm" onClick={onClose}></div>
      <Card className="relative w-full sm:max-w-sm pop rounded-b-none sm:rounded-2xl p-6 overflow-hidden">
        <div className="flex items-center justify-between mb-4">
          <h3 className="font-extrabold text-lg">Transfer ke Kartu</h3>
          <button onClick={onClose} className="w-8 h-8 grid place-items-center rounded-lg text-slate-400 hover:bg-slate-100">✕</button>
        </div>
        {/* Pilih kartu jika lebih dari satu */}
        {kartu && kartu.length > 1 && (
          <div className="mb-4">
            <div className="text-sm font-semibold mb-2">Pilih kartu tujuan</div>
            {kartu.map(c=>(
              <button key={c.nokartu} onClick={()=>setSel(c)}
                className={"w-full flex items-center gap-3 px-3 py-2.5 rounded-xl border-2 mb-1.5 transition "+(activeCard?.nokartu===c.nokartu?'border-mizu bg-mizu-light':'border-slate-200 dark:border-slate-700 hover:border-mizu/40')}>
                <div className="w-8 h-8 rounded-lg bg-mizu-light text-mizu grid place-items-center shrink-0"><Ic.tap className="w-4 h-4"/></div>
                <span className="font-bold font-mono text-sm">•••• {String(c.nokartu).slice(-4)}</span>
              </button>
            ))}
          </div>
        )}
        {kartu && kartu.length === 1 && (
          <div className="flex items-center gap-2 p-3 rounded-xl bg-slate-50 dark:bg-slate-800/60 mb-4">
            <div className="w-8 h-8 rounded-lg bg-mizu-light text-mizu grid place-items-center shrink-0"><Ic.tap className="w-4 h-4"/></div>
            <span className="font-bold font-mono text-sm">Kartu •••• {String(kartu[0].nokartu).slice(-4)}</span>
          </div>
        )}
        {/* Pilih nominal */}
        <div className="text-sm font-semibold mb-2">Nominal transfer</div>
        <div className="grid grid-cols-2 gap-2 mb-3">
          {presets.map(v=>(
            <button key={v} onClick={()=>{setSelAmt(v);setCustom('');setErr('');}}
              className={"rounded-xl border-2 py-2.5 font-bold text-sm tabular-nums transition "+(!custom&&selAmt===v?'border-mizu bg-mizu-light text-mizu-700':'border-slate-200 dark:border-slate-700 hover:border-mizu/40')}>
              {fmtRp(v)}
            </button>
          ))}
        </div>
        <div className={"flex items-center rounded-xl border-2 px-3 min-h-[48px] mb-1 transition "+(custom?'border-mizu':'border-slate-200 dark:border-slate-700')}>
          <span className="text-slate-400 font-semibold mr-1 text-sm">Rp</span>
          <input type="number" inputMode="numeric" placeholder="Jumlah lain" value={custom}
            onChange={e=>{setCustom(e.target.value);setErr('');}}
            className="flex-1 bg-transparent outline-none text-sm font-bold tabular-nums placeholder:text-slate-300"/>
        </div>
        <div className="flex justify-between text-xs text-slate-400 mb-4 px-1">
          <span>Saldo digital: <b className="text-ink dark:text-slate-200">{fmtRp(saldo)}</b></span>
          <span>Setelah: <b className="text-ink dark:text-slate-200">{fmtRp(Math.max(0, saldo - finalAmt))}</b></span>
        </div>
        {err && <p className="text-danger text-sm mb-3 text-center font-semibold">{err}</p>}
        <Button size="lg" className="w-full" disabled={finalAmt<10000||busy||finalAmt>saldo} onClick={()=>setAskPin(true)} icon={<Ic.tap className="w-5 h-5"/>}>
          {busy?'Memproses…':'Transfer ke Kartu'}
        </Button>
        <p className="text-xs text-slate-400 text-center mt-2 sub">Saldo kartu aktif setelah tap ke mesin MizuOne</p>
      </Card>
    </div>
    <PinSheet open={askPin} onClose={()=>setAskPin(false)}
      onVerify={async(pin)=>{ try{ return await window.AquAPI.verifyPin(pin); }catch(e){ return null; } }}
      onSuccess={()=>{ setAskPin(false); doTransfer(); }}
      title="Konfirmasi Transfer"
      subtitle={"Transfer "+fmtRp(finalAmt)+" ke kartu fisik MizuOne."}/>
    </React.Fragment>
  );
}

function DigitalHome({ go, notice, saldo, onOpen, user, onRefresh, onGoKartu }){
  const [riwayat, setRiwayat] = useState([]);
  const [kartu, setKartu] = useState(null); // null=belum load, []=tidak punya
  const [trfModal, setTrfModal] = useState(false);
  const prevSaldo = useRef(saldo);
  const [saldoFlash, setSaldoFlash] = useState(false);
  const [refreshing, setRefreshing] = useState(false);
  const [showWelcome, setShowWelcome] = useState(()=>localStorage.getItem('mzu_new_user')==='1');

  useEffect(()=>{ if(showWelcome) localStorage.removeItem('mzu_new_user'); }, []);
  useEffect(()=>{
    if(saldo !== prevSaldo.current && prevSaldo.current > 0){
      setSaldoFlash(true);
      setTimeout(()=>setSaldoFlash(false), 900);
    }
    prevSaldo.current = saldo;
  }, [saldo]);
  useEffect(()=>{
    if(!(window.AquAPI && window.AquAPI.hasToken())) return;
    window.AquAPI.history().then(r=>{
      setRiwayat((r.data||[]).slice(0,3).map(x=>{
        const dt=new Date(x.created_at);
        const tp=x.type==='topup'?'topup':x.type==='transfer_to_card'?'transfer':'beli';
        return { type:tp, amt:x.amount, orderId:x.order_id, liter:x.volume_ml?Math.round(x.volume_ml/100)/10:undefined, d:dt.toLocaleDateString('id-ID',{day:'numeric',month:'short'}), time:dt.toLocaleTimeString('id-ID',{hour:'2-digit',minute:'2-digit'}), via:x.type==='topup'?'QRIS Faspay':x.type==='transfer_to_card'?'Saldo digital → Kartu':'Saldo digital' };
      }));
    }).catch(()=>setRiwayat([]));
    // Load kartu terhubung
    window.AquAPI.kartuList().then(r=>setKartu(r.data||[])).catch(()=>setKartu([]));
  },[]);

  const doRefresh = ()=>{ if(refreshing||!onRefresh) return; setRefreshing(true); onRefresh(); setTimeout(()=>setRefreshing(false), 1200); };

  return (
    <div className="fade-in" data-screen-label="DigitalHome">
      <WaterHeader className="rounded-b-2xl"><div className={wrapNarrow+" px-5 pt-6 pb-10"}><p className="text-white/80 text-sm">Selamat datang,</p><h1 className="text-2xl font-extrabold tracking-tight">Halo, {(user?.name||'Member').split(' ')[0]}! 👋</h1></div></WaterHeader>

      <div className={wrapNarrow+" px-4 -mt-8 relative z-10 pb-6"}>
        {/* banner member baru */}
        {showWelcome && (
          <div className="relative overflow-hidden rounded-2xl p-4 mb-3 pop" style={{background:'linear-gradient(135deg,#003D99,#1E8BE0)'}}>
            <button onClick={()=>setShowWelcome(false)} style={{position:'absolute',top:'8px',right:'8px'}} className="w-7 h-7 grid place-items-center rounded-full bg-white/20 text-white text-lg leading-none hover:bg-white/30 transition">×</button>
            <div className="flex items-center gap-3">
              <img src={imgSrc('mascot-mizuki-shoes.png')} alt="" className="h-16 w-auto -ml-1 shrink-0"/>
              <div>
                <p className="font-extrabold text-white text-sm">Selamat datang! 🎉</p>
                <p className="text-white/85 text-xs mt-0.5">Akun berhasil dibuat. Bonus saldo Rp 5.000 sudah masuk ke dompetmu.</p>
              </div>
            </div>
          </div>
        )}

        {/* banner belum punya PIN */}
        {user && user.has_pin === false && (
          <button onClick={()=>go('ubah-pin')} className="w-full text-left pop mb-3 rounded-2xl px-4 py-3.5 flex items-center gap-3" style={{background:'linear-gradient(135deg,#B45309,#D97706)'}}>
            <span className="text-2xl shrink-0">🔐</span>
            <div className="flex-1 min-w-0">
              <p className="font-extrabold text-white text-sm">Buat PIN dulu!</p>
              <p className="text-amber-100 text-xs mt-0.5">PIN diperlukan untuk membeli air. Tap di sini untuk buat PIN.</p>
            </div>
            <Ic.arrow className="w-5 h-5 text-amber-200 shrink-0"/>
          </button>
        )}

        {/* kartu digital — gambar Kota Malang */}
        <div className="pop mx-5 relative overflow-hidden rounded-2xl shadow-soft">
          <img src={imgSrc('kartu-mizuone-digital.jpeg')} alt="Kartu Digital MizuOne"
            className="w-full h-auto block"/>
          {/* overlay atas: label + refresh */}
          <div className="absolute inset-x-0 top-0 bg-gradient-to-b from-black/55 to-transparent px-3.5 pt-3 pb-6 flex items-center justify-between">
            <span className="text-white text-sm font-bold tracking-wide drop-shadow">Kartu Digital MizuOne</span>
            <button onClick={doRefresh} aria-label="Refresh saldo"
              className={"w-7 h-7 grid place-items-center rounded-full bg-white/20 backdrop-blur-sm border border-white/25 text-white active:scale-95 transition "+(refreshing?'animate-spin':'')}>
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" className="w-3 h-3"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/></svg>
            </button>
          </div>
          {/* overlay bawah: saldo + tombol */}
          <div className="absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/80 via-black/45 to-transparent px-3.5 pt-7 pb-3">
            <div className="text-white/70 text-[10px] uppercase tracking-wide font-bold">Saldo</div>
            <div className={"text-2xl font-extrabold text-white tabular-nums drop-shadow leading-tight "+(saldoFlash?'saldo-glow':'')}>
              {fmtRp(saldo)}
            </div>
            <div className="grid grid-cols-2 gap-2 mt-2">
              <button onClick={()=>go('digital-topup')}
                className="inline-flex items-center justify-center gap-1.5 font-semibold rounded-xl min-h-[38px] px-3 bg-white text-mizu active:scale-[.98] transition text-sm shadow">
                <Ic.plus className="w-4 h-4"/> Topup
              </button>
              <button onClick={()=>go('digital-beli')}
                className="inline-flex items-center justify-center gap-1.5 font-semibold rounded-xl min-h-[38px] px-3 text-white active:scale-[.98] transition text-sm shadow"
                style={{background:'linear-gradient(135deg,#21C7E8,#1E8BE0)'}}>
                <Ic.bolt className="w-4 h-4"/> Beli Air
              </button>
            </div>
          </div>
        </div>

        {/* kartu fisik terhubung — tampil selalu jika sudah load */}
        {kartu !== null && (
          <div className="mt-5">
            <div className="flex items-center justify-between mb-2 px-1">
              <h2 className="font-bold text-base">Kartu Fisik MizuOne</h2>
              {kartu.length > 1 && <button onClick={()=>onGoKartu&&onGoKartu(kartu[0].nokartu, null)} className="text-sm font-semibold text-mizu">Buka Kartu →</button>}
            </div>
            {kartu.length === 0 ? (
              <Card className="p-4 flex items-center gap-3">
                <div className="shrink-0 w-10 h-10 rounded-xl bg-slate-100 dark:bg-slate-800 text-slate-400 grid place-items-center"><Ic.tap className="w-5 h-5"/></div>
                <div className="flex-1 min-w-0">
                  <div className="font-semibold text-sm">Belum ada kartu terhubung</div>
                  <div className="text-xs text-slate-400">Masuk dari halaman Kartu MizuOne untuk mendaftarkan</div>
                </div>
                <button onClick={()=>go('kartu-login')} className="text-xs font-bold text-mizu px-3 py-1.5 rounded-lg bg-mizu-light shrink-0 active:scale-95 transition">Daftar</button>
              </Card>
            ) : (
              <Card className="divide-y divide-slate-100 dark:divide-slate-800 overflow-hidden">
                {kartu.slice(0,2).map((c,i)=>(
                  <div key={i} className="flex items-center gap-3 p-4">
                    <div className="shrink-0 w-10 h-10 rounded-xl bg-mizu-light text-mizu grid place-items-center"><Ic.tap className="w-5 h-5"/></div>
                    <div className="flex-1 min-w-0">
                      <div className="font-semibold text-sm">Kartu •••• {String(c.nokartu).slice(-4)}</div>
                      <div className="text-xs text-slate-400">{c.saldo != null ? fmtRp(c.saldo)+' saldo kartu' : 'tap ke mesin untuk cek saldo'}</div>
                    </div>
                    <div className="flex items-center gap-1.5 shrink-0">
                      <button onClick={()=>setTrfModal(true)} className="text-xs font-bold text-mizu px-3 py-1.5 rounded-lg bg-mizu-light active:scale-95 transition">Transfer</button>
                      <button onClick={()=>onGoKartu&&onGoKartu(c.nokartu, c.saldo)} className="w-8 h-8 grid place-items-center rounded-lg hover:bg-slate-100 dark:hover:bg-slate-800 active:scale-95 transition">
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-4 h-4 text-slate-400"><path d="M9 6l6 6-6 6"/></svg>
                      </button>
                    </div>
                  </div>
                ))}
              </Card>
            )}
          </div>
        )}

        {/* riwayat */}
        <div className="flex items-center justify-between mt-6 mb-2 px-1">
          <h2 className="font-bold text-base">Riwayat Transaksi</h2>
          <button onClick={()=>go('digital-riwayat')} className="text-sm font-semibold text-mizu">Lihat semua</button>
        </div>
        <Card className="divide-y divide-slate-100 dark:divide-slate-800 overflow-hidden">
          {riwayat.length===0 && <div className="p-4 text-sm text-slate-400 text-center">Belum ada transaksi.</div>}
          {riwayat.map((r,i)=>{
            const up = r.type==='topup';
            const tr = r.type==='transfer';
            const title = up ? 'Topup Saldo' : tr ? 'Transfer ke Kartu' : 'Beli Air'+(r.liter?` ${r.liter}L`:'');
            return (
              <button key={i} onClick={()=>onOpen(mkStruk(r,'digital'))} className="w-full text-left flex items-center gap-3 p-4 hover:bg-slate-50 dark:hover:bg-slate-800/50 active:scale-[.99] transition">
                <div className={"shrink-0 w-10 h-10 rounded-xl grid place-items-center "+(up?'bg-mizu-light text-mizu':tr?'bg-purple-50 text-purple-500':'bg-[#E6F7FB] text-[#0e7fa8]')}>{up?<Ic.plus className="w-5 h-5"/>:tr?<Ic.tap className="w-5 h-5"/>:<Ic.drop className="w-5 h-5"/>}</div>
                <div className="flex-1 min-w-0"><div className="font-semibold text-sm">{title}</div><div className="text-xs text-slate-400">{r.d} · {r.time}</div></div>
                <div className="text-right shrink-0 flex items-center gap-1.5">
                  <div className={"font-bold text-sm tabular-nums "+(up?'text-mizu':'text-slate-600 dark:text-slate-300')}>{up?'+ ':'− '}{fmtRp(r.amt)}</div>
                  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-4 h-4 text-slate-300"><path d="M9 6l6 6-6 6"/></svg>
                </div>
              </button>
            );
          })}
        </Card>
      </div>

      <TransferKeKartuModal open={trfModal} kartu={kartu} saldo={saldo} notice={notice}
        onClose={()=>setTrfModal(false)}
        onDone={(newBal)=>{ setTrfModal(false); onRefresh&&onRefresh(); window.AquAPI.kartuList().then(r=>setKartu(r.data||[])).catch(()=>{}); }}/>
    </div>
  );
}

/* =========================================================================
   DIGITAL · TOPUP
   ========================================================================= */
function DigitalTopup({ go, back, saldo, onTopup }){
  const presets = [10000,20000,50000,100000];
  const [sel, setSel] = useState(20000);
  const [custom, setCustom] = useState('');
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');
  const amount = custom ? (parseInt(custom)||0) : sel;

  const doShowQR = async () => {
    setBusy(true); setErr('');
    try {
      const r = await window.AquAPI.topup(amount);
      if(r.topup_url){
        window.location.href = r.topup_url; // redirect ke aqu.inchristo.id → Faspay
        return;
      } else { setErr('Gagal membuat link pembayaran'); }
    } catch(e){ setErr(e.message || 'Gagal topup'); }
    setBusy(false);
  };
  return (
    <div className="relative fade-in" data-screen-label="DigitalTopup">
      <WaterHeader className="absolute top-0 left-0 right-0 h-44 rounded-b-[2rem]"><div className="h-44"></div></WaterHeader>
      <div className={"relative "+wrapNarrow+" px-4 pt-5 pb-12"}>
        <div className="relative z-10"><ScreenTop onBack={back} title="Topup Saldo Digital" light/></div>
        <Card className="p-5 sm:p-6 pop">
          <div className="rounded-xl bg-slate-50 dark:bg-slate-800/60 px-4 py-3 flex items-center justify-between mb-5">
            <span className="text-sm text-slate-500 sub">Saldo saat ini</span><span className="font-extrabold tabular-nums">{fmtRp(saldo)}</span>
          </div>

          <div className="text-sm font-semibold mb-2">Pilih nominal</div>
          <div className="grid grid-cols-2 gap-2.5">
            {presets.map(v=>(
              <button key={v} onClick={()=>{setSel(v);setCustom('');}}
                className={"rounded-xl border-2 py-3.5 font-bold text-[15px] tabular-nums transition "+(!custom&&sel===v?'border-mizu bg-mizu-light text-mizu-700':'border-slate-200 dark:border-slate-700 hover:border-mizu/40')}>
                {fmtRp(v)}
              </button>
            ))}
          </div>

          <label className="block mt-4">
            <span className="block text-sm font-semibold mb-1.5">Nominal lain</span>
            <div className={"flex items-center rounded-xl border-2 px-3.5 min-h-[52px] transition "+(custom?'border-mizu':'border-slate-200 dark:border-slate-700')}>
              <span className="text-slate-400 font-semibold mr-1">Rp</span>
              <input type="number" inputMode="numeric" min="10000" step="1000" placeholder="0" value={custom} onChange={e=>setCustom(e.target.value)}
                className="flex-1 bg-transparent outline-none text-[16px] font-bold tabular-nums placeholder:text-slate-300"/>
            </div>
          </label>

          <div className="mt-5 rounded-xl bg-slate-50 dark:bg-slate-800/60 p-4 flex items-center justify-between">
            <span className="text-sm text-slate-500 sub">Total bayar</span><span className="text-lg font-extrabold text-mizu tabular-nums">{fmtRp(amount)}</span>
          </div>

          {err && <p className="text-danger text-sm mt-2 text-center font-semibold">{err}</p>}
          <Button size="lg" className="w-full mt-4" disabled={amount<10000||busy} onClick={doShowQR} icon={<Ic.qr className="w-5 h-5"/>}>{busy?'Menuju halaman bayar…':'Bayar via QRIS'}</Button>
          <p className="text-center text-xs text-slate-400 mt-2.5">QRIS Faspay — akan diarahkan ke halaman pembayaran</p>
        </Card>
      </div>
    </div>
  );
}

/* =========================================================================
   QR SCANNER MODAL — pakai jsQR + kamera belakang
   ========================================================================= */
function QrScannerModal({ onScan, onClose }) {
  const videoRef = useRef(null);
  const canvasRef = useRef(null);
  const [err, setErr] = useState('');
  const streamRef = useRef(null);
  const rafRef = useRef(null);

  useEffect(() => {
    let stopped = false;

    function stop() {
      stopped = true;
      if (rafRef.current) cancelAnimationFrame(rafRef.current);
      if (streamRef.current) streamRef.current.getTracks().forEach(t => t.stop());
    }

    function tick() {
      if (stopped) return;
      const video = videoRef.current;
      const canvas = canvasRef.current;
      if (video && canvas && video.readyState === video.HAVE_ENOUGH_DATA) {
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;
        const ctx = canvas.getContext('2d');
        ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
        const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        if (window.jsQR) {
          const code = window.jsQR(imgData.data, imgData.width, imgData.height);
          if (code && code.data) {
            let raw = code.data.trim();
            // Ekstrak device_no: bisa URL ?device=XXXX atau angka langsung
            const m = raw.match(/[?&]device[=:](\d+)/i);
            const devNo = m ? m[1] : raw.replace(/\D/g, '');
            if (devNo.length >= 3) { stop(); onScan(devNo); return; }
          }
        }
      }
      rafRef.current = requestAnimationFrame(tick);
    }

    navigator.mediaDevices.getUserMedia({ video: { facingMode: { ideal: 'environment' } } })
      .then(stream => {
        if (stopped) { stream.getTracks().forEach(t => t.stop()); return; }
        streamRef.current = stream;
        if (videoRef.current) { videoRef.current.srcObject = stream; videoRef.current.play(); }
        rafRef.current = requestAnimationFrame(tick);
      })
      .catch(() => setErr('Izin kamera ditolak atau tidak tersedia'));

    return stop;
  }, []);

  return (
    <div className="fixed inset-0 z-[90] bg-black/95 flex flex-col items-center justify-center p-4" onClick={onClose}>
      <div className="w-full max-w-sm" onClick={e => e.stopPropagation()}>
        <p className="text-white text-center text-sm font-semibold mb-4">Arahkan ke QR code di stiker mesin</p>
        {err
          ? <div className="text-center text-red-400 text-sm py-8">{err}<br/><span className="text-slate-400 text-xs mt-2 block">Ketik nomor mesin secara manual</span></div>
          : <div className="relative rounded-2xl overflow-hidden bg-black">
              <video ref={videoRef} className="w-full rounded-2xl" playsInline muted autoPlay style={{maxHeight:'60vh'}}/>
              <canvas ref={canvasRef} className="hidden"/>
              <div className="absolute inset-0 flex items-center justify-center pointer-events-none">
                <div className="w-48 h-48 border-2 border-white/70 rounded-2xl shadow-[0_0_0_9999px_rgba(0,0,0,.4)]"/>
              </div>
            </div>
        }
        <button onClick={onClose} className="mt-5 w-full py-3 rounded-xl bg-white/15 text-white font-semibold text-sm active:bg-white/25">Batal</button>
      </div>
    </div>
  );
}

/* =========================================================================
   DIGITAL · BELI AIR (slider Rupiah)
   ========================================================================= */
function DigitalBeli({ go, back, saldo, onBeli, deviceNo='' }){
  const MIN=100, MAX=50000, STEP=100;
  const QUICK = [1000, 2000, 5000, 10000, 25000, 50000];
  const [amount, setAmount] = useState(1000);
  const [askPin, setAskPin] = useState(false);
  const [dev, setDev] = useState(deviceNo);
  const [scanning, setScanning] = useState(false);
  const devOk = dev.trim().length >= 3;
  const cukup = saldo >= amount;
  const pct = ((amount-MIN)/(MAX-MIN))*100;
  const fmtQ = v => v>=1000 ? (v/1000)+'K' : String(v);
  return (
    <div className="relative fade-in" data-screen-label="DigitalBeli">
      {scanning && <QrScannerModal onScan={no=>{ setDev(no); setScanning(false); }} onClose={()=>setScanning(false)}/>}
      <WaterHeader className="absolute top-0 left-0 right-0 h-44 rounded-b-[2rem]"><div className="h-44"></div></WaterHeader>
      <div className={"relative "+wrapNarrow+" px-4 pt-5 pb-12"}>
        <div className="relative z-10"><ScreenTop onBack={back} title="Beli Air" light/></div>
        <Card className="p-5 sm:p-6 pop">
          <div className="flex items-center justify-between">
            <span className="text-sm text-slate-500 sub">Saldo</span><span className="font-bold tabular-nums">{fmtRp(saldo)}</span>
          </div>

          {/* nomor mesin */}
          <div className={"mt-3 mb-1 flex items-center gap-2.5 rounded-xl border-2 px-3.5 py-3 transition "+(devOk?'border-mizu bg-mizu-light/30':'border-slate-200 dark:border-slate-700')}>
            <button onClick={()=>setScanning(true)} className={"shrink-0 p-1 -m-1 rounded-lg active:bg-slate-100 transition "+(devOk?'text-mizu':'text-slate-400')} aria-label="Scan QR mesin">
              <Ic.bolt className="w-5 h-5"/>
            </button>
            <div className="flex-1 min-w-0">
              <div className="text-[10px] font-bold uppercase tracking-wide text-slate-400 mb-0.5">Nomor Mesin</div>
              {deviceNo
                ? <div className="font-bold text-sm text-ink">{dev}</div>
                : <input value={dev} onChange={e=>setDev(e.target.value)} placeholder="Scan QR mesin atau ketik di sini…"
                    inputMode="numeric" autoFocus
                    className="w-full bg-transparent outline-none text-sm font-bold text-ink placeholder:text-slate-300"/>}
            </div>
            {devOk && <Ic.check className="w-4 h-4 text-mizu shrink-0"/>}
          </div>
          {!deviceNo && !devOk && <p className="text-[11px] text-slate-400 px-1 mb-1">Tap ⚡ untuk scan QR mesin, atau ketik nomor mesin.</p>}

          <div className="relative text-center py-4 mt-1">
            <img src={imgSrc('mascot-mizu-shoes.png')} alt="" aria-hidden="true" className="absolute right-0 bottom-2 h-24 sm:h-28 w-auto opacity-90 pointer-events-none select-none"/>
            <div className="text-sm text-slate-500 sub">Nominal Pembelian</div>
            <div className="text-5xl font-extrabold tracking-tight text-mizu tabular-nums">{fmtRp(amount)}</div>
          </div>

          <input type="range" className="mizu-range w-full mt-2" min={MIN} max={MAX} step={STEP} value={amount} onChange={e=>setAmount(+e.target.value)}
            aria-label="Nominal pembelian air"
            style={{ background:`linear-gradient(90deg,#0066CC 0%,#21C7E8 ${pct}%, #E2E8F0 ${pct}%, #E2E8F0 100%)` }}/>
          <div className="flex justify-between mt-1 text-xs text-slate-400"><span>Rp 100</span><span>Rp 50K</span></div>

          <div className="grid grid-cols-6 gap-1.5 mt-3">
            {QUICK.map(v=>(
              <button key={v} onClick={()=>setAmount(v)}
                className={"rounded-xl border-2 py-2 font-bold text-[12px] tabular-nums transition "+(amount===v?'border-mizu bg-mizu-light text-mizu-700':'border-slate-200 dark:border-slate-700 hover:border-mizu/40')}>
                {fmtQ(v)}
              </button>
            ))}
          </div>

          <div className="mt-5 rounded-xl bg-slate-50 dark:bg-slate-800/60 p-4 space-y-1.5">
            <div className="flex items-center justify-between text-sm"><span className="text-slate-500 sub">Dibayar dari saldo</span><span className="font-bold tabular-nums">{fmtRp(amount)}</span></div>
            <div className="flex items-center justify-between text-sm"><span className="text-slate-500 sub">Saldo setelah</span><span className={"font-bold tabular-nums "+(cukup?'':'text-danger')}>{fmtRp(Math.max(0,saldo-amount))}</span></div>
          </div>

          {!devOk
            ? <Button size="lg" className="w-full mt-4" disabled icon={<Ic.bolt className="w-5 h-5"/>}>Masukkan nomor mesin dulu</Button>
            : cukup
              ? <Button size="lg" className="w-full mt-4" onClick={()=>setAskPin(true)} icon={<span className="grid place-items-center w-7 h-7 rounded-full bg-white/95"><DropletArt className="w-5 h-5"/></span>} iconRight={<Ic.arrow className="w-5 h-5"/>}>Beli Sekarang</Button>
              : <Button size="lg" variant="outline" className="w-full mt-4 !border-danger/40 !text-danger" disabled icon={<Ic.bolt className="w-5 h-5"/>}>Saldo tidak cukup</Button>}
          {devOk && !cukup && <button onClick={()=>go('digital-topup')} className="block mx-auto mt-3 text-sm font-semibold text-mizu">Topup saldo dulu →</button>}
          <p className="text-center text-xs text-slate-400 mt-2.5 flex items-center justify-center gap-1.5"><Ic.shield className="w-4 h-4"/> Rp 100 = ±1 gelas · dikonfirmasi dengan PIN</p>
        </Card>
      </div>
      <PinSheet open={askPin} onClose={()=>setAskPin(false)}
        onVerify={async(pin)=>{ try{ return await window.AquAPI.verifyPin(pin); }catch(e){ return null; } }}
        onSuccess={(pin, status)=>{ setAskPin(false); onBeli(amount, dev.trim()); }}
        title="Konfirmasi dengan PIN" subtitle={"Bayar "+fmtRp(amount)+" dari saldo untuk membeli air di mesin "+dev.trim()+"."}/>
    </div>
  );
}

/* =========================================================================
   DIGITAL · SUKSES (air keluar)
   ========================================================================= */
function DigitalSukses({ go, lastBuy, saldo }){
  const bubbles = React.useMemo(()=>Array.from({length:16}).map((_,i)=>({ left:4+(i*6)%92, size:6+(i%4)*6, dur:6+(i%5)*1.6, delay:(i*0.4)%5 })),[]);
  return (
    <div className="relative min-h-screen wh-flow text-white overflow-hidden flex flex-col fade-in" data-screen-label="DigitalSukses">
      <div className="wh-caustic" aria-hidden="true"></div>
      <div className="wh-bubbles" aria-hidden="true">{bubbles.map((b,i)=>(<span key={i} className="wh-bubble" style={{left:b.left+'%',width:b.size,height:b.size,animationDuration:b.dur+'s',animationDelay:b.delay+'s'}}></span>))}</div>
      <div className="relative z-10 flex-1 flex flex-col items-center justify-center px-6 text-center">
        <img src={imgSrc('mascot-mizu-shoes.png')} alt="Maskot MizuOne gembira" className="h-52 sm:h-60 w-auto drop-shadow-2xl pop"/>
        <h1 className="text-3xl font-extrabold tracking-tight mt-2">Air Keluar! 🎉</h1>
        <p className="text-white/90 text-lg mt-1"><b>{fmtRp(lastBuy.amount)}</b> · air sedang mengalir</p>
        <div className="mt-6 rounded-2xl bg-white/12 border border-white/25 backdrop-blur px-6 py-4">
          <div className="text-white/80 text-sm">Saldo kamu</div>
          <div className="text-2xl font-extrabold tabular-nums">{fmtRp(saldo)}</div>
        </div>
        <div className="flex gap-2.5 mt-7 w-full max-w-xs">
          <Button variant="white" className="flex-1" onClick={()=>go('digital-beli')}>Beli Lagi</Button>
          <Button variant="glass" className="flex-1" onClick={()=>go('digital-home')}>Beranda</Button>
        </div>
      </div>
    </div>
  );
}

/* =========================================================================
   RIWAYAT — daftar transaksi + filter (Semua / Topup / Beli Air)
   ========================================================================= */
function Riwayat({ go, flow='digital', onOpen }){
  const [filter, setFilter] = useState('all');
  const home = flow==='kartu' ? 'kartu-home' : 'digital-home';
  const [data, setData] = useState([]);
  useEffect(()=>{
    if(!(window.AquAPI && window.AquAPI.hasToken())) return;
    window.AquAPI.history().then(r=>{
      setData((r.data||[]).map(x=>{
        const dt = new Date(x.created_at);
        const tp = x.type==='topup' ? 'topup' : x.type==='transfer_to_card' ? 'transfer' : 'beli';
        return {
          type: tp,
          amt: x.amount,
          liter: x.volume_ml ? Math.round(x.volume_ml/100)/10 : undefined,
          d: dt.toLocaleDateString('id-ID',{day:'numeric',month:'short'}),
          time: dt.toLocaleTimeString('id-ID',{hour:'2-digit',minute:'2-digit'}),
          orderId: x.order_id,
          via: x.type==='topup' ? 'QRIS Faspay' : x.type==='transfer_to_card' ? 'Saldo digital → Kartu fisik' : ('Saldo digital'+(x.device_no?(' · '+x.device_no):'')),
          status: x.status,
        };
      }));
    }).catch(()=>setData([]));
  },[]);
  const list = data.filter(d=> filter==='all' || d.type===filter);
  const totalTopup = data.filter(d=>d.type==='topup').reduce((s,d)=>s+d.amt,0);
  const totalLiter = data.filter(d=>d.type==='beli').reduce((s,d)=>s+(d.liter||0),0);
  const chips = [['all','Semua'],['topup','Topup'],['beli','Beli Air'],['transfer','Transfer']];

  return (
    <div className="fade-in" data-screen-label="Riwayat">
      <WaterHeader className="rounded-b-2xl">
        <div className={wrapNarrow+" px-4 pt-5 pb-12"}>
          <div className="flex items-center gap-2.5 text-white">
            <button onClick={()=>go(home)} aria-label="Kembali" className="w-11 h-11 grid place-items-center rounded-xl bg-white/15 border border-white/30 backdrop-blur hover:bg-white/25 active:scale-95 transition shrink-0"><Ic.back className="w-5 h-5"/></button>
            <h1 className="text-xl font-extrabold tracking-tight">Riwayat Transaksi</h1>
          </div>
        </div>
      </WaterHeader>

      <div className={wrapNarrow+" px-4 -mt-8 relative z-10 pb-24"}>
        {/* ringkasan */}
        <div className="grid grid-cols-2 gap-3">
          <Card className="p-4">
            <div className="flex items-center gap-2 text-slate-500 text-xs font-semibold sub"><Ic.wallet className="w-4 h-4"/> Total Topup</div>
            <div className="text-xl font-extrabold tabular-nums mt-1 text-mizu">{fmtRp(totalTopup)}</div>
          </Card>
          <Card className="p-4">
            <div className="flex items-center gap-2 text-slate-500 text-xs font-semibold sub"><Ic.drop className="w-4 h-4"/> Air Dibeli</div>
            <div className="text-xl font-extrabold tabular-nums mt-1">{totalLiter} <span className="text-sm text-slate-400">Liter</span></div>
          </Card>
        </div>

        {flow==='kartu' && (
          <p className="text-xs text-slate-400 mt-3 px-1 sub">Riwayat topup saldo digital dari akun yang terhubung.</p>
        )}
        {/* filter */}
        <div className="flex gap-2 mt-4">
          {chips.map(([k,l])=>(
            <button key={k} onClick={()=>setFilter(k)} className={"px-4 py-2 rounded-lg text-sm font-semibold border transition "+(filter===k?'bg-mizu text-white border-mizu':'bg-slate-100 border-slate-200 dark:bg-slate-800 dark:border-slate-700 text-slate-600 dark:text-slate-300 hover:border-mizu/40')}>{l}</button>
          ))}
        </div>

        {/* daftar */}
        {list.length===0 ? (
          <Card className="mt-3 p-8 text-center pop">
            <img src={imgSrc('mascot-mizuki-full.png')} alt="" aria-hidden="true" className="h-40 w-auto mx-auto opacity-90"/>
            <h3 className="text-lg font-extrabold mt-2">Belum ada transaksi</h3>
            <p className="text-slate-500 text-sm mt-1 sub max-w-xs mx-auto">Transaksi {chips.find(c=>c[0]===filter)?.[1].toLowerCase()} kamu akan tampil di sini.</p>
            <Button className="mt-4" onClick={()=>setFilter('all')} icon={<Ic.list className="w-5 h-5"/>}>Lihat semua</Button>
          </Card>
        ) : (
        <Card className="mt-3 divide-y divide-slate-100 dark:divide-slate-800 overflow-hidden">
          {list.map((r,i)=>{
            const topup = r.type==='topup';
            const transfer = r.type==='transfer';
            const title = topup ? (flow==='kartu'?'Topup Saldo Digital':'Topup Saldo') : transfer ? 'Transfer ke Kartu' : 'Beli Air'+(r.liter?` ${r.liter}L`:'');
            return (
              <button key={i} onClick={()=>onOpen(mkStruk(r,flow))} className="w-full text-left flex items-center gap-3 p-4 hover:bg-slate-50 dark:hover:bg-slate-800/50 active:scale-[.99] transition">
                <div className={"shrink-0 w-10 h-10 rounded-xl grid place-items-center "+(topup?'bg-mizu-light text-mizu':transfer?'bg-purple-50 text-purple-500':'bg-[#E6F7FB] text-[#0e7fa8]')}>{topup?<Ic.plus className="w-5 h-5"/>:transfer?<Ic.tap className="w-5 h-5"/>:<Ic.drop className="w-5 h-5"/>}</div>
                <div className="flex-1 min-w-0">
                  <div className="font-semibold text-sm">{title}</div>
                  <div className="text-xs text-slate-400 truncate">{r.d} · {r.time} · {r.via}</div>
                </div>
                <div className="text-right shrink-0">
                  <div className={"font-bold text-sm tabular-nums "+(topup?'text-mizu':'text-slate-600 dark:text-slate-300')}>{topup?'+ ':'− '}{fmtRp(r.amt)}</div>
                  <div className={"text-[11px] font-semibold "+(r.status==='failed'||r.status==='refunded'?'text-danger':r.status==='pending'?'text-warning':'text-success')}>{r.status==='failed'?'Gagal':r.status==='refunded'?'Direfund':r.status==='pending'?'Pending':'Berhasil'}</div>
                </div>
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-4 h-4 text-slate-300 shrink-0"><path d="M9 6l6 6-6 6"/></svg>
              </button>
            );
          })}
        </Card>
        )}
      </div>
    </div>
  );
}

/* baris menu untuk halaman Akun */
function MenuRow({ icon, label, sub, onClick, right, danger }){
  return (
    <button onClick={onClick} className="w-full flex items-center gap-3 p-4 text-left hover:bg-slate-50 dark:hover:bg-slate-800/50 active:scale-[.99] transition">
      <div className={"shrink-0 w-10 h-10 rounded-xl grid place-items-center "+(danger?'bg-danger/10 text-danger':'bg-mizu-light text-mizu')}>{icon}</div>
      <div className="flex-1 min-w-0">
        <div className={"font-semibold text-sm "+(danger?'text-danger':'')}>{label}</div>
        {sub && <div className="text-xs text-slate-400 truncate">{sub}</div>}
      </div>
      {right || <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-5 h-5 text-slate-300 shrink-0"><path d="M9 6l6 6-6 6"/></svg>}
    </button>
  );
}
function SectionLabel({ children }){ return <div className="text-xs font-bold uppercase tracking-wide text-slate-400 px-1 mb-2 mt-5">{children}</div>; }

/* =========================================================================
   AKUN — profil, keamanan, aktivitas, bantuan
   ========================================================================= */
function Akun({ go, notice, flow='digital', saldo=0, cardNo='1234', openSub, user, onGoKartu }){
  const isKartu = flow==='kartu';
  const home = isKartu ? 'kartu-home' : 'digital-home';
  const nama = user?.name || (isKartu ? 'Pemegang Kartu' : 'Member');
  const telp = user?.phone || '-';
  const verified = <span className="inline-flex items-center gap-1 text-xs font-bold text-success"><Ic.check className="w-3.5 h-3.5"/> Terverifikasi</span>;
  const [showLogout, setShowLogout] = useState(false);
  const [kartu, setKartu] = useState(null);
  useEffect(()=>{
    if(isKartu || !(window.AquAPI && window.AquAPI.hasToken())) return;
    window.AquAPI.kartuList().then(r=>setKartu(r.data||[])).catch(()=>setKartu([]));
  },[]);
  return (
    <div className="fade-in" data-screen-label="Akun">
      <WaterHeader className="rounded-b-[2rem]">
        <div className={wrapNarrow+" px-4 pt-5 pb-12"}>
          <div className="flex items-center gap-2.5 text-white">
            <button onClick={()=>go(home)} aria-label="Kembali" className="w-11 h-11 grid place-items-center rounded-xl bg-white/15 border border-white/30 backdrop-blur hover:bg-white/25 active:scale-95 transition shrink-0"><Ic.back className="w-5 h-5"/></button>
            <h1 className="text-xl font-extrabold tracking-tight">Akun Saya</h1>
          </div>
          <div className="flex items-center gap-3.5 mt-5 text-white">
            <div className="shrink-0 w-16 h-16 rounded-2xl overflow-hidden border border-white/30 bg-white/20">
              {user?.avatar_url
                ? <img src={user.avatar_url} className="w-full h-full object-cover" alt="avatar"/>
                : <div className="w-full h-full grid place-items-center text-2xl font-extrabold text-white backdrop-blur">{nama[0]}</div>}
            </div>
            <div className="min-w-0">
              <div className="text-lg font-extrabold leading-tight">{nama}</div>
              <div className="text-white/80 text-sm">{telp}</div>
              <span className="inline-flex items-center gap-1 mt-1 px-2 py-0.5 rounded-full text-[11px] font-bold bg-white/20 border border-white/25">{isKartu?'Pemegang Kartu ••'+cardNo:'Member Digital'}</span>
            </div>
          </div>
        </div>
      </WaterHeader>

      <div className={wrapNarrow+" px-4 -mt-6 relative z-10 pb-24"}>
        {/* saldo mini */}
        <Card className="p-4 flex items-center justify-between">
          <div>
            <div className="text-xs text-slate-500 font-semibold sub">{isKartu?'Saldo Kartu':'Saldo Digital'}</div>
            <div className="text-xl font-extrabold tabular-nums text-mizu mt-0.5">{fmtRp(saldo)}</div>
          </div>
          <Button size="sm" onClick={()=>go(isKartu?'kartu-topup':'digital-topup')} icon={<Ic.plus className="w-4 h-4"/>}>Topup</Button>
        </Card>

        <SectionLabel>Profil &amp; Keamanan</SectionLabel>
        <Card className="divide-y divide-slate-100 dark:divide-slate-800 overflow-hidden">
          <MenuRow icon={<Ic.user className="w-5 h-5"/>} label="Edit Profil" sub="Nama, foto, alamat" onClick={()=>{ if(isKartu && !(window.AquAPI&&window.AquAPI.hasToken())){ notice('Masuk ke akun digital untuk mengedit profil'); return; } openSub('edit-profil', flow); }}/>
          <MenuRow icon={<Ic.shield className="w-5 h-5"/>} label="Ubah PIN Keamanan" sub="PIN 4-digit untuk transaksi" onClick={()=>{ if(isKartu && !(window.AquAPI&&window.AquAPI.hasToken())){ notice('Masuk ke akun digital untuk mengubah PIN'); return; } openSub('ubah-pin', flow); }}/>
          <MenuRow icon={<Ic.phone className="w-5 h-5"/>} label="Nomor WhatsApp" right={verified} onClick={()=>notice('Nomor sudah terverifikasi')}/>
          {isKartu && <MenuRow icon={<Ic.card className="w-5 h-5"/>} label="Kartu Tertaut" sub={'MizuOne ••'+cardNo} right={<span className="inline-flex items-center gap-1 text-xs font-bold text-success"><Ic.check className="w-3.5 h-3.5"/> Aktif</span>} onClick={()=>notice('Kartu MizuOne ••'+cardNo+' aktif & tertaut')}/>}
        </Card>

        {/* kartu terhubung — hanya flow digital */}
        {!isKartu && kartu && kartu.length > 0 && (
          <>
            <SectionLabel>Kartu Terhubung</SectionLabel>
            <Card className="divide-y divide-slate-100 dark:divide-slate-800 overflow-hidden">
              {kartu.map((c,i)=>(
                <MenuRow key={i}
                  icon={<Ic.tap className="w-5 h-5"/>}
                  label={`Kartu MizuOne •••• ${String(c.nokartu).slice(-4)}`}
                  sub={c.saldo != null ? fmtRp(c.saldo)+' saldo kartu' : 'tap ke mesin untuk cek saldo'}
                  onClick={()=>onGoKartu&&onGoKartu(c.nokartu, c.saldo)}/>
              ))}
            </Card>
          </>
        )}

        <SectionLabel>Aktivitas</SectionLabel>
        <Card className="divide-y divide-slate-100 dark:divide-slate-800 overflow-hidden">
          <MenuRow icon={<Ic.clock className="w-5 h-5"/>} label="Riwayat Transaksi" sub="Topup &amp; beli air" onClick={()=>go(isKartu?'kartu-riwayat':'digital-riwayat')}/>
          <MenuRow icon={<Ic.wallet className="w-5 h-5"/>} label="Metode Pembayaran" sub="QRIS · Faspay" onClick={()=>openSub('metode-bayar', flow)}/>
        </Card>

        <SectionLabel>Bantuan</SectionLabel>
        <Card className="divide-y divide-slate-100 dark:divide-slate-800 overflow-hidden">
          <MenuRow icon={<Ic.drop className="w-5 h-5"/>} label="Chat Mizu (WhatsApp)" sub="Asisten pembelian air" right={<Ic.ext className="w-4 h-4 text-slate-300"/>} onClick={()=>{ notice('Membuka WhatsApp…'); openExt(LINKS.wa); }}/>
          <MenuRow icon={<Ic.drop className="w-5 h-5"/>} label="Chat Mizuki (Telegram)" sub="Asisten pembelian air" right={<Ic.ext className="w-4 h-4 text-slate-300"/>} onClick={()=>{ notice('Membuka Telegram…'); openExt(LINKS.tg); }}/>
          <MenuRow icon={<Ic.shield className="w-5 h-5"/>} label="Pusat Bantuan &amp; S&amp;K" sub="FAQ &amp; Syarat & Ketentuan" onClick={()=>openSub('bantuan', flow)}/>
        </Card>

        <Button variant="outline" size="lg" className="w-full mt-6 !border-danger/40 !text-danger" onClick={()=>setShowLogout(true)} icon={<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" className="w-5 h-5"><path d="M15 4h3a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-3"/><path d="M10 17l-5-5 5-5"/><path d="M5 12h12"/></svg>}>Keluar</Button>
        <p className="text-center text-xs text-slate-400 mt-4">MizuOne v1.0.0 · PT. Dinasty Sahabat Mulia</p>
      </div>

      {/* Modal konfirmasi logout — fixed di dalam root div */}
      {showLogout && (
        <div className="fixed inset-0 z-[65] flex items-end sm:items-center justify-center p-4 bg-black/50 backdrop-blur-sm fade-in" onClick={()=>setShowLogout(false)}>
          <div className="w-full max-w-sm bg-white dark:bg-slate-900 rounded-2xl p-6 pop shadow-soft" onClick={e=>e.stopPropagation()}>
            <h3 className="font-extrabold text-lg text-center">Keluar dari akun?</h3>
            <p className="text-slate-500 text-sm text-center mt-1 mb-5 sub">Kamu perlu masuk lagi untuk akses saldo dan transaksi.</p>
            <div className="grid grid-cols-2 gap-2.5">
              <Button variant="outline" onClick={()=>setShowLogout(false)}>Batal</Button>
              <Button variant="outline" className="!border-danger/40 !text-danger" onClick={()=>{ if(window.AquAPI) window.AquAPI.logout(); go('landing'); }}>Keluar</Button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* =========================================================================
   STRUK — e-Struk detail transaksi
   ========================================================================= */
function Struk({ tx, back, notice }){
  if(!tx) return null;
  const rows = [
    ['Jenis transaksi', tx.title],
    ['Nominal', fmtRp(tx.amt)],
    tx.liter ? ['Jumlah air', tx.liter+' Liter'] : null,
    ['Tanggal & jam', (tx.date||'')+(tx.time?' · '+tx.time:'')],
    ['ID Transaksi', tx.id],
    ['Metode bayar', tx.method],
    ['Status', tx.status],
  ].filter(Boolean);
  const receiptText = 'MizuOne — e-Struk\n'+'-'.repeat(30)+'\n'+rows.map(([k,v])=>k+': '+v).join('\n')+'\n'+'-'.repeat(30)+'\nPT. Dinasty Sahabat Mulia · mizuone.id';
  const share = async()=>{
    const data = { title:'e-Struk MizuOne', text:receiptText };
    if(navigator.share){ try{ await navigator.share(data); }catch(e){} }
    else { try{ await navigator.clipboard.writeText(receiptText); notice('Detail struk disalin'); }catch(e){ notice('Tidak dapat membagikan'); } }
  };
  const unduh = ()=>{
    try{
      const blob = new Blob([receiptText], {type:'text/plain'});
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a'); a.href=url; a.download='e-struk-'+tx.id+'.txt'; document.body.appendChild(a); a.click(); a.remove();
      setTimeout(()=>URL.revokeObjectURL(url), 1000);
      notice('Struk diunduh');
    }catch(e){ notice('Gagal mengunduh struk'); }
  };
  return (
    <div className="relative fade-in" data-screen-label="Struk">
      <WaterHeader className="absolute top-0 left-0 right-0 h-44 rounded-b-[2rem]"><div className="h-44"></div></WaterHeader>
      <div className={"relative "+wrapNarrow+" px-4 pt-5 pb-24"}>
        <div className="relative z-10"><ScreenTop onBack={back} title="Detail Transaksi" light/></div>
        <Card className="overflow-hidden pop">
          <div className="relative p-6 text-center border-b border-dashed border-slate-200 dark:border-slate-700">
            <div className="mx-auto w-16 h-16 rounded-full bg-success/12 text-success grid place-items-center mb-3 pop"><Ic.check className="w-8 h-8"/></div>
            <div className="text-sm text-slate-500 sub">{tx.title}</div>
            <div className="text-3xl font-extrabold tabular-nums mt-1">{fmtRp(tx.amt)}</div>
            <Badge tone="success" className="mt-3">Transaksi Berhasil</Badge>
            <span className="absolute -left-3 -bottom-3 w-6 h-6 rounded-full bg-canvas dark:bg-slate-950" aria-hidden="true"></span>
            <span className="absolute -right-3 -bottom-3 w-6 h-6 rounded-full bg-canvas dark:bg-slate-950" aria-hidden="true"></span>
          </div>
          <div className="p-6 space-y-3">
            {rows.map(([k,v],i)=>(
              <div key={i} className="flex items-start justify-between gap-4 text-sm">
                <span className="text-slate-500 sub shrink-0">{k}</span>
                {k==='ID Transaksi'
                  ? <button onClick={()=>{ try{navigator.clipboard.writeText(v);notice('ID disalin!');}catch(e){} }} className="font-mono text-xs break-all text-slate-600 dark:text-slate-300 text-right flex items-center gap-1.5 group hover:text-mizu transition">
                      <span>{v}</span>
                      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" className="w-3.5 h-3.5 shrink-0 text-slate-400 group-hover:text-mizu"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
                    </button>
                  : <span className="font-semibold text-right">{v}</span>
                }
              </div>
            ))}
          </div>
          <div className="px-6 pb-5 pt-4 flex items-center gap-2 text-xs text-slate-400 border-t border-dashed border-slate-200 dark:border-slate-700">
            <Logo className="h-5"/> {tx.type==='topup' && <span className="ml-auto">Aman oleh Faspay</span>}
          </div>
        </Card>
        <div className="grid grid-cols-2 gap-2.5 mt-4">
          <Button variant="outline" onClick={share} icon={<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" className="w-5 h-5"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="m8.6 13.5 6.8 4M15.4 6.5l-6.8 4"/></svg>}>Bagikan struk</Button>
          <Button onClick={unduh} icon={<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" className="w-5 h-5"><path d="M12 4v11m0 0 4-4m-4 4-4-4"/><path d="M5 19h14"/></svg>}>Unduh struk</Button>
        </div>
      </div>
    </div>
  );
}

/* =========================================================================
   EDIT PROFIL — nama, foto, alamat, kelurahan, kecamatan, kota + map
   ========================================================================= */
function EditProfil({ flow='digital', back, notice, user, onSave }){
  const [nama, setNama] = useState(user?.name || '');
  const [alamat, setAlamat] = useState(user?.alamat || '');
  const [kelurahan, setKelurahan] = useState(user?.kelurahan || '');
  const [kecamatan, setKecamatan] = useState(user?.kecamatan || '');
  const [kota, setKota] = useState(user?.kota || '');
  const [lat, setLat] = useState(user?.lat || null);
  const [lng, setLng] = useState(user?.lng || null);
  const [avatarFile, setAvatarFile] = useState(null);
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');
  const [showMap, setShowMap] = useState(false);
  const pickAvatar = (e)=>{ const f=e.target.files?.[0]; if(!f) return; const rd=new FileReader(); rd.onload=ev=>setAvatarFile({preview:ev.target.result, base64:ev.target.result}); rd.readAsDataURL(f); };
  const onMapPick = (d)=>{ setLat(d.lat); setLng(d.lng); if(d.display_name && !alamat) setAlamat(d.display_name.split(',').slice(0,3).join(',').trim()); if(d.kelurahan) setKelurahan(d.kelurahan); if(d.kecamatan) setKecamatan(d.kecamatan); if(d.kota) setKota(d.kota); };
  const doSave = async ()=>{
    if(!nama.trim()||busy) return;
    setBusy(true); setErr('');
    try{
      if(avatarFile) await window.AquAPI.uploadAvatar(avatarFile.base64);
      await window.AquAPI.updateProfile({name:nama, alamat, kelurahan, kecamatan, kota, lat, lng});
      if(onSave) onSave();
      notice('Profil berhasil disimpan');
      back();
    }catch(e){ setErr(e.message||'Gagal menyimpan profil'); setBusy(false); }
  };
  const avatarSrc = avatarFile?.preview || user?.avatar_url;
  return (
    <div className="relative fade-in" data-screen-label="EditProfil">
      <WaterHeader className="absolute top-0 left-0 right-0 h-44 rounded-b-[2rem]"><div className="h-44"></div></WaterHeader>
      <div className={"relative "+wrapNarrow+" px-4 pt-5 pb-24"}>
        <div className="relative z-10"><ScreenTop onBack={back} title="Edit Profil" light/></div>
        <Card className="p-6 pop">
          {/* Foto profil */}
          <div className="flex flex-col items-center mb-5">
            <label className="cursor-pointer group relative">
              <div className="w-20 h-20 rounded-full bg-slate-100 dark:bg-slate-800 border-2 border-dashed border-slate-300 dark:border-slate-600 group-hover:border-mizu overflow-hidden grid place-items-center transition">
                {avatarSrc
                  ? <img src={avatarSrc} className="w-full h-full object-cover" alt="foto"/>
                  : <Ic.user className="w-8 h-8 text-slate-400"/>}
              </div>
              <div className="absolute -bottom-1 -right-1 w-7 h-7 rounded-full bg-mizu text-white grid place-items-center shadow-soft"><Ic.plus className="w-4 h-4"/></div>
              <input type="file" accept="image/*" className="sr-only" onChange={pickAvatar}/>
            </label>
            <span className="mt-2 text-xs text-slate-400">Foto profil (opsional)</span>
          </div>

          <label className="block mb-4">
            <span className="block text-sm font-semibold mb-1.5">Nama lengkap</span>
            <input value={nama} onChange={e=>setNama(e.target.value)} onKeyDown={e=>e.key==='Enter'&&document.getElementById('ep-alamat')?.focus()} className={inputCls} placeholder="Nama lengkap"/>
          </label>
          <label className="block mb-4">
            <span className="block text-sm font-semibold mb-1.5">Nomor HP</span>
            <div className="flex">
              <span className="inline-flex items-center px-3 rounded-l-xl border border-r-0 border-slate-300 dark:border-slate-600 bg-slate-50 dark:bg-slate-800 text-slate-500 text-sm font-semibold">+62</span>
              <input value={(user?.phone||'').replace(/^0/,'').replace(/^62/,'')} disabled className={inputCls+" rounded-l-none !bg-slate-50 dark:!bg-slate-800/60 text-slate-400"}/>
            </div>
            <span className="block text-xs text-slate-400 mt-1.5 flex items-center gap-1"><Ic.check className="w-3.5 h-3.5 text-success"/> Terverifikasi · hubungi bantuan untuk mengubah</span>
          </label>
          <label className="block mb-3">
            <span className="block text-sm font-semibold mb-1.5">Alamat</span>
            <textarea id="ep-alamat" value={alamat} onChange={e=>setAlamat(e.target.value)} rows="2" className={inputCls+" resize-none leading-snug"} placeholder="Jl. Merdeka No. 1"/>
          </label>

          <button type="button" onClick={()=>setShowMap(m=>!m)}
            className="mb-3 inline-flex items-center gap-1.5 text-xs font-semibold text-mizu hover:underline">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-4 h-4"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>
            {showMap ? 'Sembunyikan Peta' : 'Pilih Lokasi di Peta'}{lat ? ' ✓' : ''}
          </button>
          {showMap && <MapPicker initLat={lat} initLng={lng} onPick={onMapPick}/>}

          <div className="grid grid-cols-2 gap-3 mb-3">
            <label className="block">
              <span className="block text-sm font-semibold mb-1.5">Kelurahan</span>
              <input value={kelurahan} onChange={e=>setKelurahan(e.target.value)} className={inputCls} placeholder="Singosari"/>
            </label>
            <label className="block">
              <span className="block text-sm font-semibold mb-1.5">Kecamatan</span>
              <input value={kecamatan} onChange={e=>setKecamatan(e.target.value)} className={inputCls} placeholder="Singosari"/>
            </label>
          </div>
          <label className="block mb-5">
            <span className="block text-sm font-semibold mb-1.5">Kota / Kabupaten</span>
            <input value={kota} onChange={e=>setKota(e.target.value)} onKeyDown={e=>e.key==='Enter'&&doSave()} className={inputCls} placeholder="Malang"/>
          </label>

          {err && <p className="text-danger text-sm mb-3 text-center font-semibold">{err}</p>}
          <Button size="lg" className="w-full" disabled={!nama.trim()||busy} onClick={doSave} icon={<Ic.check className="w-5 h-5"/>}>{busy?'Menyimpan…':'Simpan Perubahan'}</Button>
        </Card>
      </div>
    </div>
  );
}

/* =========================================================================
   UBAH PIN — verifikasi lama → buat baru → konfirmasi (reuse PinSheet)
   ========================================================================= */
function UbahPin({ back, notice }){
  const order = ['lama','baru','konfirmasi'];
  const [step, setStep] = useState('lama');
  const [npin, setNpin] = useState('');
  const [pinLama, setPinLama] = useState('');
  const [checking, setChecking] = useState(true);

  useEffect(()=>{
    if(!window.AquAPI || !window.AquAPI.hasToken()){ setChecking(false); return; }
    // Probe: cek apakah user sudah punya PIN (no_pin → skip step lama)
    window.AquAPI.verifyPin('0000').then(r=>{
      if(r && r.status==='no_pin') setStep('baru');
      setChecking(false);
    }).catch(()=>setChecking(false));
  },[]);

  const doSavePin = async(confirmedPin)=>{
    try {
      await window.AquAPI.setPin(confirmedPin, pinLama || undefined);
      notice && notice('PIN berhasil ' + (pinLama ? 'diubah' : 'dibuat') + ' ✓');
      back();
    } catch(e){ notice && notice('Gagal menyimpan PIN, coba lagi'); }
  };

  return (
    <div className="relative fade-in" data-screen-label="UbahPin">
      <WaterHeader className="absolute top-0 left-0 right-0 h-44 rounded-b-[2rem]"><div className="h-44"></div></WaterHeader>
      <div className={"relative "+wrapNarrow+" px-4 pt-5 pb-24"}>
        <div className="relative z-10"><ScreenTop onBack={back} title="Ubah PIN" light/></div>
        <Card className="p-6 pop text-center">
          <div className="w-14 h-14 rounded-2xl bg-mizu-light text-mizu grid place-items-center mx-auto mb-3"><Ic.shield className="w-7 h-7"/></div>
          <h2 className="text-xl font-extrabold">{step==='baru'&&!pinLama ? 'Buat PIN Keamanan' : 'Ubah PIN Keamanan'}</h2>
          <p className="text-slate-500 text-sm mt-1 sub">{ step==='lama'?'Masukkan PIN kamu saat ini.':step==='baru'?'Pilih 4 angka yang mudah kamu ingat.':'Masukkan ulang PIN baru kamu.' }</p>
          <div className="flex justify-center gap-2 mt-5">
            {order.map((s,i)=>{
              const done = order.indexOf(step) > i, cur = step===s;
              return <span key={s} className={"h-1.5 rounded-full transition-all "+(cur?'w-8 bg-mizu':done?'w-5 bg-mizu/50':'w-5 bg-slate-200 dark:bg-slate-700')}></span>;
            })}
          </div>
          {checking && <p className="text-xs text-slate-400 mt-4">Memuat status PIN…</p>}
        </Card>
      </div>
      {!checking && (
        <PinSheet key={step} open
          onVerify={step==='lama' ? async(pin)=>{ try{ return await window.AquAPI.verifyPin(pin); }catch(e){ return null; } } : null}
          expect={step==='konfirmasi' ? npin : null}
          hint={false}
          errMsg={ step==='konfirmasi' ? 'PIN tidak cocok, ulangi' : 'PIN salah, coba lagi' }
          title={ step==='lama'?'Masukkan PIN lama':step==='baru'?'Buat PIN baru':'Konfirmasi PIN baru' }
          subtitle={ step==='lama'?'Demi keamanan, masukkan PIN kamu saat ini.':step==='baru'?'Pilih 4 angka yang mudah kamu ingat.':'Masukkan ulang PIN baru kamu.' }
          onClose={back}
          onSuccess={(pin, status)=>{
            if(step==='lama'){
              if(status==='no_pin') setStep('baru');
              else { setPinLama(pin); setStep('baru'); }
            }
            else if(step==='baru'){ setNpin(pin); setStep('konfirmasi'); }
            else { doSavePin(pin); }
          }}/>
      )}
    </div>
  );
}

/* =========================================================================
   METODE PEMBAYARAN — QRIS Faspay aktif
   ========================================================================= */
function MetodeBayar({ flow='digital', back }){
  return (
    <div className="relative fade-in" data-screen-label="MetodeBayar">
      <WaterHeader className="absolute top-0 left-0 right-0 h-44 rounded-b-[2rem]"><div className="h-44"></div></WaterHeader>
      <div className={"relative "+wrapNarrow+" px-4 pt-5 pb-24"}>
        <div className="relative z-10"><ScreenTop onBack={back} title="Metode Pembayaran" light/></div>
        <Card className="p-5 pop">
          <div className="flex items-center gap-3">
            <div className="shrink-0 w-12 h-12 rounded-xl bg-mizu-light text-mizu grid place-items-center"><Ic.qr className="w-6 h-6"/></div>
            <div className="flex-1 min-w-0">
              <div className="font-bold">QRIS · Faspay</div>
              <div className="text-xs text-slate-400">GoPay, OVO, DANA, ShopeePay, m-banking, dll.</div>
            </div>
            <Badge tone="success">Aktif</Badge>
          </div>
        </Card>
        <p className="text-xs text-slate-400 text-center mt-4 px-6 sub">Seluruh pembayaran MizuOne diproses aman lewat QRIS Faspay. Metode lain akan ditambahkan menyusul.</p>
      </div>
    </div>
  );
}

/* =========================================================================
   BANTUAN — FAQ + Syarat & Ketentuan
   ========================================================================= */
function FaqItem({ q, children }){
  const [open, setOpen] = useState(false);
  return (
    <div>
      <button onClick={()=>setOpen(o=>!o)} className="w-full flex items-center justify-between gap-3 p-4 text-left active:scale-[.99] transition">
        <span className="font-semibold text-sm">{q}</span>
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={"w-5 h-5 text-slate-400 shrink-0 transition-transform "+(open?'rotate-180':'')}><path d="m6 9 6 6 6-6"/></svg>
      </button>
      {open && <div className="px-4 pb-4 -mt-1 text-sm text-slate-500 dark:text-slate-400 sub leading-relaxed fade-in">{children}</div>}
    </div>
  );
}
function Bantuan({ back }){
  const sk = [
    'Layanan MizuOne dikelola oleh PT. Dinasty Sahabat Mulia.',
    'Saldo yang telah diisi tidak dapat diuangkan kembali (non-refundable), kecuali terjadi kegagalan sistem yang terverifikasi.',
    'Harga air dapat berubah sewaktu-waktu dengan pemberitahuan di aplikasi.',
    'Pengguna bertanggung jawab menjaga kerahasiaan PIN dan kode OTP. Jangan bagikan ke siapa pun.',
    'Transaksi yang telah berhasil diproses tidak dapat dibatalkan.',
  ];
  return (
    <div className="relative fade-in" data-screen-label="Bantuan">
      <WaterHeader className="absolute top-0 left-0 right-0 h-44 rounded-b-[2rem]"><div className="h-44"></div></WaterHeader>
      <div className={"relative "+wrapNarrow+" px-4 pt-5 pb-24"}>
        <div className="relative z-10"><ScreenTop onBack={back} title="Pusat Bantuan & S&K" light/></div>

        <Card className="p-4 pop">
          <div className="text-sm font-semibold mb-3">Butuh bantuan langsung?</div>
          <div className="grid grid-cols-2 gap-2.5">
            <button onClick={()=>openExt(LINKS.wa)} className="inline-flex items-center justify-center gap-2 min-h-[44px] px-3 rounded-xl bg-[#25D366] text-white font-semibold active:scale-[.98] transition">
              <svg viewBox="0 0 24 24" fill="currentColor" className="w-5 h-5"><path d="M12 2a10 10 0 0 0-8.6 15l-1.3 4.7L7 20.4A10 10 0 1 0 12 2Zm5.3 14.1c-.2.6-1.3 1.2-1.8 1.2-.5.1-1 .1-1.7-.1-.4-.1-.9-.3-1.6-.6-2.8-1.2-4.6-4-4.7-4.2-.1-.2-1.1-1.5-1.1-2.8 0-1.3.7-2 .9-2.2.2-.3.5-.3.7-.3h.5c.2 0 .4 0 .6.5l.8 1.9c.1.2.1.4 0 .6l-.4.6c-.2.2-.3.3-.1.6.2.3.8 1.3 1.7 2.1 1.2 1 2.1 1.4 2.4 1.5.2.1.4.1.6-.1l.7-.9c.2-.2.4-.2.6-.1l1.8.9c.3.1.4.2.5.3 0 .2 0 .8-.2 1.3Z"/></svg> Mizu
            </button>
            <button onClick={()=>openExt(LINKS.tg)} className="inline-flex items-center justify-center gap-2 min-h-[44px] px-3 rounded-xl bg-[#229ED9] text-white font-semibold active:scale-[.98] transition">
              <svg viewBox="0 0 24 24" fill="currentColor" className="w-5 h-5"><path d="M21.9 4.5 18.7 19c-.2 1-.9 1.3-1.7.8l-4.6-3.4-2.2 2.1c-.2.2-.4.4-.9.4l.3-4.6 8.5-7.7c.4-.3-.1-.5-.6-.2L7.3 13.4l-4.5-1.4c-1-.3-1-1 .2-1.4l17.6-6.8c.8-.3 1.5.2 1.3 1.3Z"/></svg> Mizuki
            </button>
          </div>
        </Card>

        <SectionLabel>Pertanyaan Umum</SectionLabel>
        <Card className="divide-y divide-slate-100 dark:divide-slate-800 overflow-hidden">
          <FaqItem q="Bagaimana cara isi ulang air?">Pilih <b>Topup Digital</b> atau <b>Topup Kartu</b> di beranda, tentukan nominal, lalu bayar lewat QRIS Faspay.</FaqItem>
          <FaqItem q="Apa fungsi PIN keamanan?">PIN 4-digit melindungi saldomu. PIN diminta setiap kali membeli air dari saldo digital agar tidak bisa dipakai orang lain.</FaqItem>
          <FaqItem q="Metode pembayaran apa yang didukung?">Semua aplikasi yang mendukung QRIS — GoPay, OVO, DANA, ShopeePay, hingga m-banking bank apa pun.</FaqItem>
          <FaqItem q="Saldo belum bertambah setelah bayar?">Pembayaran QRIS umumnya masuk dalam 1–2 menit. Jika lebih lama, hubungi Mizu (WhatsApp) dengan menyertakan ID Transaksi pada e-struk kamu.</FaqItem>
          <FaqItem q="Bagaimana cara mendapat kartu fisik MizuOne?">Hubungi agen kami lewat WhatsApp untuk pemesanan kartu fisik MizuOne.</FaqItem>
        </Card>

        <SectionLabel>Syarat &amp; Ketentuan</SectionLabel>
        <Card className="p-5">
          <ol className="space-y-3 text-sm text-slate-500 dark:text-slate-400 sub">
            {sk.map((t,i)=>(
              <li key={i} className="flex gap-3">
                <span className="shrink-0 w-5 h-5 rounded-full bg-mizu-light text-mizu grid place-items-center text-[11px] font-bold">{i+1}</span>
                <span className="leading-relaxed">{t}</span>
              </li>
            ))}
          </ol>
          <p className="text-xs text-slate-400 mt-5 pt-4 border-t border-slate-100 dark:border-slate-800">© 2025 MizuOne · PT. Dinasty Sahabat Mulia · Jl. Raya Randu Agung No. 246, Singosari, Malang</p>
        </Card>
      </div>
    </div>
  );
}

Object.assign(window, { Footer, Landing, DigitalLogin, DaftarDigital, DigitalHome, DigitalTopup, DigitalBeli, DigitalSukses, Riwayat, Akun, Struk, EditProfil, UbahPin, MetodeBayar, Bantuan });
