import { useState, useRef, useEffect, useCallback } from "react";

// ─── CONFIG ──────────────────────────────────────────────────────────────────
const GOOGLE_CLIENT_ID = "341636884631-ohtrvrk226om6juedq7h3tc8d0ab0d9d.apps.googleusercontent.com";
const API_URL = "https://calcheckai-api.fun-ae5.workers.dev";

// ─── CONSTANTS ───────────────────────────────────────────────────────────────
const CATS=[{id:"breakfast",label:"Breakfast",emoji:"🌅"},{id:"lunch",label:"Lunch",emoji:"☀️"},{id:"dinner",label:"Dinner",emoji:"🌙"},{id:"snacks",label:"Snacks",emoji:"🍿"}];
const ACT=[{id:"sedentary",label:"Sedentary",sub:"Little or no exercise",emoji:"🛋️",mult:1.2},{id:"light",label:"Lightly Active",sub:"1–3 days/week",emoji:"🚶",mult:1.375},{id:"moderate",label:"Moderately Active",sub:"3–5 days/week",emoji:"🏃",mult:1.55},{id:"active",label:"Very Active",sub:"6–7 days/week",emoji:"🏋️",mult:1.725}];
const GOALS=[{id:"lose",label:"Lose weight",sub:"500 kcal deficit",emoji:"📉",adj:-500},{id:"maintain",label:"Maintain",sub:"Stay at current weight",emoji:"⚖️",adj:0},{id:"gain",label:"Build muscle",sub:"300 kcal surplus",emoji:"💪",adj:300}];
const LTYPES=["country","region","city","organisation","sub-group","other"];

// ─── HELPERS ─────────────────────────────────────────────────────────────────
const getCat=()=>{const h=new Date().getHours();if(h>=4&&h<11)return"breakfast";if(h>=11&&h<15)return"lunch";if(h>=17&&h<22)return"dinner";return"snacks";};
const dayKey=d=>d.toDateString();const todayKey=()=>dayKey(new Date());
const getLast7=()=>{const today=new Date(),day=today.getDay(),diff=day===0?6:day-1,mon=new Date(today);mon.setDate(today.getDate()-diff);const a=[];for(let i=0;i<7;i++){const d=new Date(mon);d.setDate(mon.getDate()+i);a.push(d);}return a;};
const getLastN=n=>{const a=[];for(let i=n-1;i>=0;i--){const d=new Date();d.setDate(d.getDate()-i);a.push(d);}return a;};
const dayShort=d=>["S","M","T","W","T","F","S"][d.getDay()];
const dayFmt=d=>{const t=todayKey(),y=new Date();y.setDate(y.getDate()-1);if(dayKey(d)===t)return"Today";if(dayKey(d)===dayKey(y))return"Yesterday";return d.toLocaleDateString(undefined,{weekday:"long",month:"short",day:"numeric"});};
const sumM=m=>({total:m.reduce((s,x)=>s+(x.total||0),0),protein:m.reduce((s,x)=>s+(x.totalProtein||0),0),carbs:m.reduce((s,x)=>s+(x.totalCarbs||0),0),fat:m.reduce((s,x)=>s+(x.totalFat||0),0),fiber:m.reduce((s,x)=>s+(x.totalFiber||0),0),sodium:m.reduce((s,x)=>s+(x.totalSodium||0),0),sugar:m.reduce((s,x)=>s+(x.totalSugar||0),0)});
const sumI=i=>({totalProtein:i.reduce((s,x)=>s+(x.protein||0),0),totalCarbs:i.reduce((s,x)=>s+(x.carbs||0),0),totalFat:i.reduce((s,x)=>s+(x.fat||0),0),totalFiber:i.reduce((s,x)=>s+(x.fiber||0),0),totalSodium:i.reduce((s,x)=>s+(x.sodium||0),0),totalSugar:i.reduce((s,x)=>s+(x.sugar||0),0)});
const favName=items=>!items?.length?"Saved":items.length===1?items[0].name:items.length===2?`${items[0].name} + ${items[1].name}`:`${items[0].name} + ${items.length-1} more`;
const itemsEq=(a,b)=>{if(!a||!b||a.length!==b.length)return false;return[...a].map(x=>x.name).sort().every((x,i)=>x===[...b].map(y=>y.name).sort()[i]);};
const loadH=()=>{try{const n=localStorage.getItem("kcal_history");if(n)return JSON.parse(n);}catch{}return{};};
const loadF=()=>{try{const s=localStorage.getItem("kcal_favorites");return s?JSON.parse(s):[]}catch{return[];}};
const getThm=()=>{try{const s=localStorage.getItem("kcal_theme");if(s)return s;}catch{}return"dark";};
const getToken=()=>{try{return localStorage.getItem("kcal_token")||"";}catch{return"";}};
// Device fingerprint — lightweight, non-invasive
const getDeviceFP=()=>{
  try{
    const parts=[
      navigator.userAgent,
      screen.width+"x"+screen.height,
      screen.colorDepth,
      navigator.language,
      new Date().getTimezoneOffset(),
      navigator.hardwareConcurrency||"?",
      navigator.platform||"?",
    ].join("|");
    // Simple hash
    let h=0;for(let i=0;i<parts.length;i++){h=Math.imul(31,h)+parts.charCodeAt(i)|0;}
    return Math.abs(h).toString(36);
  }catch{return "unknown";}
};

const calcT=({age,gender,heightCm,weightKg,activityLevel,goal})=>{const bmr=gender==="male"?10*weightKg+6.25*heightCm-5*age+5:10*weightKg+6.25*heightCm-5*age-161;const mult=ACT.find(a=>a.id===activityLevel)?.mult||1.2;const adj=GOALS.find(g=>g.id===goal)?.adj||0;const kcal=Math.round(bmr*mult+adj);return{calories:kcal,protein:Math.round(kcal*.25/4),carbs:Math.round(kcal*.5/4),fat:Math.round(kcal*.25/9)};};
const CIRC=2*Math.PI*82;
const rFill={on:"#c8f135",warn:"#ffb347",over:"#ff4d4d"};
const rTxt={dark:{on:"#c8f135",warn:"#ffb347",over:"#ff4d4d"},light:{on:"#3d6400",warn:"#8a5000",over:"#c82020"}};

// ─── API ─────────────────────────────────────────────────────────────────────
const api=async(path,opts={})=>{
  const token=getToken();
  const res=await fetch(API_URL+path,{...opts,headers:{...(opts.headers||{}),"Content-Type":"application/json",...(token?{Authorization:`Bearer ${token}`}:{})}});
  if(!res.ok){const e=await res.json().catch(()=>({error:res.statusText}));throw new Error(e.error||res.statusText);}
  return res.json();
};

// ─── AI HELPERS ──────────────────────────────────────────────────────────────
const imgP=`Identify every visible food item. For each item estimate: name, calories based on visible portion, protein, carbs, fat, fiber, sodium (mg), sugar in grams, and a matching emoji. Return ONLY valid JSON:
{"items":[{"name":"Food","calories":250,"emoji":"🍕","protein":12,"carbs":30,"fat":8,"fiber":2,"sodium":320,"sugar":4}],"total":250,"notes":"note"}`;
const txtP=d=>`Estimate calories and macros for: "${d}". Standard portions if unspecified. Return ONLY valid JSON:
{"items":[{"name":"Food","calories":250,"emoji":"🍕","protein":12,"carbs":30,"fat":8,"fiber":2,"sodium":320,"sugar":4}],"total":250,"notes":"note"}`;
const callAI=async(msgs,setScan,setLoad)=>{setLoad(true);setScan(null);try{const res=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:"claude-sonnet-4-20250514",max_tokens:1200,messages:msgs})});const j=await res.json();const raw=j.content?.map(b=>b.text||"").join("")||"";setScan(JSON.parse(raw.replace(/```json|```/g,"").trim()));}catch{setScan({error:true});}finally{setLoad(false);}};

// ─── ALL CSS ─────────────────────────────────────────────────────────────────
// ─── ZXING BARCODE ENGINE ─────────────────────────────────────────────────────
const loadZX=()=>new Promise(res=>{if(window.ZXing){res();return;}const s=document.createElement("script");s.src="https://cdnjs.cloudflare.com/ajax/libs/zxing-js/0.21.3/zxing.min.js";s.onload=res;s.onerror=res;document.head.appendChild(s);});
const initZX=async()=>{await loadZX();if(!window.ZXing)return null;try{const h=new Map();h.set(window.ZXing.DecodeHintType.POSSIBLE_FORMATS,[window.ZXing.BarcodeFormat.EAN_13,window.ZXing.BarcodeFormat.EAN_8,window.ZXing.BarcodeFormat.UPC_A,window.ZXing.BarcodeFormat.UPC_E,window.ZXing.BarcodeFormat.CODE_128]);h.set(window.ZXing.DecodeHintType.TRY_HARDER,true);return new window.ZXing.MultiFormatReader(h);}catch{return null;}};
const lookupBarcode=async bc=>{const r=await fetch(`https://world.openfoodfacts.org/api/v0/product/${bc}.json`);const d=await r.json();if(d.status!==1)throw new Error("Not found");const p=d.product,n=p.nutriments||{},hasSrv=!!n["energy-kcal_serving"],sfx=hasSrv?"_serving":"_100g",per=hasSrv?"serving":"100g";return{name:p.product_name||"Unknown",brand:p.brands||"",imageUrl:p.image_front_url||null,per,calories:Math.round(n[`energy-kcal${sfx}`]||0),protein:Math.round(n[`proteins${sfx}`]||0),carbs:Math.round(n[`carbohydrates${sfx}`]||0),fat:Math.round(n[`fat${sfx}`]||0),fiber:Math.round(n[`fiber${sfx}`]||0),sodium:Math.round(n[`sodium${sfx}`]||0),sugar:Math.round(n[`sugars${sfx}`]||0),barcode:bc,type:"barcode"};};

// ─── SMART SCANNER COMPONENT ───────────────────────────────────────────────────
// Single viewfinder — auto-detects food vs barcode vs nutrition label
function SmartScanner({theme,onFoodCapture,onBarcodeResult,onLabelCapture,onClose}){
  const videoRef=useRef(null);const canvasRef=useRef(null);const streamRef=useRef(null);
  const rafRef=useRef(null);const readerRef=useRef(null);const lastCode=useRef(null);
  const [mode,setMode]=useState("food");
  const [torch,setTorch]=useState(false);
  const [ready,setReady]=useState(false);

  useEffect(()=>{
    initZX().then(r=>{readerRef.current=r;setReady(!!r);});
    startCam();
    return()=>stop();
  },[]);

  const stop=()=>{if(rafRef.current)cancelAnimationFrame(rafRef.current);streamRef.current?.getTracks().forEach(t=>t.stop());streamRef.current=null;};

  const startCam=async()=>{
    try{
      const s=await navigator.mediaDevices.getUserMedia({video:{facingMode:"environment",width:{ideal:1280},height:{ideal:1280}}});
      streamRef.current=s;videoRef.current.srcObject=s;await videoRef.current.play();
      loop();
    }catch(e){onClose&&onClose("Camera access denied — use photo or manual entry");}
  };

  const loop=()=>{
    rafRef.current=requestAnimationFrame(()=>{
      if(!streamRef.current)return;
      const v=videoRef.current,c=canvasRef.current;
      if(v?.readyState===v.HAVE_ENOUGH_DATA){
        c.width=v.videoWidth;c.height=v.videoHeight;
        const ctx=c.getContext("2d",{willReadFrequently:true});
        ctx.drawImage(v,0,0);
        if(readerRef.current){
          try{
            const img=ctx.getImageData(0,0,c.width,c.height);
            const lum=new window.ZXing.RGBLuminanceSource(img.data,c.width,c.height);
            const bmp=new window.ZXing.BinaryBitmap(new window.ZXing.HybridBinarizer(lum));
            const res=readerRef.current.decode(bmp);
            if(res?.getText()&&res.getText()!==lastCode.current){
              lastCode.current=res.getText();setMode("barcode");
              stop();onBarcodeResult(res.getText());return;
            }
          }catch{}
        }
        setMode("food");
      }
      loop();
    });
  };

  const capture=async()=>{
    const v=videoRef.current,c=canvasRef.current;
    if(!v||!c)return;
    c.width=v.videoWidth;c.height=v.videoHeight;
    c.getContext("2d").drawImage(v,0,0);
    const base64=c.toDataURL("image/jpeg",.88).split(",")[1];
    stop();
    onFoodCapture(base64,"image/jpeg");
  };

  const captureLabel=()=>{
    const v=videoRef.current,c=canvasRef.current;
    if(!v||!c)return;
    c.width=v.videoWidth;c.height=v.videoHeight;
    c.getContext("2d").drawImage(v,0,0);
    const base64=c.toDataURL("image/jpeg",.92).split(",")[1];
    stop();
    onLabelCapture(base64,"image/jpeg");
  };

  const toggleTorch=async()=>{
    const t=streamRef.current?.getVideoTracks()[0];if(!t)return;
    try{await t.applyConstraints({advanced:[{torch:!torch}]});setTorch(x=>!x);}catch{}
  };

  return(
    <div className="sc" data-theme={theme} style={{position:"fixed",inset:0,zIndex:60,display:"flex",flexDirection:"column",background:"#000"}}>
      <div style={{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"18px 20px",position:"relative",zIndex:2}}>
        <button style={{background:"none",border:"none",color:"#fff",fontSize:".9rem",display:"flex",alignItems:"center",gap:6}} onClick={()=>{stop();onClose&&onClose();}}>
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
          Cancel
        </button>
        <button style={{background:torch?"rgba(200,241,53,.2)":"rgba(255,255,255,.08)",border:"1px solid",borderColor:torch?"rgba(200,241,53,.5)":"rgba(255,255,255,.1)",borderRadius:10,width:36,height:36,color:torch?"#c8f135":"#8a8a8a",display:"flex",alignItems:"center",justifyContent:"center"}} onClick={toggleTorch}>
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
        </button>
      </div>
      <div style={{flex:1,position:"relative",overflow:"hidden"}}>
        <video ref={videoRef} style={{width:"100%",height:"100%",objectFit:"cover"}} autoPlay playsInline muted/>
        <canvas ref={canvasRef} style={{display:"none"}}/>
        {/* Corner guides */}
        {["tl","tr","br","bl"].map(c=>(
          <div key={c} style={{position:"absolute",width:28,height:28,borderColor:"#c8f135",borderStyle:"solid",borderRadius:4,
            ...(c==="tl"?{top:60,left:20,borderWidth:"3px 0 0 3px",borderRadius:"4px 0 0 0"}:
               c==="tr"?{top:60,right:20,borderWidth:"3px 3px 0 0",borderRadius:"0 4px 0 0"}:
               c==="br"?{bottom:80,right:20,borderWidth:"0 3px 3px 0",borderRadius:"0 0 4px 0"}:
                        {bottom:80,left:20,borderWidth:"0 0 3px 3px",borderRadius:"0 0 0 4px"})}}/>
        ))}
        {/* Mode pill */}
        <div style={{position:"absolute",bottom:24,left:"50%",transform:"translateX(-50%)",display:"flex",alignItems:"center",gap:6,background:"rgba(0,0,0,.72)",backdropFilter:"blur(8px)",WebkitBackdropFilter:"blur(8px)",border:`1px solid ${mode==="barcode"?"rgba(255,255,255,.25)":"rgba(200,241,53,.3)"}`,borderRadius:100,padding:"7px 16px",fontSize:".76rem",fontWeight:600,color:mode==="barcode"?"#fff":"#c8f135",whiteSpace:"nowrap",transition:"all .3s"}}>
          <span style={{width:6,height:6,borderRadius:"50%",background:mode==="barcode"?"#fff":"#c8f135",animation:mode==="barcode"?"bl 0.8s linear infinite":"none"}}/>
          {mode==="barcode"?"🔖 Barcode detected":"📷 Point at food or barcode"}
        </div>
      </div>
      {/* Action row */}
      <div style={{padding:"20px 24px 36px",display:"flex",alignItems:"center",justifyContent:"space-between",gap:12,background:"rgba(0,0,0,.9)"}}>
        <button style={{flex:1,height:52,background:"rgba(255,255,255,.06)",border:"1px solid rgba(255,255,255,.1)",borderRadius:14,color:"#8a8a8a",fontSize:".8rem",fontWeight:500,display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",gap:3}} onClick={captureLabel}>
          <span style={{fontSize:"1.1rem"}}>🏷️</span>Nutrition label
        </button>
        <button style={{width:70,height:70,background:"#c8f135",border:"none",borderRadius:"50%",display:"flex",alignItems:"center",justifyContent:"center",boxShadow:"0 0 0 6px rgba(200,241,53,.15),0 0 0 12px rgba(200,241,53,.06)",flexShrink:0}} onClick={capture}>
          <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#000" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
        </button>
        <div style={{flex:1}}/>
      </div>
      <style>{`@keyframes bl{0%,100%{opacity:1}50%{opacity:.2}}`}</style>
    </div>
  );
}

// ─── GAMIFICATION (CLIENT) ───────────────────────────────────────────────────
const XP_VALS={log_meal:10,hit_goal:25,scan_barcode:5,scan_label:5,join_squad:20,follow_user:10};
const LEVELS=[{level:1,xp:0,title:"Newcomer"},{level:2,xp:100,title:"Tracker"},{level:3,xp:300,title:"Consistent"},{level:4,xp:600,title:"Dedicated"},{level:5,xp:1000,title:"Committed"},{level:6,xp:1750,title:"Elite"},{level:7,xp:2750,title:"Champion"},{level:8,xp:4000,title:"Legend"}];
const BADGES=[{id:"first_meal",emoji:"🌱",name:"First Bite"},{id:"streak_7",emoji:"🔥",name:"On Fire"},{id:"streak_30",emoji:"💪",name:"Consistent"},{id:"streak_100",emoji:"🎖️",name:"Century"},{id:"on_target_5",emoji:"🎯",name:"On Target"},{id:"scans_10",emoji:"📸",name:"Shutterbug"},{id:"label_first",emoji:"🔖",name:"Label Reader"},{id:"barcodes_5",emoji:"🛒",name:"Barcode Pro"},{id:"first_follow",emoji:"🤝",name:"Social"},{id:"first_squad",emoji:"🏘️",name:"Community"},{id:"squads_5",emoji:"🌍",name:"Squad Builder"},{id:"favs_3",emoji:"⭐",name:"Favourites"},{id:"squad_top",emoji:"🏆",name:"Weekly Champion"},{id:"hydrated",emoji:"💧",name:"Hydrated"},{id:"flow_state",emoji:"🌊",name:"Flow State"}];
const getLevel=xp=>{let l=LEVELS[0];for(const v of LEVELS){if(xp>=v.xp)l=v;}const n=LEVELS.find(v=>v.xp>xp)||null;return{...l,nextXp:n?.xp||null,nextTitle:n?.title||null};};
const logMealToServer=async(calories,goal,scanType,token)=>{if(!token||API_URL==="YOUR_WORKER_URL_HERE")return null;try{const r=await fetch(API_URL+"/api/log-meal",{method:"POST",headers:{"Content-Type":"application/json","Authorization":"Bearer "+token},body:JSON.stringify({calories,goal,scanType})});return r.ok?r.json():null;}catch{return null;}};

// ─── WATER & SHARE CONSTANTS ─────────────────────────────────────────────────
const GLASS_ML = 250; // 1 glass = 250ml
const mlToGlasses = ml => Math.round(ml / GLASS_ML);
const glassesMl  = g  => g * GLASS_ML;

const calcWaterGoal = weightKg => {
  if(!weightKg||weightKg<30) return 2000;
  return Math.round(weightKg*35/100)*100;
};

const logWaterToServer = async(intakeMl, unitPref, usePersonalised, token) => {
  if(!token||API_URL==="YOUR_WORKER_URL_HERE") return null;
  try{
    const r = await fetch(API_URL+"/api/water",{method:"POST",headers:{"Content-Type":"application/json","Authorization":"Bearer "+token},body:JSON.stringify({intake_ml:intakeMl,unit_pref:unitPref,use_personalised:usePersonalised})});
    return r.ok ? r.json() : null;
  }catch{return null;}
};

// ─── SHARE CARD GENERATOR ─────────────────────────────────────────────────────
// Generates a 1080x1920 canvas image (portrait, stories format)
// Returns a data URL for native share sheet
const generateShareCard = (type, data) => {
  const canvas = document.createElement("canvas");
  canvas.width = 1080; canvas.height = 1920;
  const ctx = canvas.getContext("2d");

  // Background
  ctx.fillStyle = "#080808";
  ctx.fillRect(0, 0, 1080, 1920);

  // Lime top bar
  ctx.fillStyle = "#c8f135";
  ctx.fillRect(0, 0, 1080, 12);

  // Subtle grid
  ctx.strokeStyle = "#141414";
  ctx.lineWidth = 1;
  for(let x=0;x<1080;x+=40){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,1920);ctx.stroke();}
  for(let y=0;y<1920;y+=40){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(1080,y);ctx.stroke();}

  // Radial glow
  const grd = ctx.createRadialGradient(540,960,0,540,960,700);
  grd.addColorStop(0,"rgba(200,241,53,0.07)");
  grd.addColorStop(1,"rgba(8,8,8,0)");
  ctx.fillStyle = grd;
  ctx.fillRect(0,0,1080,1920);

  // Brand name
  ctx.fillStyle = "#f2f2f2";
  ctx.font = "bold 52px Arial, sans-serif";
  ctx.textAlign = "left";
  ctx.fillText("CalCheckAI", 80, 140);

  // Main achievement number/emoji — large centred
  const { emoji, headline, subline, detail, handle } = data;

  ctx.textAlign = "center";
  ctx.font = "bold 200px Arial, sans-serif";
  ctx.fillText(emoji, 540, 820);

  // Headline
  ctx.fillStyle = "#c8f135";
  ctx.font = "900 96px Arial Black, sans-serif";
  ctx.fillText(headline, 540, 980);

  // Subline
  ctx.fillStyle = "#f2f2f2";
  ctx.font = "bold 56px Arial, sans-serif";
  ctx.fillText(subline, 540, 1080);

  // Detail
  if(detail){
    ctx.fillStyle = "#8a8a8a";
    ctx.font = "44px Arial, sans-serif";
    ctx.fillText(detail, 540, 1160);
  }

  // Handle
  if(handle){
    ctx.fillStyle = "#c8f135";
    ctx.font = "bold 44px Arial, sans-serif";
    ctx.fillText("@"+handle, 540, 1700);
  }

  // Bottom lime bar
  ctx.fillStyle = "rgba(200,241,53,0.3)";
  ctx.fillRect(0, 1908, 1080, 12);

  return canvas.toDataURL("image/png");
};

const shareCard = async (type, data, handle) => {
  const templates = {
    streak:    { emoji:"🔥", headline: data.days+" DAYS",       subline:"Streak Achieved",      detail:"CalCheckAI Food Tracker" },
    badge:     { emoji: data.emoji, headline: data.name,         subline:"Badge Unlocked",       detail:"CalCheckAI Achievements"  },
    weekly:    { emoji:"📊", headline: data.score+"/100",        subline:"Weekly Score",         detail:"CalCheckAI Weekly Report" },
    leaderboard:{ emoji:"🥇", headline:"#1 in Squad",            subline: data.squadName||"Squad Leader", detail:"CalCheckAI Leaderboard" },
    water:     { emoji:"💧", headline: data.days+" DAYS",        subline:"Hydration Streak",     detail:"CalCheckAI Water Tracker" },
  };
  const tmpl = templates[type];
  if(!tmpl) return;

  const cardData = { ...tmpl, handle };
  const imageUrl = generateShareCard(type, cardData);

  if(navigator.share && navigator.canShare){
    try{
      const blob = await(await fetch(imageUrl)).blob();
      const file = new File([blob],"calcheckai-achievement.png",{type:"image/png"});
      if(navigator.canShare({files:[file]})){
        await navigator.share({ files:[file], title:"CalCheckAI Achievement", text: tmpl.subline+" on CalCheckAI" });
        return;
      }
    }catch{}
  }
  // Fallback — download the image
  const a = document.createElement("a");
  a.href = imageUrl; a.download = "calcheckai-achievement.png"; a.click();
};

// ─── SOCIAL POSTS CONSTANTS ──────────────────────────────────────────────────
const REACTIONS = [
  {id:'heart',   emoji:'❤️', label:'Like'},
  {id:'fire',    emoji:'🔥', label:'Fire'},
  {id:'strong',  emoji:'💪', label:'Strong'},
  {id:'laugh',   emoji:'😂', label:'Laugh'},
  {id:'inspire', emoji:'✨', label:'Inspire'},
  {id:'respect', emoji:'🙌', label:'Respect'},
  {id:'wow',     emoji:'😮', label:'Wow'},
];
const POST_LIMITS = {text:200, image:50, total:250};

// ─── PM HELPERS ──────────────────────────────────────────────────────────────
const DEMO_CONVS = [
  {id:"c1",other_user:{id:"u2",name:"Jordan Kim",handle:"jordan.kim"},last_message:"Nice work on the streak! 🔥",last_message_at:new Date(Date.now()-1800000).toISOString(),my_unread:2},
  {id:"c2",other_user:{id:"u3",name:"Maria Santos",handle:"maria.santos"},last_message:"Same squad, let's go 💪",last_message_at:new Date(Date.now()-86400000).toISOString(),my_unread:0},
  {id:"c3",other_user:{id:"u4",name:"Liam Chen",handle:"liam.chen"},last_message:"What's your daily goal?",last_message_at:new Date(Date.now()-172800000).toISOString(),my_unread:1},
];
const DEMO_MSGS = {
  c1:[
    {id:"m1",sender_id:"u2",text:"Hey! Saw you hit a 30-day streak 🔥 That's incredible!",created_at:new Date(Date.now()-3600000).toISOString()},
    {id:"m2",sender_id:"me",text:"Thanks! Been consistent with the morning scans. How's yours going?",created_at:new Date(Date.now()-3000000).toISOString()},
    {id:"m3",sender_id:"u2",text:"At 18 days — trying to catch up to you 😅",created_at:new Date(Date.now()-1800000).toISOString()},
    {id:"m4",sender_id:"u2",text:"Nice work on the streak! 🔥",created_at:new Date(Date.now()-900000).toISOString()},
  ],
};

const CSS=`
@import url('https://fonts.googleapis.com/css2?family=Syne:wght@700;800&family=DM+Sans:opsz,wght@9..40,400;9..40,500;9..40,600&display=swap');
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
html,body{min-height:100dvh;overflow-x:hidden;-webkit-font-smoothing:antialiased;font-family:'DM Sans',sans-serif;}
button{font-family:'DM Sans',sans-serif;cursor:pointer;}input,textarea,select{font-family:'DM Sans',sans-serif;}
.sc{--bg:#080808;--s1:#131313;--s2:#1c1c1c;--s3:#242424;--bd:#2c2c2c;--ac:#c8f135;--at:#c8f135;--tx:#f2f2f2;--mu:#5a5a5a;--m2:#8a8a8a;--re:#ff4d4d;--pr:#c8f135;--ca:#ffb347;--fa:#b4a3ff;--ov:rgba(0,0,0,.78);--sh:none;min-height:100dvh;background:var(--bg);color:var(--tx);transition:background .3s,color .2s;}
.sc[data-theme=light]{--bg:#f2f2ee;--s1:#fff;--s2:#ededea;--s3:#e5e5e0;--bd:#d8d8d2;--at:#3d6400;--tx:#111;--mu:#9a9a96;--m2:#6a6a66;--re:#c82020;--pr:#3d6400;--ca:#8a5000;--fa:#4835b8;--ov:rgba(0,0,0,.48);--sh:0 1px 3px rgba(0,0,0,.08),0 4px 16px rgba(0,0,0,.06);}
.btn-lime{background:var(--ac);color:#000;border:none;border-radius:18px;height:60px;padding:0 28px;font-family:'Syne',sans-serif;font-weight:700;font-size:1rem;display:flex;align-items:center;justify-content:center;gap:10px;transition:transform .15s,filter .15s;width:100%;}
.btn-lime:hover{transform:scale(1.02);filter:brightness(1.06);}.btn-lime:active{transform:scale(.97);}
.btn-out{background:var(--s1);color:var(--tx);border:1px solid var(--bd);border-radius:18px;height:52px;padding:0 24px;font-size:.92rem;font-weight:500;display:flex;align-items:center;justify-content:center;gap:10px;transition:border-color .2s,color .2s;width:100%;box-shadow:var(--sh);}
.btn-out:hover{border-color:var(--ac);color:var(--at);}
.btn-dim{flex:1;height:48px;background:var(--s2);border:1px solid var(--bd);border-radius:14px;color:var(--mu);font-size:.9rem;transition:border-color .2s,color .2s;}
.btn-dim:hover{border-color:var(--m2);color:var(--tx);}
.btn-act{flex:2;height:48px;background:var(--ac);border:none;border-radius:14px;color:#000;font-family:'Syne',sans-serif;font-weight:700;font-size:.95rem;transition:filter .15s;}
.btn-act:hover{filter:brightness(1.06);}.btn-act:disabled{opacity:.4;cursor:not-allowed;}
.btn-act1{flex:1;height:48px;background:var(--ac);border:none;border-radius:14px;color:#000;font-family:'Syne',sans-serif;font-weight:700;font-size:.95rem;transition:filter .15s;}
.btn-act1:hover{filter:brightness(1.06);}
.btn-follow{height:38px;padding:0 20px;background:var(--ac);border:none;border-radius:100px;color:#000;font-family:'Syne',sans-serif;font-weight:700;font-size:.82rem;transition:filter .15s;}
.btn-follow:hover{filter:brightness(1.06);}
.btn-unfollow{height:38px;padding:0 20px;background:var(--s2);border:1px solid var(--bd);border-radius:100px;color:var(--mu);font-size:.82rem;transition:all .18s;}
.btn-unfollow:hover{border-color:var(--re);color:var(--re);}
.overlay{position:fixed;inset:0;background:var(--ov);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);z-index:50;display:flex;align-items:flex-end;animation:fadeIn .2s ease;}
.overlay.center{align-items:center;justify-content:center;padding:24px;}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
.sheet{width:100%;background:var(--s1);border-radius:26px 26px 0 0;border-top:1px solid var(--bd);padding:12px 24px 36px;animation:shUp .32s cubic-bezier(.4,0,.2,1);max-height:88dvh;overflow-y:auto;-webkit-overflow-scrolling:touch;}
@keyframes shUp{from{transform:translateY(100%)}to{transform:translateY(0)}}
.handle{width:36px;height:4px;background:var(--s3);border-radius:2px;margin:0 auto 20px;}
.preview{width:100%;max-height:220px;object-fit:cover;border-radius:16px;margin-bottom:20px;display:block;}
.scanning{display:flex;flex-direction:column;align-items:center;padding:40px 0 28px;gap:18px;}
.spinner{width:44px;height:44px;border:3px solid var(--bd);border-top-color:var(--ac);border-radius:50%;animation:spin .75s linear infinite;}
@keyframes spin{to{transform:rotate(360deg)}}.spin-t{font-size:.9rem;color:var(--mu);}
.r-items{display:flex;flex-direction:column;gap:8px;margin-bottom:14px;}
.r-item{display:flex;align-items:flex-start;justify-content:space-between;padding:12px 14px;background:var(--s2);border-radius:14px;gap:12px;}
.r-il{display:flex;align-items:flex-start;gap:10px;font-size:.88rem;flex:1;min-width:0;}
.r-blk{display:flex;flex-direction:column;gap:4px;min-width:0;}
.r-nm{color:var(--tx);font-weight:500;}.r-mac{font-size:.7rem;color:var(--mu);display:flex;gap:8px;flex-wrap:wrap;}.r-mac span{white-space:nowrap;}
.r-em{font-size:1.15rem;flex-shrink:0;line-height:1.4;}.r-cal{font-family:'Syne',sans-serif;font-weight:700;font-size:.9rem;color:var(--at);flex-shrink:0;padding-top:2px;}
.r-tot{display:flex;align-items:center;justify-content:space-between;padding:14px 0;border-top:1px solid var(--bd);border-bottom:1px solid var(--bd);margin-bottom:14px;}
.r-tl{font-size:.85rem;color:var(--mu);}.r-tn{font-family:'Syne',sans-serif;font-weight:800;font-size:1.5rem;letter-spacing:-.5px;}
.r-mr{display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;}
.r-mc{background:var(--s2);border-radius:10px;padding:8px 10px;display:flex;flex-direction:column;gap:2px;}
.r-mcl{font-size:.62rem;text-transform:uppercase;letter-spacing:1px;color:var(--mu);font-weight:500;display:flex;align-items:center;gap:4px;}
.r-mcn{font-family:'Syne',sans-serif;font-weight:700;font-size:.95rem;color:var(--tx);}
.r-mcn span{font-size:.65rem;color:var(--mu);font-weight:400;font-family:'DM Sans',sans-serif;}
.r-notes{font-size:.78rem;color:var(--mu);margin-bottom:20px;padding:10px 14px;background:var(--s2);border-radius:12px;border-left:2px solid var(--ac);line-height:1.5;}
.r-acts{display:flex;gap:10px;margin-top:18px;}
.cat-chips{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin-bottom:18px;}
.cat-chip{background:var(--s2);border:1px solid var(--bd);border-radius:12px;padding:10px 6px;color:var(--mu);font-size:.78rem;font-weight:500;display:flex;flex-direction:column;align-items:center;gap:4px;transition:all .18s;min-height:54px;justify-content:center;}
.cat-chip:hover{border-color:var(--m2);color:var(--tx);}.cat-chip.on{background:rgba(200,241,53,.08);border-color:var(--ac);color:var(--at);}
.cat-chip-em{font-size:1.1rem;line-height:1;}.cat-lbl{font-size:.7rem;text-transform:uppercase;letter-spacing:1.2px;color:var(--mu);font-weight:500;margin-bottom:9px;}
.err-st{text-align:center;padding:36px 0 24px;}.err-ic{font-size:2.2rem;display:block;margin-bottom:12px;}.err-ms{color:var(--re);font-size:.9rem;margin-bottom:20px;line-height:1.5;}
.mdot{width:6px;height:6px;border-radius:50%;flex-shrink:0;display:inline-block;}
.gbox{background:var(--s1);border:1px solid var(--bd);border-radius:22px;padding:28px 24px;width:100%;max-width:360px;animation:popIn .22s cubic-bezier(.4,0,.2,1);}
@keyframes popIn{from{transform:scale(.94);opacity:0}to{transform:scale(1);opacity:1}}
.g-ti{font-family:'Syne',sans-serif;font-weight:700;font-size:1.05rem;margin-bottom:4px;color:var(--tx);}
.g-su{font-size:.78rem;color:var(--mu);margin-bottom:20px;}
.g-inp{width:100%;background:var(--s2);border:1px solid var(--bd);border-radius:14px;padding:14px;font-family:'Syne',sans-serif;font-size:1.5rem;font-weight:700;color:var(--tx);text-align:center;outline:none;margin-bottom:16px;transition:border-color .2s;min-height:60px;}
.g-inp:focus{border-color:var(--ac);}
.m-ta{width:100%;background:var(--s2);border:1px solid var(--bd);border-radius:14px;padding:14px 16px;font-size:1rem;color:var(--tx);outline:none;margin-bottom:8px;transition:border-color .2s;min-height:90px;resize:vertical;line-height:1.5;}
.m-ta:focus{border-color:var(--ac);}.m-ta::placeholder{color:var(--mu);}
.m-ac{display:flex;gap:10px;}
.logo{font-family:'Syne',sans-serif;font-weight:800;font-size:1.2rem;letter-spacing:-.5px;color:var(--tx);}

.thm-btn{width:36px;height:36px;background:var(--s1);border:1px solid var(--bd);border-radius:10px;color:var(--m2);display:flex;align-items:center;justify-content:center;transition:all .2s;box-shadow:var(--sh);}
.thm-btn:hover{border-color:var(--ac);color:var(--at);}
.toast-w{position:fixed;inset:0;background:none;padding:0;animation:none;opacity:1;z-index:200;pointer-events:none;max-width:none;display:flex;align-items:flex-end;justify-content:center;padding-bottom:80px;}
.toast{pointer-events:auto;background:var(--s1);border:1px solid var(--bd);border-radius:100px;padding:10px 18px;color:var(--tx);font-size:.85rem;font-weight:500;display:flex;align-items:center;gap:8px;animation:toastIn .3s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 20px rgba(0,0,0,.15);white-space:nowrap;}
.toast.ok{border-color:var(--ac);}.toast-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;}.toast.ok .toast-dot{background:var(--ac);}.toast.inf .toast-dot{background:var(--m2);}
@keyframes toastIn{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}
.sh-tr{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:18px;}
.sh-ti{font-family:'Syne',sans-serif;font-weight:700;font-size:1.15rem;margin-bottom:4px;color:var(--tx);}
.sh-su{font-size:.8rem;color:var(--mu);}
.sv-btn{width:42px;height:42px;background:var(--s2);border:1px solid var(--bd);border-radius:12px;color:var(--mu);display:flex;align-items:center;justify-content:center;transition:all .2s;flex-shrink:0;}
.sv-btn:hover{border-color:var(--ac);color:var(--at);}.sv-btn.saved{background:rgba(200,241,53,.1);border-color:var(--ac);color:var(--at);}

/* guest */
.g-hdr{display:flex;align-items:center;justify-content:space-between;padding:22px 28px 0;}
.g-hero{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px 28px 40px;text-align:center;gap:10px;}
.g-eye{font-size:.72rem;text-transform:uppercase;letter-spacing:2px;color:var(--mu);font-weight:500;}
.g-hl{font-family:'Syne',sans-serif;font-weight:800;font-size:2.3rem;line-height:1.1;letter-spacing:-1px;color:var(--tx);margin:8px 0 4px;}
.g-hl span{color:var(--ac);}.g-sub{font-size:.88rem;color:var(--mu);margin-bottom:32px;line-height:1.5;}
.g-ctas{width:100%;max-width:320px;display:flex;flex-direction:column;gap:10px;}
.g-si{margin-top:24px;font-size:.8rem;color:var(--mu);}
.g-si button{background:none;border:none;color:var(--at);font-size:.8rem;font-weight:600;padding:2px 4px;text-decoration:underline;text-underline-offset:2px;}
.nudge{margin-top:14px;padding:14px 16px;background:rgba(200,241,53,.06);border:1px solid rgba(200,241,53,.2);border-radius:16px;}
.nudge-ti{font-family:'Syne',sans-serif;font-weight:700;font-size:.88rem;color:var(--at);margin-bottom:4px;display:flex;align-items:center;gap:6px;}
.nudge-bo{font-size:.78rem;color:var(--mu);line-height:1.5;margin-bottom:12px;}
.nudge-btn{width:100%;background:var(--ac);color:#000;border:none;border-radius:12px;height:44px;font-family:'Syne',sans-serif;font-weight:700;font-size:.88rem;}
/* signin */
.si-bk{background:none;border:none;color:var(--mu);padding:22px 24px;display:flex;align-items:center;gap:8px;font-size:.88rem;transition:color .2s;}
.si-bk:hover{color:var(--tx);}
.si-body{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px 32px 60px;text-align:center;}
.si-logo{font-family:'Syne',sans-serif;font-weight:800;font-size:2rem;letter-spacing:-.5px;margin-bottom:8px;color:var(--tx);}
.si-logo span{color:var(--ac);}.si-tag{font-size:.9rem;color:var(--mu);margin-bottom:48px;line-height:1.5;}
.si-btns{width:100%;max-width:300px;display:flex;flex-direction:column;gap:12px;}
.g-btn-w{min-height:44px;display:flex;align-items:center;justify-content:center;}
.g-fb{width:100%;height:48px;background:var(--s1);border:1px solid var(--bd);border-radius:100px;display:flex;align-items:center;justify-content:center;gap:10px;color:var(--tx);font-size:.92rem;font-weight:500;transition:border-color .2s;box-shadow:var(--sh);}
.g-fb:hover{border-color:var(--m2);}.si-div{display:flex;align-items:center;gap:12px;color:var(--mu);font-size:.75rem;}.si-dl{flex:1;height:1px;background:var(--bd);}
.demo-btn{width:100%;height:48px;background:transparent;border:1px dashed var(--bd);border-radius:100px;color:var(--mu);font-size:.82rem;transition:all .2s;}
.demo-btn:hover{border-color:var(--m2);color:var(--tx);}.si-note{margin-top:32px;font-size:.72rem;color:var(--mu);line-height:1.6;max-width:260px;}
/* onboarding */
.ob-w{max-width:430px;margin:0 auto;min-height:100dvh;display:flex;flex-direction:column;padding:0 28px 40px;}
.ob-hd{padding:24px 0 0;display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;}
.ob-sc{font-size:.75rem;color:var(--mu);}
.ob-prog{height:3px;background:var(--s2);border-radius:2px;margin-bottom:40px;overflow:hidden;}
.ob-pf{height:100%;background:var(--ac);border-radius:2px;transition:width .4s ease;}
.ob-st{flex:1;display:flex;flex-direction:column;animation:stIn .3s ease;}
@keyframes stIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
.ob-lbl{font-size:.7rem;text-transform:uppercase;letter-spacing:2px;color:var(--mu);font-weight:500;margin-bottom:12px;}
.ob-ti{font-family:'Syne',sans-serif;font-weight:800;font-size:1.9rem;line-height:1.1;letter-spacing:-.5px;color:var(--tx);margin-bottom:8px;}
.ob-su{font-size:.88rem;color:var(--mu);margin-bottom:36px;}
.ob-in{width:100%;background:var(--s2);border:1px solid var(--bd);border-radius:14px;padding:16px;font-family:'Syne',sans-serif;font-size:1.7rem;font-weight:700;color:var(--tx);outline:none;margin-bottom:16px;transition:border-color .2s;text-align:center;min-height:68px;-webkit-appearance:none;}
.ob-in:focus{border-color:var(--ac);}.ob-in.sm{font-size:1.3rem;}
.ob-row{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:16px;}
.ob-pill{background:var(--s2);border:1px solid var(--bd);border-radius:14px;padding:14px 16px;color:var(--mu);font-size:.88rem;font-weight:500;text-align:left;transition:all .18s;display:flex;align-items:center;gap:10px;width:100%;margin-bottom:8px;}
.ob-pill:hover{border-color:var(--m2);color:var(--tx);}.ob-pill.on{background:rgba(200,241,53,.08);border-color:var(--ac);color:var(--at);}
.ob-pill-em{font-size:1.2rem;line-height:1;flex-shrink:0;}
.ob-pill-tx{display:flex;flex-direction:column;gap:2px;}.ob-pill-su{font-size:.7rem;color:var(--mu);font-weight:400;}
.ob-gen{display:flex;gap:8px;margin-bottom:24px;}
.ob-gb{flex:1;padding:12px;background:var(--s2);border:1px solid var(--bd);border-radius:12px;color:var(--mu);font-size:.88rem;font-weight:500;transition:all .18s;}
.ob-gb.on{background:rgba(200,241,53,.08);border-color:var(--ac);color:var(--at);}
.ob-sum{background:var(--s1);border:1px solid var(--bd);border-radius:20px;padding:24px;margin-bottom:24px;box-shadow:var(--sh);}
.ob-sum-c{font-family:'Syne',sans-serif;font-weight:800;font-size:2.8rem;letter-spacing:-2px;line-height:1;margin-bottom:4px;}
.ob-sum-l{font-size:.75rem;color:var(--mu);text-transform:uppercase;letter-spacing:1.5px;margin-bottom:20px;}
.ob-mps{display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;}
.ob-mp{background:var(--s2);border-radius:12px;padding:10px 12px;}
.ob-mp-l{font-size:.62rem;text-transform:uppercase;letter-spacing:1px;color:var(--mu);display:flex;align-items:center;gap:4px;margin-bottom:4px;}
.ob-mp-v{font-family:'Syne',sans-serif;font-weight:700;font-size:1.1rem;color:var(--tx);}
.ob-mp-v span{font-size:.65rem;color:var(--mu);font-weight:400;font-family:'DM Sans',sans-serif;}
.ob-ft{margin-top:auto;padding-top:24px;display:flex;gap:10px;}
.ob-fl-l{font-size:.72rem;color:var(--mu);margin-bottom:6px;text-transform:uppercase;letter-spacing:1px;}
/* main app layout */
.app-w{max-width:430px;margin:0 auto;min-height:100dvh;display:flex;flex-direction:column;padding-bottom:72px;opacity:0;animation:appear .4s ease forwards;}
@keyframes appear{to{opacity:1}}
.hdr{display:flex;align-items:center;justify-content:space-between;padding:18px 28px;gap:8px;}
.hdr-r{display:flex;align-items:center;gap:8px;}
.goal-chip{display:flex;align-items:center;gap:6px;background:var(--s1);border:1px solid var(--bd);border-radius:100px;padding:6px 12px;font-size:.75rem;color:var(--mu);transition:all .2s;box-shadow:var(--sh);}
.goal-chip:hover{border-color:var(--ac);color:var(--at);}
.goal-dot{width:6px;height:6px;border-radius:50%;background:var(--ac);}
.av-btn{width:34px;height:34px;border-radius:50%;background:var(--ac);color:#000;border:none;font-family:'Syne',sans-serif;font-weight:700;font-size:.72rem;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:transform .2s;}
.av-btn:hover{transform:scale(1.08);}.av-btn img{width:100%;height:100%;object-fit:cover;}
/* bottom nav */
.bnav{position:fixed;bottom:0;left:0;right:0;background:var(--s1);border-top:1px solid var(--bd);display:flex;z-index:40;max-width:430px;margin:0 auto;}
.bnav-btn{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:10px 0 14px;gap:3px;font-size:.65rem;color:var(--mu);background:none;border:none;transition:color .18s;}
.bnav-btn.active{color:var(--at);}.bnav-btn svg{transition:transform .18s;}
.bnav-btn.active svg{transform:scale(1.15);}
/* calorie tracker */
.ring-w{display:flex;flex-direction:column;align-items:center;padding:12px 28px 22px;}
.ring-c{position:relative;width:200px;height:200px;}
.ring-svg{transform:rotate(-90deg);overflow:visible;}.ring-tr{stroke:var(--s2);}.ring-fl{stroke-linecap:round;transition:stroke-dashoffset .7s cubic-bezier(.4,0,.2,1),stroke .4s ease;}
.ring-in{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;}
.ring-n{font-family:'Syne',sans-serif;font-weight:800;font-size:2.4rem;line-height:1;letter-spacing:-1px;}
.ring-s{font-size:.68rem;color:var(--mu);text-transform:uppercase;letter-spacing:1.5px;font-weight:500;}
.ring-rm{margin-top:14px;font-size:.85rem;color:var(--mu);text-align:center;}.ring-rm b{color:var(--tx);font-weight:600;}
.macros{padding:0 28px;display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;margin-bottom:22px;}
.macro{background:var(--s1);border:1px solid var(--bd);border-radius:14px;padding:11px 11px 13px;display:flex;flex-direction:column;gap:7px;box-shadow:var(--sh);}
.macro-l{font-size:.64rem;text-transform:uppercase;letter-spacing:1.2px;color:var(--m2);font-weight:500;display:flex;align-items:center;gap:5px;}
.macro-n{font-family:'Syne',sans-serif;font-weight:700;font-size:1.1rem;line-height:1;}
.macro-n small{font-size:.68rem;font-weight:400;color:var(--mu);margin-left:2px;}
.macro-b{height:4px;background:var(--s3);border-radius:2px;overflow:hidden;}
.macro-bf{height:100%;border-radius:2px;transition:width .6s cubic-bezier(.4,0,.2,1);}
.macro-t{font-size:.66rem;color:var(--mu);}
.cta-w{padding:0 28px;display:flex;flex-direction:column;gap:9px;}
.qa-w{padding:18px 0 0 28px;}
.qa-h{display:flex;align-items:center;justify-content:space-between;padding-right:28px;margin-bottom:9px;}
.qa-l{font-size:.67rem;text-transform:uppercase;letter-spacing:1.5px;color:var(--mu);font-weight:500;display:flex;align-items:center;gap:5px;}
.qa-m{background:none;border:none;color:var(--mu);font-size:.72rem;padding:4px 8px;border-radius:8px;transition:all .2s;}
.qa-m:hover{color:var(--at);background:var(--s2);}
.qa-s{display:flex;gap:7px;overflow-x:auto;padding-right:28px;padding-bottom:2px;scrollbar-width:none;-ms-overflow-style:none;}
.qa-s::-webkit-scrollbar{display:none;}
.fav-chip{flex-shrink:0;background:var(--s1);border:1px solid var(--bd);border-radius:13px;padding:9px 13px;color:var(--tx);font-size:.83rem;display:flex;align-items:center;gap:9px;transition:all .18s;min-height:46px;max-width:210px;box-shadow:var(--sh);}
.fav-chip:hover{border-color:var(--ac);transform:translateY(-1px);}
.fav-cal{font-family:'Syne',sans-serif;font-weight:700;font-size:.74rem;color:var(--at);flex-shrink:0;}
.fav-nm{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.empty{text-align:center;padding:44px 28px 20px;color:var(--mu);line-height:1.8;}
.empty-ic{font-size:2rem;display:block;margin-bottom:8px;}.empty small{font-size:.8rem;opacity:.7;}
.cat-s{padding:0 28px;margin-top:26px;}
.cat-s:first-of-type{margin-top:30px;}
.cat-hd{display:flex;align-items:center;justify-content:space-between;padding:0 4px 11px;border-bottom:1px solid var(--bd);margin-bottom:11px;}
.cat-ti{display:flex;align-items:center;gap:9px;}
.cat-nm{font-family:'Syne',sans-serif;font-weight:700;font-size:.88rem;text-transform:uppercase;letter-spacing:1px;}
.cat-cnt{font-size:.68rem;color:var(--mu);background:var(--s2);border-radius:9px;padding:2px 7px;}
.cat-tot{font-family:'Syne',sans-serif;font-weight:700;font-size:.92rem;}
.cat-tot small{font-size:.62rem;color:var(--mu);font-weight:400;margin-left:2px;}
.cat-cs{display:flex;flex-direction:column;gap:9px;}
.mc{background:var(--s1);border:1px solid var(--bd);border-radius:17px;overflow:hidden;animation:slIn .3s cubic-bezier(.4,0,.2,1);box-shadow:var(--sh);}
@keyframes slIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
.mc-hd{display:flex;align-items:center;justify-content:space-between;padding:13px 15px 9px;border-bottom:1px solid var(--bd);}
.mc-tw{display:flex;align-items:center;gap:7px;}.mc-t{font-size:.71rem;color:var(--mu);}
.mc-si{width:17px;height:17px;border-radius:50%;background:var(--s2);display:flex;align-items:center;justify-content:center;color:var(--mu);}
.mc-r{display:flex;align-items:center;gap:7px;}
.mc-tot{font-family:'Syne',sans-serif;font-weight:700;font-size:1rem;}
.mc-tot sup{font-size:.58rem;font-weight:400;color:var(--mu);margin-left:2px;vertical-align:super;}
.mc-btn{width:27px;height:27px;background:none;border:1px solid var(--bd);border-radius:8px;color:var(--mu);font-size:.95rem;display:flex;align-items:center;justify-content:center;transition:all .18s;}
.mc-btn:hover{border-color:var(--ac);color:var(--at);}.mc-btn.del:hover{border-color:var(--re);color:var(--re);}.mc-btn.saved{border-color:var(--ac);color:var(--at);background:rgba(200,241,53,.07);}
.mc-its{padding:11px 15px 7px;display:flex;flex-direction:column;gap:9px;}
.mc-it{display:flex;align-items:flex-start;justify-content:space-between;gap:9px;}
.mc-il{display:flex;align-items:flex-start;gap:9px;font-size:.83rem;flex:1;min-width:0;}
.mc-in{display:flex;flex-direction:column;gap:2px;min-width:0;}
.mc-im{font-size:.68rem;color:var(--mu);display:flex;gap:7px;flex-wrap:wrap;}.mc-im span{white-space:nowrap;}
.mc-ic{font-size:.8rem;color:var(--tx);font-weight:500;flex-shrink:0;padding-top:2px;}
.mc-ft{display:flex;padding:9px 15px 13px;gap:12px;border-top:1px dashed var(--bd);margin-top:3px;}
.mc-fs{display:flex;align-items:center;gap:5px;font-size:.72rem;color:var(--mu);}.mc-fs b{color:var(--tx);font-weight:600;}
.wk-s{padding:0 28px;margin-top:32px;}
.wk-c{background:var(--s1);border:1px solid var(--bd);border-radius:19px;padding:18px 17px 15px;box-shadow:var(--sh);}
.wk-h{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:16px;padding:0 3px;}
.wk-t{font-family:'Syne',sans-serif;font-weight:700;font-size:.88rem;text-transform:uppercase;letter-spacing:1.2px;}
.wk-st{display:flex;flex-direction:column;align-items:flex-end;gap:2px;font-size:.68rem;color:var(--mu);}.wk-st b{color:var(--tx);font-weight:600;}
.ch-w{position:relative;height:110px;padding:8px 0 0;}
.gl{position:absolute;left:3px;right:3px;height:1px;border-top:1px dashed var(--bd);z-index:1;pointer-events:none;}
.gl::after{content:attr(data-label);position:absolute;right:0;top:-15px;font-size:.58rem;color:var(--mu);background:var(--s1);padding:0 3px;}
.bars{display:flex;align-items:flex-end;height:100%;gap:5px;position:relative;z-index:2;}
.dc{flex:1;min-width:0;height:100%;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;background:none;border:none;padding:0;cursor:pointer;transition:transform .15s;}
.dc:hover{transform:translateY(-2px);}
.bt{width:100%;flex:1;display:flex;align-items:flex-end;position:relative;}
.bar{width:100%;border-radius:5px 5px 2px 2px;background:var(--s3);transition:height .7s cubic-bezier(.4,0,.2,1),background .3s;min-height:4px;position:relative;}
.bar.em{background:var(--s2);height:6px!important;}.bar.td{box-shadow:0 0 0 2px var(--bg),0 0 0 3px currentColor;}
.bv{position:absolute;bottom:calc(100% + 3px);left:50%;transform:translateX(-50%);font-size:.58rem;color:var(--tx);font-weight:600;white-space:nowrap;opacity:0;transition:opacity .3s;}
.dc:hover .bv,.dc.tdc .bv{opacity:1;}
.dl-r{display:flex;gap:5px;margin-top:7px;}
.dl{flex:1;text-align:center;font-size:.68rem;color:var(--mu);font-weight:500;}.dl.td{color:var(--at);font-weight:700;}
.wk-em{text-align:center;padding:22px 10px;color:var(--mu);font-size:.83rem;line-height:1.6;}

/* community / discover */
.comm-w{max-width:430px;margin:0 auto;display:flex;flex-direction:column;padding:0 0 20px;}
.comm-hdr{padding:18px 24px 14px;display:flex;align-items:center;justify-content:space-between;}
.comm-ti{font-family:'Syne',sans-serif;font-weight:800;font-size:1.3rem;letter-spacing:-.5px;}
.search-bar{margin:0 24px 16px;position:relative;}
.search-input{width:100%;background:var(--s1);border:1px solid var(--bd);border-radius:14px;padding:12px 16px 12px 40px;font-size:.9rem;color:var(--tx);outline:none;transition:border-color .2s;box-shadow:var(--sh);}
.search-input:focus{border-color:var(--ac);}.search-input::placeholder{color:var(--mu);}
.search-icon{position:absolute;left:13px;top:50%;transform:translateY(-50%);color:var(--mu);pointer-events:none;}
.section-head{display:flex;align-items:center;justify-content:space-between;padding:0 24px;margin-bottom:10px;}
.section-ti{font-family:'Syne',sans-serif;font-weight:700;font-size:.82rem;text-transform:uppercase;letter-spacing:1.2px;color:var(--mu);}
.label-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px;padding:0 24px;margin-bottom:20px;}
.label-card{background:var(--s1);border:1px solid var(--bd);border-radius:14px;padding:14px;transition:all .18s;box-shadow:var(--sh);cursor:pointer;text-align:left;}
.label-card:hover{border-color:var(--ac);transform:translateY(-1px);}
.label-card-name{font-family:'Syne',sans-serif;font-weight:700;font-size:.95rem;color:var(--tx);margin-bottom:4px;}
.label-card-meta{font-size:.72rem;color:var(--mu);}
.label-card-type{display:inline-block;font-size:.62rem;text-transform:uppercase;letter-spacing:1px;color:var(--at);background:rgba(200,241,53,.1);padding:2px 7px;border-radius:6px;margin-bottom:6px;}
.user-list{display:flex;flex-direction:column;gap:8px;padding:0 24px;}
.user-row{display:flex;align-items:center;gap:12px;padding:12px 14px;background:var(--s1);border:1px solid var(--bd);border-radius:14px;cursor:pointer;transition:border-color .18s;box-shadow:var(--sh);}
.user-row:hover{border-color:var(--ac);}
.user-av{width:40px;height:40px;border-radius:50%;background:var(--ac);color:#000;font-family:'Syne',sans-serif;font-weight:700;font-size:.85rem;display:flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden;}
.user-av img{width:100%;height:100%;object-fit:cover;}
.user-info{flex:1;min-width:0;}
.user-name{font-weight:600;font-size:.9rem;color:var(--tx);}
.user-bio{font-size:.75rem;color:var(--mu);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:1px;}
.user-fc{font-size:.72rem;color:var(--mu);flex-shrink:0;}
.breadcrumb{display:flex;align-items:center;gap:4px;padding:0 24px;margin-bottom:14px;flex-wrap:wrap;}
.bc-item{font-size:.78rem;color:var(--mu);background:none;border:none;padding:4px 6px;border-radius:8px;cursor:pointer;transition:color .18s;}
.bc-item:hover{color:var(--at);}.bc-item.active{color:var(--tx);font-weight:600;}
.bc-sep{color:var(--mu);font-size:.78rem;}
.create-label-btn{display:flex;align-items:center;gap:10px;width:100%;padding:12px 16px;background:rgba(200,241,53,.06);border:1px dashed rgba(200,241,53,.3);border-radius:14px;color:var(--at);font-size:.88rem;font-weight:500;text-align:left;margin-top:4px;}
.create-label-btn:hover{background:rgba(200,241,53,.1);}

/* profile screen */
.prof-scr{max-width:430px;margin:0 auto;display:flex;flex-direction:column;padding:0 0 20px;}
.prof-hdr{padding:18px 24px 0;display:flex;align-items:center;gap:12px;}
.prof-hdr-title{font-family:'Syne',sans-serif;font-weight:800;font-size:1.2rem;flex:1;}
.prof-cover{background:linear-gradient(135deg,rgba(200,241,53,.12),rgba(200,241,53,.04));padding:28px 24px 20px;display:flex;flex-direction:column;align-items:center;text-align:center;border-bottom:1px solid var(--bd);}
.prof-av{width:72px;height:72px;border-radius:50%;background:var(--ac);color:#000;font-family:'Syne',sans-serif;font-weight:800;font-size:1.4rem;display:flex;align-items:center;justify-content:center;margin-bottom:12px;overflow:hidden;border:3px solid var(--bg);}
.prof-av img{width:100%;height:100%;object-fit:cover;}
.prof-name{font-family:'Syne',sans-serif;font-weight:800;font-size:1.3rem;letter-spacing:-.3px;margin-bottom:4px;}
.prof-bio{font-size:.83rem;color:var(--mu);line-height:1.5;max-width:280px;margin-bottom:14px;}
.prof-stats{display:flex;gap:24px;margin-bottom:16px;}
.prof-stat{display:flex;flex-direction:column;align-items:center;gap:2px;}
.prof-stat-n{font-family:'Syne',sans-serif;font-weight:800;font-size:1.2rem;}
.prof-stat-l{font-size:.7rem;color:var(--mu);text-transform:uppercase;letter-spacing:.8px;}
.prof-actions{display:flex;gap:8px;}
.prof-edit-btn{height:36px;padding:0 16px;background:var(--s2);border:1px solid var(--bd);border-radius:100px;color:var(--tx);font-size:.8rem;font-weight:500;transition:all .18s;}
.prof-edit-btn:hover{border-color:var(--m2);}
.prof-so-btn{height:36px;padding:0 16px;background:none;border:1px solid var(--bd);border-radius:100px;color:var(--re);font-size:.8rem;font-weight:500;transition:all .18s;}
.prof-so-btn:hover{border-color:var(--re);background:rgba(255,77,77,.06);}
.prof-section{padding:18px 24px 0;}
.prof-section-ti{font-family:'Syne',sans-serif;font-weight:700;font-size:.78rem;text-transform:uppercase;letter-spacing:1.2px;color:var(--mu);margin-bottom:12px;}
.label-chips{display:flex;flex-wrap:wrap;gap:8px;}
.label-chip{display:flex;align-items:center;gap:6px;background:var(--s2);border:1px solid var(--bd);border-radius:100px;padding:6px 12px;font-size:.8rem;color:var(--tx);cursor:pointer;transition:all .18s;}
.label-chip:hover{border-color:var(--ac);color:var(--at);}
.label-chip-x{color:var(--mu);font-size:.85rem;margin-left:2px;transition:color .18s;}
.label-chip:hover .label-chip-x{color:var(--re);}
.add-label-btn{display:flex;align-items:center;gap:6px;background:none;border:1px dashed var(--bd);border-radius:100px;padding:6px 12px;font-size:.8rem;color:var(--mu);transition:all .18s;}
.add-label-btn:hover{border-color:var(--ac);color:var(--at);}
.fav-l{display:flex;flex-direction:column;gap:7px;margin-bottom:7px;}
.fav-r{display:flex;align-items:center;gap:9px;padding:11px 13px;background:var(--s2);border-radius:13px;}
.fav-ri{flex:1;min-width:0;background:none;border:none;padding:0;text-align:left;cursor:pointer;}
.fav-rn{font-size:.88rem;color:var(--tx);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.fav-rm{font-size:.68rem;color:var(--mu);display:flex;gap:7px;margin-top:2px;}.fav-rm b{color:var(--at);font-weight:700;}
.fav-rd{width:34px;height:34px;background:none;border:1px solid var(--bd);border-radius:9px;color:var(--mu);display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:all .18s;}
.fav-rd:hover{border-color:var(--re);color:var(--re);}
/* label picker modal */
.lp-search{width:100%;background:var(--s2);border:1px solid var(--bd);border-radius:12px;padding:11px 14px;font-size:.9rem;color:var(--tx);outline:none;margin-bottom:14px;transition:border-color .2s;}
.lp-search:focus{border-color:var(--ac);}.lp-search::placeholder{color:var(--mu);}
.lp-items{display:flex;flex-direction:column;gap:6px;max-height:260px;overflow-y:auto;}
.lp-item{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;background:var(--s2);border-radius:12px;cursor:pointer;transition:all .18s;}
.lp-item:hover{background:var(--s3);}.lp-item-l{display:flex;flex-direction:column;gap:2px;}
.lp-item-name{font-size:.88rem;font-weight:500;color:var(--tx);}
.lp-item-meta{font-size:.7rem;color:var(--mu);}
.lp-arrow{color:var(--mu);font-size:1rem;}.lp-joined{color:var(--at);font-size:.72rem;font-weight:600;}
.lp-create{display:flex;align-items:center;gap:10px;padding:10px 12px;background:rgba(200,241,53,.06);border:1px dashed rgba(200,241,53,.25);border-radius:12px;cursor:pointer;color:var(--at);font-size:.88rem;font-weight:500;margin-top:6px;}
.lp-create:hover{background:rgba(200,241,53,.1);}
.lp-back{background:none;border:none;color:var(--mu);font-size:.82rem;display:flex;align-items:center;gap:5px;padding:0;margin-bottom:14px;transition:color .18s;}
.lp-back:hover{color:var(--tx);}
.lp-type-row{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:12px;}
.lp-type{padding:5px 10px;background:var(--s2);border:1px solid var(--bd);border-radius:8px;font-size:.74rem;color:var(--mu);transition:all .18s;}
.lp-type:hover{border-color:var(--m2);color:var(--tx);}.lp-type.on{background:rgba(200,241,53,.08);border-color:var(--ac);color:var(--at);}

@media(max-width:480px){
  .g-hl{font-size:1.9rem;}.ob-ti{font-size:1.6rem;}.ob-sum-c{font-size:2.3rem;}
  .sheet{padding:10px 20px 32px;max-height:92dvh;}
  .hdr{padding:14px 20px;}.ring-c{width:180px;height:180px;}.ring-n{font-size:2rem;}
  .macros{padding:0 20px;gap:6px;margin-bottom:16px;}.cta-w{padding:0 20px;gap:8px;}
  .btn-lime{height:56px;font-size:.9rem;}.btn-out{height:50px;font-size:.88rem;}
  .qa-w{padding-left:20px;}.qa-h,.qa-s{padding-right:20px;}
  .cat-s{padding:0 20px;margin-top:20px;}.wk-s{padding:0 20px;margin-top:26px;}.ch-w{height:90px;}
  .cat-chips{gap:5px;}.cat-chip{font-size:.7rem;padding:8px 4px;min-height:50px;}
  .label-grid{grid-template-columns:1fr;padding:0 20px;}.search-bar{margin:0 20px 14px;}
  .section-head,.user-list,.comm-hdr,.prof-section,.breadcrumb{padding-left:20px;padding-right:20px;}
  .toast-w{padding-bottom:76px;}
}
@media(min-width:901px){
  .app-w,.comm-w,.prof-scr,.ob-w{max-width:400px;padding-top:20px;}
  .hdr,.macros,.cta-w,.cat-s,.wk-s{padding-left:32px;padding-right:32px;}
  .qa-w{padding-left:32px;}.qa-h,.qa-s{padding-right:32px;}
  .overlay{justify-content:center;}.sheet{max-width:480px;border-radius:26px;padding:22px 32px 32px;}
  .bnav{max-width:400px;}
}
`;

// ─── SHARED COMPONENTS ────────────────────────────────────────────────────────
function ThemeBtn({theme,setTheme}){
  return(
    <button className="thm-btn" onClick={()=>setTheme(t=>t==="dark"?"light":"dark")}>
      {theme==="dark"
        ?<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>
        :<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
      }
    </button>
  );
}

function Avatar({user,size=40}){
  const initials=user?.name?.split(" ").map(n=>n[0]).slice(0,2).join("").toUpperCase()||"?";
  return(
    <div className="user-av" style={{width:size,height:size,fontSize:size*0.32}}>
      {user?.picture?<img src={user.picture} alt={user.name}/>:initials}
    </div>
  );
}

function ManualModal({theme,onClose,onSubmit}){
  const [val,setVal]=useState("");
  return(
    <div className="sc overlay center" data-theme={theme} onClick={e=>e.target===e.currentTarget&&onClose()}>
      <div className="gbox">
        <div className="g-ti">Ate something and forgot to snap it?</div>
        <div className="g-su">No judgment. Just tell us what it was.</div>
        <textarea className="m-ta" placeholder="e.g., grilled chicken with rice and salad, large portion" value={val} onChange={e=>setVal(e.target.value)} onKeyDown={e=>{if(e.key==="Enter"&&(e.metaKey||e.ctrlKey)&&val.trim()){onClose();onSubmit(val.trim());}}} autoFocus/>
        <div className="m-ac">
          <button className="btn-dim" onClick={onClose}>Cancel</button>
          <button className="btn-act" onClick={()=>{if(val.trim()){onClose();onSubmit(val.trim());}}} disabled={!val.trim()}>Analyse</button>
        </div>
      </div>
    </div>
  );
}

// ─── LABEL PICKER ─────────────────────────────────────────────────────────────
function LabelPicker({theme,userLabels,onJoin,onLeave,onClose}){
  const [parentChain,setParentChain]=useState([]);
  const [search,setSearch]=useState("");
  const [items,setItems]=useState([]);
  const [loading,setLoading]=useState(false);
  const [creating,setCreating]=useState(false);
  const [newName,setNewName]=useState("");
  const [newType,setNewType]=useState("other");

  const currentParent=parentChain[parentChain.length-1]||null;

  const load=useCallback(async()=>{
    setLoading(true);
    try{
      const qs=search?`?search=${encodeURIComponent(search)}`:currentParent?`?parent_id=${currentParent.id}`:"";
      const data=await api(`/api/labels${qs}`);
      setItems(data);
    }catch{setItems([]);}
    finally{setLoading(false);}
  },[currentParent,search]);

  useEffect(()=>{const t=setTimeout(load,300);return()=>clearTimeout(t);},[load]);

  const isJoined=id=>userLabels.some(l=>l.id===id);

  const drillDown=item=>{setParentChain(c=>[...c,item]);setSearch("");};
  const goBack=()=>{setParentChain(c=>c.slice(0,-1));setSearch("");};
  const goRoot=()=>{setParentChain([]);setSearch("");};

  const handleCreate=async()=>{
    if(!newName.trim())return;
    try{
      const{label}=await api("/api/labels",{method:"POST",body:JSON.stringify({name:newName.trim(),parent_id:currentParent?.id||null,type:newType})});
      await onJoin(label);
      setCreating(false);setNewName("");load();
    }catch{}
  };

  return(
    <div className="sc overlay" data-theme={theme} onClick={e=>e.target===e.currentTarget&&onClose()}>
      <div className="sheet">
        <div className="handle"/>
        <div className="sh-ti">Add Labels</div>
        <div className="sh-su" style={{marginBottom:14}}>Browse or search — each label is independent</div>

        {parentChain.length>0&&(
          <button className="lp-back" onClick={goBack}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="15 18 9 12 15 6"/></svg>
            Back to {parentChain.length>1?parentChain[parentChain.length-2].name:"All labels"}
          </button>
        )}

        {parentChain.length>0&&(
          <div className="breadcrumb" style={{padding:0,marginBottom:12}}>
            <button className="bc-item" onClick={goRoot}>All</button>
            {parentChain.map((p,i)=>(
              <span key={p.id} style={{display:"flex",alignItems:"center",gap:4}}>
                <span className="bc-sep">›</span>
                <button className="bc-item" onClick={()=>setParentChain(c=>c.slice(0,i+1))}>{p.name}</button>
              </span>
            ))}
          </div>
        )}

        <input className="lp-search" placeholder={`Search ${currentParent?`in ${currentParent.name}`:"all labels"}…`} value={search} onChange={e=>setSearch(e.target.value)}/>

        {creating?(
          <>
            <div className="g-su">New label under {currentParent?.name||"root"}</div>
            <input className="lp-search" placeholder="Label name" value={newName} onChange={e=>setNewName(e.target.value)} autoFocus/>
            <div className="lp-type-row">
              {LTYPES.map(t=><button key={t} className={`lp-type ${newType===t?"on":""}`} onClick={()=>setNewType(t)}>{t}</button>)}
            </div>
            <div className="r-acts">
              <button className="btn-dim" onClick={()=>setCreating(false)}>Cancel</button>
              <button className="btn-act" onClick={handleCreate} disabled={!newName.trim()}>Create &amp; Join</button>
            </div>
          </>
        ):(
          <div className="lp-items">
            {loading&&<div style={{textAlign:"center",padding:20,color:"var(--mu)"}}>Loading…</div>}
            {!loading&&items.map(item=>(
              <div key={item.id}>
                <div className="lp-item">
                  <div className="lp-item-l" onClick={()=>drillDown(item)} style={{flex:1}}>
                    <div className="lp-item-name">{item.name}</div>
                    <div className="lp-item-meta">{item.member_count} members · {item.type}</div>
                  </div>
                  <div style={{display:"flex",alignItems:"center",gap:8}}>
                    {isJoined(item.id)
                      ?<><span className="lp-joined">✓ Joined</span><button style={{fontSize:".8rem",color:"var(--mu)",background:"none",border:"none"}} onClick={()=>onLeave(item)}>Leave</button></>
                      :<button style={{fontSize:".8rem",color:"var(--at)",background:"rgba(200,241,53,.08)",border:"1px solid var(--ac)",borderRadius:8,padding:"4px 10px",fontWeight:600}} onClick={()=>onJoin(item)}>Join</button>
                    }
                    <span className="lp-arrow" onClick={()=>drillDown(item)}>›</span>
                  </div>
                </div>
              </div>
            ))}
            {!loading&&!items.length&&<div style={{textAlign:"center",padding:20,color:"var(--mu)",fontSize:".88rem"}}>No labels found</div>}
            {!loading&&(
              <div className="lp-create" onClick={()=>setCreating(true)}>
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
                Create "{search||"new label"}" {currentParent?`under ${currentParent.name}`:"at top level"}
              </div>
            )}
          </div>
        )}
      </div>
    </div>
  );
}

// ─── ROOT ─────────────────────────────────────────────────────────────────────
export default function App() {
  const [auth,setAuth]=useState(()=>{try{return localStorage.getItem("kcal_user")?"app":"guest";}catch{return"guest";}});
  const [user,setUser]=useState(()=>{try{const s=localStorage.getItem("kcal_user");return s?JSON.parse(s):null;}catch{return null;}});
  const [theme,setTheme]=useState(getThm);

  useEffect(()=>{
    document.body.style.backgroundColor=theme==="dark"?"#080808":"#f2f2ee";
    try{localStorage.setItem("kcal_theme",theme);}catch{}
  },[theme]);

  const signIn=async ud=>{
    try{
      const ex=localStorage.getItem("kcal_profile_"+(ud.email||"demo"));
      if(ex){
        const fu={...ud,profile:JSON.parse(ex)};
        localStorage.setItem("kcal_user",JSON.stringify(fu));
        setUser(fu);setAuth("app");return;
      }
    }catch{}
    setUser(ud);setAuth("onboarding");
  };

  const completeOnboard=profile=>{
    const fu={...user,profile};
    localStorage.setItem("kcal_user",JSON.stringify(fu));
    try{localStorage.setItem("kcal_profile_"+(user.email||"demo"),JSON.stringify(profile));}catch{}
    setUser(fu);setAuth("app");
  };

  const signOut=()=>{
    localStorage.removeItem("kcal_user");
    localStorage.removeItem("kcal_token");
    setUser(null);setAuth("guest");
  };

  const updateUser=u=>{
    const fu={...user,...u};
    localStorage.setItem("kcal_user",JSON.stringify(fu));
    setUser(fu);
  };

  return(
    <>
      <style>{CSS}</style>
      {auth==="guest"      &&<GuestScreen theme={theme} setTheme={setTheme} onSignIn={()=>setAuth("signin")}/>}
      {auth==="signin"     &&<SignInScreen theme={theme} onBack={()=>setAuth("guest")} onSignIn={signIn}/>}
      {auth==="onboarding" &&<OnboardingScreen theme={theme} user={user} onComplete={completeOnboard}/>}
      {auth==="app"        &&<MainApp theme={theme} setTheme={setTheme} user={user} onSignOut={signOut} onUpdateUser={updateUser}/>}
    </>
  );
}

// ─── GUEST ────────────────────────────────────────────────────────────────────
function GuestScreen({theme,setTheme,onSignIn}){
  const [scan,setScan]=useState(null);const [loading,setLoading]=useState(false);
  const [img,setImg]=useState(null);const [show,setShow]=useState(false);
  const [showCam,setShowCam]=useState(false);
  const waterGoal = user?.profile?.weightKg && user?.profile?.usePersonalisedWater
    ? calcWaterGoal(parseInt(user.profile.weightKg)||70)
    : 2000;
  const [waterMl,  setWaterMl]   = useState(()=>{try{const s=localStorage.getItem("cc_water_"+todayKey());return s?parseInt(s):0;}catch{return 0;}});
  const [waterUnit,setWaterUnit] = useState(()=>{try{return localStorage.getItem("cc_water_unit")||"glasses";}catch{return"glasses";}});
  const [lastWaterTime,setLastWaterTime] = useState(Date.now());
  const waterProgress = Math.min(waterMl/waterGoal,1);
  const waterGlasses  = mlToGlasses(waterMl);
  const goalGlasses   = mlToGlasses(waterGoal);

  useEffect(()=>{try{localStorage.setItem("cc_water_"+todayKey(),waterMl.toString());}catch{};},[waterMl]);
  useEffect(()=>{try{localStorage.setItem("cc_water_unit",waterUnit);}catch{};},[waterUnit]);

  // In-app hydration reminder — toast if no water logged in 2 hours
  useEffect(()=>{
    const id=setInterval(()=>{
      const twoHours=2*60*60*1000;
      if(Date.now()-lastWaterTime>twoHours&&waterMl<waterGoal){
        showToast("💧 Time to hydrate — you haven\'t logged water in a while.");
      }
    },30*60*1000); // check every 30 minutes
    return()=>clearInterval(id);
  },[lastWaterTime,waterMl,waterGoal]);

  const addWater = () => {
    const addMl = waterUnit==="glasses" ? GLASS_ML : 250;
    const newMl = Math.min(waterMl+addMl, waterGoal*2); // cap at 2x goal
    setWaterMl(newMl);
    setLastWaterTime(Date.now());
    const token=getToken();
    logWaterToServer(newMl, waterUnit, false, token).then(r=>{
      if(!r)return;
      if(r.goalHit&&newMl-addMl<waterGoal) showToast("💧 Daily water goal hit! +15 XP");
      if(r.newBadges?.length) r.newBadges.forEach(b=>setTimeout(()=>showToast(b.emoji+" "+b.name+" badge unlocked!"),800));
      if(onRefreshGami) onRefreshGami();
    });
  };

  const removeWater = () => {
    const removeMl = waterUnit==="glasses" ? GLASS_ML : 250;
    setWaterMl(Math.max(0,waterMl-removeMl));
  };
  const dismiss=()=>{setShow(false);setScan(null);setImg(null);};

  const handleFoodCapture=async(base64,mt)=>{
    setShowCam(false);setImg("data:"+mt+";base64,"+base64);setShow(true);
    await callAI([{role:"user",content:[{type:"image",source:{type:"base64",media_type:mt,data:base64}},{type:"text",text:imgP}]}],setScan,setLoading);
  };
  const handleBarcodeResult=async code=>{
    setShowCam(false);setShow(true);setLoading(true);setScan(null);
    try{const r=await lookupBarcode(code);setScan(r);}catch{setScan({error:true});}finally{setLoading(false);}
  };
  const handleLabelCapture=async(base64,mt)=>{
    setShowCam(false);setShow(true);setLoading(true);setScan(null);
    try{
      const r=await fetch(API_URL+"/api/scan/label",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:base64,mediaType:mt})});
      const d=await r.json();setScan({...d,type:"label",items:[{name:"Nutrition label scan",calories:d.calories,emoji:"🏷️",protein:d.protein,carbs:d.carbs,fat:d.fat,fiber:d.fiber||0,sodium:d.sodium||0,sugar:d.sugar||0}],total:d.calories,fiber:d.fiber||0,sodium:d.sodium||0,sugar:d.sugar||0});
    }catch{setScan({error:true});}finally{setLoading(false);}
  };
  return(
    <div className="sc" data-theme={theme} style={{display:"flex",flexDirection:"column"}}>
      <div className="g-hdr"><div className="logo">CalCheckAI</div><ThemeBtn theme={theme} setTheme={setTheme}/></div>
      <div className="g-hero">
        <div className="g-eye">AI Calorie Scanner</div>
        <h1 className="g-hl">Snap a meal.<br/><span>Know</span> the calories.</h1>
        <p className="g-sub">Point your camera at any food.<br/>No signup needed to get started.</p>
        <div className="g-ctas">
          <button className="btn-lime" onClick={()=>setShowCam(true)}><svg width="21" height="21" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>Snap your meal</button>
        </div>
        <p className="g-si">Already have an account? <button onClick={onSignIn}>Sign in</button></p>
      </div>
{showCam&&<SmartScanner theme={theme} onFoodCapture={handleFoodCapture} onBarcodeResult={handleBarcodeResult} onLabelCapture={handleLabelCapture} onClose={msg=>{setShowCam(false);if(msg)alert(msg);}}/>}
      {show&&<ScanResultSheet scan={scan} loading={loading} img={img} isGuest={true} onDismiss={dismiss} onAdd={dismiss} onSave={()=>{}} saved={false} selCat="" setSelCat={()=>{}} onSignIn={onSignIn} theme={theme}/>}
      
    </div>
  );
}

// ─── SIGN IN ──────────────────────────────────────────────────────────────────
function SignInScreen({theme,onBack,onSignIn}){
  const handleGoogle=async credential=>{
    try{
      if(API_URL!=="YOUR_WORKER_URL_HERE"){
        const thm=localStorage.getItem("kcal_theme")||"dark";const{token,user}=await api("/api/auth",{method:"POST",body:JSON.stringify({credential,theme:thm})});
        localStorage.setItem("kcal_token",token);
        onSignIn({...user,apiToken:token});
      }else{
        const p=JSON.parse(atob(credential.split(".")[1]));
        onSignIn({name:p.name,email:p.email,picture:p.picture,givenName:p.given_name});
      }
    }catch(e){alert("Sign-in failed: "+e.message);}
  };
  useEffect(()=>{
    if(GOOGLE_CLIENT_ID==="YOUR_CLIENT_ID_HERE")return;
    const s=document.createElement("script");s.src="https://accounts.google.com/gsi/client";
    s.onload=()=>{window.google?.accounts.id.initialize({client_id:GOOGLE_CLIENT_ID,callback:r=>handleGoogle(r.credential)});window.google?.accounts.id.renderButton(document.getElementById("g-btn"),{theme:"filled_black",size:"large",width:"300",shape:"pill",text:"continue_with"});};
    document.head.appendChild(s);
  },[]);
  return(
    <div className="sc" data-theme={theme} style={{display:"flex",flexDirection:"column"}}>
      <button className="si-bk" onClick={onBack}><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="15 18 9 12 15 6"/></svg>Back</button>
      <div className="si-body">
        <div className="si-logo">CalCheckAI</div>
        <p className="si-tag">Track your meals.<br/>Connect with your community.</p>
        <div className="si-btns">
          {GOOGLE_CLIENT_ID!=="YOUR_CLIENT_ID_HERE"
            ?<div className="g-btn-w"><div id="g-btn"/></div>
            :<button className="g-fb" onClick={()=>onSignIn({name:"Alex Demo",email:"demo@calcheckai.com",picture:null,givenName:"Alex",isDemo:true})}><svg width="18" height="18" viewBox="0 0 24 24"><path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/><path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/><path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/><path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/></svg>Continue with Google</button>
          }
          <div className="si-div"><div className="si-dl"/><span>or</span><div className="si-dl"/></div>
          <button className="demo-btn" onClick={()=>onSignIn({name:"Alex Demo",email:"demo@calcheckai.com",picture:null,givenName:"Alex",isDemo:true})}>Try Demo Mode</button>
        </div>
        <p className="si-note">{GOOGLE_CLIENT_ID==="YOUR_CLIENT_ID_HERE"?"Add your Google Client ID + Worker URL to enable full auth.":"Your data stays in your Google Drive. CalCheckAI.com"}</p>
      </div>
    </div>
  );
}

// ─── ONBOARDING ───────────────────────────────────────────────────────────────
function OnboardingScreen({theme,user,onComplete}){
  const [step,setStep]=useState(0);
  const [d,setD]=useState({name:user?.givenName||"",age:"25",gender:"male",heightCm:"170",weightKg:"70",activityLevel:"moderate",goal:"maintain"});
  const [handleInput,setHandleInput]=useState("");
  const [handleStatus,setHandleStatus]=useState(null); // null | 'checking' | 'available' | 'taken' | 'error'
  const [handleMsg,setHandleMsg]=useState("");

  const checkHandle=useCallback(async val=>{
    const h=val.toLowerCase().trim();
    if(!h){setHandleStatus(null);setHandleMsg("");return;}
    if(h.length<5){setHandleStatus("error");setHandleMsg("At least 5 characters");return;}
    if(h.length>19){setHandleStatus("error");setHandleMsg("Max 19 characters");return;}
    if(!/^[a-z0-9_.]+$/.test(h)){setHandleStatus("error");setHandleMsg("Only letters, numbers, _ and . allowed");return;}
    if(/^[._]/.test(h)||/[._]$/.test(h)){setHandleStatus("error");setHandleMsg("Cannot start or end with . or _");return;}
    if(/[._]{2}|[._][._]/.test(h)){setHandleStatus("error");setHandleMsg("No consecutive separators");return;}
    setHandleStatus("checking");
    try{
      if(API_URL!=="YOUR_WORKER_URL_HERE"){
        const r=await api("/api/handles/check?handle="+encodeURIComponent(h));
        setHandleStatus(r.available?"available":"taken");
        setHandleMsg(r.available?"✓ Available":"✗ Already taken");
      }else{
        setHandleStatus("available");setHandleMsg("✓ Available (connect backend to verify)");
      }
    }catch{setHandleStatus("error");setHandleMsg("Could not check — try again");}
  },[]);

  useEffect(()=>{if(!handleInput)return;const t=setTimeout(()=>checkHandle(handleInput),400);return()=>clearTimeout(t);},[handleInput,checkHandle]);
  const set=(k,v)=>setD(p=>({...p,[k]:v}));
  const tgts=step===6?calcT({...d,age:parseInt(d.age)||25,heightCm:parseInt(d.heightCm)||170,weightKg:parseInt(d.weightKg)||70}):null;
  const done=()=>{const p={...d,age:parseInt(d.age)||25,heightCm:parseInt(d.heightCm)||170,weightKg:parseInt(d.weightKg)||70,handle:handleInput||null};p.targets=calcT(p);onComplete(p);};
  return(
    <div className="sc" data-theme={theme}>
      <div className="ob-w">
        <div className="ob-hd"><div className="logo">CalCheckAI</div><div className="ob-sc">Step {step+1} of 7</div></div>
        <div className="ob-prog"><div className="ob-pf" style={{width:`${((step+1)/7)*100}%`}}/></div>
        <div className="ob-st" key={step}>
          {step===0&&<><div className="ob-lbl">Welcome</div><div className="ob-ti">Hi there 👋<br/>What's your name?</div><div className="ob-su">We'll use this to personalise your experience</div><input className="ob-in" type="text" placeholder="Your name" value={d.name} onChange={e=>set("name",e.target.value)} autoFocus/></>}
          {step===1&&<>
            <div className="ob-lbl">Your identity</div>
            <div className="ob-ti">Choose your<br/>@handle</div>
            <div className="ob-su">This is how people find and mention you — you can change it later</div>
            <div style={{position:"relative",marginBottom:8}}>
              <span style={{position:"absolute",left:16,top:"50%",transform:"translateY(-50%)",fontFamily:"'Syne',sans-serif",fontWeight:700,fontSize:"1.5rem",color:"var(--at)"}}>@</span>
              <input className="ob-in" style={{paddingLeft:40,textAlign:"left"}} type="text" placeholder="yourhandle" value={handleInput} onChange={e=>setHandleInput(e.target.value.toLowerCase().replace(/[^a-z0-9_.]/g,""))} autoFocus/>
            </div>
            <div style={{fontSize:".78rem",marginBottom:16,minHeight:20,color:handleStatus==="available"?"var(--at)":handleStatus==="taken"||handleStatus==="error"?"var(--re)":"var(--mu)",fontWeight:handleStatus==="available"||handleStatus==="taken"?600:400}}>
              {handleStatus==="checking"?"Checking…":handleMsg||"5–19 characters · letters, numbers, _ and . only"}
            </div>
            <div style={{display:"flex",flexWrap:"wrap",gap:8,marginBottom:8}}>
              {d.name&&[d.name.toLowerCase().replace(/\s+/g,"_"),d.name.toLowerCase().replace(/\s+/g,".")+".fit",d.name.toLowerCase().replace(/\s+/g,"")+"_health"].filter(s=>s.length>=5&&s.length<=19).map(s=><button key={s} style={{padding:"6px 12px",background:"var(--s2)",border:"1px solid var(--bd)",borderRadius:100,fontSize:".78rem",color:"var(--mu)",transition:"all .18s"}} onClick={()=>{setHandleInput(s);checkHandle(s);}}>@{s}</button>)}
            </div>
          </>}
          {step===2&&<><div className="ob-lbl">About you</div><div className="ob-ti">Basic info</div><div className="ob-su">Used to calculate your calorie needs accurately</div><div className="ob-gen">{["male","female","other"].map(g=><button key={g} className={`ob-gb ${d.gender===g?"on":""}`} onClick={()=>set("gender",g)}>{g.charAt(0).toUpperCase()+g.slice(1)}</button>)}</div><input className="ob-in sm" type="number" inputMode="numeric" placeholder="Age" value={d.age} onChange={e=>set("age",e.target.value)}/></>}
          {step===3&&<><div className="ob-lbl">Your body</div><div className="ob-ti">Height &amp;<br/>Weight</div><div className="ob-su">Approximate is fine</div>
            <div style={{display:"flex",gap:8,marginBottom:12,justifyContent:"center"}}>
              <div className="wt-unit-toggle" style={{borderRadius:11}}>
                {["kg","lbs"].map(u=><button key={u} className={`wt-unit-btn ${(d.weightUnit||"kg")===u?"on":""}`} style={{padding:"8px 18px"}} onClick={()=>set("weightUnit",u)}>{u}</button>)}
              </div>
            </div>
            <div className="ob-row"><div><div className="ob-fl-l">Height {(d.weightUnit||"kg")==="lbs"?"(ft/in)":"(cm)"}</div><input className="ob-in sm" type="number" inputMode="numeric" placeholder={(d.weightUnit||"kg")==="lbs"?"70":"170"} value={d.heightCm} onChange={e=>set("heightCm",e.target.value)}/></div><div><div className="ob-fl-l">Weight ({d.weightUnit||"kg"})</div><input className="ob-in sm" type="number" inputMode="numeric" placeholder={(d.weightUnit||"kg")==="lbs"?"154":"70"} value={d.weightKg} onChange={e=>set("weightKg",e.target.value)}/></div></div></>}}
          {step===4&&<><div className="ob-lbl">Lifestyle</div><div className="ob-ti">How active<br/>are you?</div><div className="ob-su" style={{marginBottom:20}}>Be honest — this directly affects your targets</div>{ACT.map(a=><button key={a.id} className={`ob-pill ${d.activityLevel===a.id?"on":""}`} onClick={()=>set("activityLevel",a.id)}><span className="ob-pill-em">{a.emoji}</span><span className="ob-pill-tx"><span>{a.label}</span><span className="ob-pill-su">{a.sub}</span></span></button>)}</>}
          {step===5&&<><div className="ob-lbl">Your goal</div><div className="ob-ti">What are you<br/>working towards?</div><div className="ob-su" style={{marginBottom:20}}>Adjusts your daily calorie target</div>{GOALS.map(g=><button key={g.id} className={`ob-pill ${d.goal===g.id?"on":""}`} onClick={()=>set("goal",g.id)}><span className="ob-pill-em">{g.emoji}</span><span className="ob-pill-tx"><span>{g.label}</span><span className="ob-pill-su">{g.sub}</span></span></button>)}</>}
          {step===6&&tgts&&<><div className="ob-lbl">Your targets</div><div className="ob-ti">Here's your plan{d.name?`, ${d.name.split(" ")[0]}`:""}.</div><div className="ob-su">Calculated using Mifflin-St Jeor</div><div className="ob-sum"><div className="ob-sum-c" style={{color:"var(--at)"}}>{tgts.calories.toLocaleString()}</div><div className="ob-sum-l">kcal per day</div><div className="ob-mps"><div className="ob-mp"><div className="ob-mp-l"><span className="mdot" style={{background:"var(--pr)"}}/>Protein</div><div className="ob-mp-v">{tgts.protein}<span>g</span></div></div><div className="ob-mp"><div className="ob-mp-l"><span className="mdot" style={{background:"var(--ca)"}}/>Carbs</div><div className="ob-mp-v">{tgts.carbs}<span>g</span></div></div><div className="ob-mp"><div className="ob-mp-l"><span className="mdot" style={{background:"var(--fa)"}}/>Fat</div><div className="ob-mp-v">{tgts.fat}<span>g</span></div></div></div></div></>}
        </div>
        <div className="ob-ft">
          {step>0&&<button className="btn-dim" onClick={()=>setStep(s=>s-1)}>Back</button>}
          {step<5?<button className="btn-act" style={{flex:1}} onClick={()=>setStep(s=>s+1)} disabled={step===0&&!d.name.trim()}>Continue →</button>:<button className="btn-act" style={{flex:1}} onClick={done}>Let's go 🚀</button>}
        </div>
      </div>
    </div>
  );
}

// ─── MAIN APP ─────────────────────────────────────────────────────────────────
function MainApp({theme,setTheme,user,onSignOut,onUpdateUser}){
  const [tab,setTab]=useState("track");
  const [refCode]=useState(()=>new URLSearchParams(window.location.search).get("ref")||null);
  const [showRefLanding,setShowRefLanding]=useState(()=>!!new URLSearchParams(window.location.search).get("ref"));
  const [showNotifs,setShowNotifs]=useState(false);
  const [unreadVisits,setUnreadVisits]=useState(0);
  const [weightUnit,setWeightUnit]=useState(()=>localStorage.getItem("kcal_weight_unit")||"kg");

  useEffect(()=>{
    if(API_URL==="YOUR_WORKER_URL_HERE"){setUnreadVisits(2);return;} // demo badge
    const fetchUnread=()=>{
      api("/api/visits").then(d=>setUnreadVisits(d.unreadCount||0)).catch(()=>{});
    };
    fetchUnread();
    const id=setInterval(fetchUnread,5*60*1000); // poll every 5 minutes
    return()=>clearInterval(id);
  },[]);
  const [gami,setGami]=useState(null);
  const [unreadMsgs,setUnreadMsgs]=useState(3); // demo: 3 unread
  const [activeConv,setActiveConv]=useState(null);
  const [showConvList,setShowConvList]=useState(false);

  const openConversation=conv=>{setActiveConv(conv);setShowConvList(false);};

  useEffect(()=>{
    const token=getToken();
    if(!token||API_URL==="YOUR_WORKER_URL_HERE")return;
    api("/api/gamification/me").then(setGami).catch(()=>{});
  },[]);

  const refreshGami=()=>{
    const token=getToken();
    if(!token||API_URL==="YOUR_WORKER_URL_HERE")return;
    api("/api/gamification/me").then(setGami).catch(()=>{});
  };
  const [toast,setToast]=useState(null);
  const showToast=useCallback((text,type="ok")=>{setToast({text,type});setTimeout(()=>setToast(null),2200);},[]);
  const [viewUser,setViewUser]=useState(null);

  return(
    <>
            {/* Envelope icon */}
      <div className="env-wrap" style={{marginRight:4}}>
        <button className="icon-btn" onClick={()=>setShowConvList(true)} title="Messages">
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
        </button>
        {unreadMsgs>0&&<span className="env-badge">{unreadMsgs>9?"9+":unreadMsgs}</span>}
      </div>
      {showNotifs&&<NotificationsSheet
        theme={theme}
        currentUserId={user?.id}
        onViewUser={uid=>{setViewUser(uid);setShowNotifs(false);}}
        onClose={()=>setShowNotifs(false)}
      />}
      {showRefLanding&&!user&&(
        <ReferralLanding
          refCode={refCode}
          theme={theme}
          onSignUp={()=>{setShowRefLanding(false);setTimeout(()=>{document.querySelector(".sign-in-btn,.g-sign-btn")?.click();},200);}}
          onSignIn={()=>setShowRefLanding(false)}
        />
      )}
      {showConvList&&<ConversationsListSheet
        theme={theme}
        onSelectConv={openConversation}
        onClose={()=>setShowConvList(false)}
      />}
      {activeConv&&<ConversationSheet
        theme={theme}
        conv={activeConv}
        currentUserId={user?.id||"me"}
        onClose={()=>setActiveConv(null)}
      />}
      {tab==="feed"      &&<FeedScreen theme={theme} currentUser={user} showToast={showToast}/>}
      {tab==="track"     &&<CalorieTracker theme={theme} setTheme={setTheme} user={user} showToast={showToast} gami={gami} onRefreshGami={refreshGami}/>}
      {tab==="discover"  &&<CommunityScreen theme={theme} currentUser={user} showToast={showToast} onViewUser={setViewUser}/>}
      {tab==="profile"   &&<ProfileScreen theme={theme} user={user} isOwn={true} showToast={showToast} onSignOut={onSignOut} onUpdateUser={onUpdateUser} onViewUser={setViewUser} gami={gami}/>}

      {/* Bottom Nav */}
      <nav className="sc bnav" data-theme={theme}>
        {[
          {id:"feed",    label:"Feed",     icon:<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>},
          {id:"track",   label:"Track",    icon:<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>},
          {id:"discover",label:"Discover", icon:<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>},
          {id:"profile", label:"Profile",  icon:<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>},
        ].map(t=>(
          <button key={t.id} className={`bnav-btn ${tab===t.id?"active":""}`} onClick={()=>setTab(t.id)}>
            {t.icon}<span>{t.label}</span>
          </button>
        ))}
      </nav>

      {/* User profile view overlay */}
      {viewUser&&<ProfileScreen theme={theme} userId={viewUser} currentUser={user} isOwn={false} showToast={showToast} onSignOut={()=>{}} onUpdateUser={()=>{}} onViewUser={setViewUser} onClose={()=>setViewUser(null)}/>}

      {toast&&<div className="toast-w"><div className={`toast ${toast.type}`}><span className="toast-dot"/>{toast.text}</div></div>}
    </>
  );
}

// ─── CALORIE TRACKER TAB ──────────────────────────────────────────────────────
function CalorieTracker({theme,setTheme,user,showToast,gami,onRefreshGami,...props}){
  const basGoal=user?.profile?.targets?.calories||2000;
  const [goal,setGoal]=useState(()=>{try{const s=localStorage.getItem("kcal_goal");return s?parseInt(s):basGoal;}catch{return basGoal;}});
  const [hist,setHist]=useState(loadH);
  const [favs,setFavs]=useState(loadF);
  const [scan,setScan]=useState(null);const [loading,setLoading]=useState(false);
  const [img,setImg]=useState(null);const [showScan,setShowScan]=useState(false);
  const [showGoal,setShowGoal]=useState(false);const [goalInp,setGoalInp]=useState(goal.toString());
  const [showM,setShowM]=useState(false);const [selCat,setSelCat]=useState(getCat());
  const [activeDay,setActiveDay]=useState(null);const [showFavs,setShowFavs]=useState(false);
  const ref=useRef(null);

  useEffect(()=>{try{localStorage.setItem("kcal_history",JSON.stringify(hist));}catch{};},[hist]);
  useEffect(()=>{try{localStorage.setItem("kcal_favorites",JSON.stringify(favs));}catch{};},[favs]);
  useEffect(()=>{try{localStorage.setItem("kcal_goal",goal.toString());}catch{};},[goal]);

  const log=hist[todayKey()]?.meals||[];
  const upd=meals=>{const tk=todayKey();setHist(p=>({...p,[tk]:{...(p[tk]||{}),meals,goal}}));};
  const tots=sumM(log);
  const mTgt=user?.profile?.targets||{protein:Math.round(goal*.25/4),carbs:Math.round(goal*.5/4),fat:Math.round(goal*.25/9)};
  const prog=Math.min(tots.total/goal,1);
  const tier=prog>=1?"over":prog>=.85?"warn":"on";

  const [showCam,setShowCam]=useState(false);
  const handleFoodCapture=async(base64,mt)=>{
    setShowCam(false);setImg("data:"+mt+";base64,"+base64);setSelCat(getCat());setShowScan(true);
    await callAI([{role:"user",content:[{type:"image",source:{type:"base64",media_type:mt,data:base64}},{type:"text",text:imgP}]}],setScan,setLoading);
  };
  const handleBarcodeResult=async code=>{
    setShowCam(false);setImg(null);setShowScan(true);setLoading(true);setScan(null);
    try{const r=await lookupBarcode(code);setScan(r);}catch{setScan({error:true});}finally{setLoading(false);}
  };
  const handleLabelCapture=async(base64,mt)=>{
    setShowCam(false);setImg(null);setShowScan(true);setLoading(true);setScan(null);
    try{
      const r=await api("/api/scan/label",{method:"POST",body:JSON.stringify({image:base64,mediaType:mt})});
      setScan({...r,type:"label",items:[{name:"Nutrition label scan",calories:r.calories,emoji:"🏷️",protein:r.protein,carbs:r.carbs,fat:r.fat,fiber:r.fiber||0,sodium:r.sodium||0,sugar:r.sugar||0}],total:r.calories,fiber:r.fiber||0,sodium:r.sodium||0,sugar:r.sugar||0});
    }catch{setScan({error:true});}finally{setLoading(false);}
  };
  const addToLog=(overrideScan=null)=>{
    if(!scan||scan.error)return;
    const activeScan = overrideScan||scan;
    const mac=sumI(activeScan.items);
    upd([{id:Date.now(),items:activeScan.items,total:activeScan.total,...mac,notes:activeScan.notes||scan.notes,imageUrl:img,category:selCat,time:new Date().toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})},...log]);
    // Fire server-side log event for streak + XP
    const token=getToken();
    logMealToServer((overrideScan||scan).total,goal,scan.type||"food",token).then(r=>{
      if(!r)return;
      if(r.xp)showToast("+"+(r.xp.xpEarned)+" XP · "+r.xp.title);
      if(r.newBadges?.length)r.newBadges.forEach(b=>{setTimeout(()=>{showToast(b.emoji+" "+b.name+" badge unlocked!");},800);setTimeout(()=>{if(window.confirm("Share your "+b.name+" badge?"))shareCard("badge",b,user?.profile?.handle||user?.name?.split(" ")[0]||"");},1500);});
      if(onRefreshGami)onRefreshGami();
    });
    dismiss();
  };
  const dismiss=()=>{setShowScan(false);setScan(null);setImg(null);};
  const saveGoal=()=>{const v=parseInt(goalInp);if(!isNaN(v)&&v>0)setGoal(v);setShowGoal(false);};
  const saveFav=(items,total)=>{if(favs.some(f=>itemsEq(f.items,items))){showToast("Already saved","inf");return;}const mac=sumI(items);setFavs(p=>[{id:Date.now(),name:favName(items),emoji:items[0]?.emoji||"⭐",items:items.map(i=>({...i})),total,...mac},...p]);showToast("Saved to favorites");};
  const quickAdd=fav=>{upd([{id:Date.now(),items:fav.items.map(i=>({...i})),total:fav.total,totalProtein:fav.totalProtein,totalCarbs:fav.totalCarbs,totalFat:fav.totalFat,notes:null,imageUrl:null,category:getCat(),time:new Date().toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})},...log]);showToast(`Added ${fav.name}`);};

  const cats=CATS.map(c=>({...c,meals:log.filter(m=>(m.category||"snacks")===c.id),total:log.filter(m=>(m.category||"snacks")===c.id).reduce((s,m)=>s+(m.total||0),0)}));
  const isSaved=scan&&!scan.error?favs.some(f=>itemsEq(f.items,scan.items)):false;

  const last7=getLast7();
  const wData=last7.map(d=>{const k=dayKey(d),m=hist[k]?.meals||[],g=hist[k]?.goal||goal,t=sumM(m);return{...t,date:d,key:k,goal:g,has:m.length>0};});
  const dwd=wData.filter(d=>d.has);
  const wavg=dwd.length>0?Math.round(dwd.reduce((s,d)=>s+d.total,0)/dwd.length):0;
  const otc=dwd.filter(d=>d.total>=d.goal*.85&&d.total<=d.goal*1.15).length;
  const maxB=Math.max(...wData.map(d=>d.total),goal*1.1);

  return(
    <>
      <div className="sc app-w" data-theme={theme}>
        <div className="hdr">
          <div className="logo">CalCheckAI</div>
          {props.gami&&props.gami.streak?.current_streak>0&&<div className="streak-chip" style={{cursor:"pointer"}} onClick={()=>shareCard("streak",{days:props.gami.streak.current_streak},user?.profile?.handle||user?.name?.split(" ")[0]||"")} title="Tap to share">🔥<span className="streak-num">{props.gami.streak.current_streak}</span>day streak ↗</div>}
          <div className="hdr-r">
            <ThemeBtn theme={theme} setTheme={setTheme}/>
            <button className="goal-chip" onClick={()=>{setGoalInp(goal.toString());setShowGoal(true);}}><span className="goal-dot"/>{goal.toLocaleString()} kcal</button>
            <button className="av-btn" style={{background:"var(--ac)",color:"#000"}}>{user?.name?.split(" ").map(n=>n[0]).slice(0,2).join("").toUpperCase()||"?"}</button>
          </div>
        </div>

        <div className="ring-w">
          <div className="ring-c">
            <svg className="ring-svg" width="100%" height="100%" viewBox="0 0 200 200">
              <circle className="ring-tr" cx="100" cy="100" r="82" fill="none" strokeWidth="12"/>
              <circle className="ring-fl" cx="100" cy="100" r="82" fill="none" strokeWidth="12" stroke={rFill[tier]} strokeDasharray={CIRC} strokeDashoffset={CIRC-(prog*CIRC)}/>
            </svg>
            <div className="ring-in"><div className="ring-n" style={{color:rTxt[theme][tier]}}>{tots.total.toLocaleString()}</div><div className="ring-s">kcal today</div></div>
          </div>
          <div className="ring-rm">{tots.total>=goal?<><b style={{color:"var(--re)"}}>+{(tots.total-goal).toLocaleString()} kcal</b> over goal</>:<><b>{(goal-tots.total).toLocaleString()} kcal</b> left for today</>}</div>
        </div>

        {gami&&(()=>{const lvl=getLevel(gami.xp?.total||0);const pct=lvl.nextXp?Math.round(((gami.xp?.total||0)-lvl.xp)/(lvl.nextXp-lvl.xp)*100):100;return(<div className="xp-bar-w"><div className="xp-bar-hd"><div className="xp-level"><svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>Lv.{lvl.level} {lvl.title}</div><div className="xp-pts">{(gami.xp?.total||0).toLocaleString()} XP{lvl.nextXp?" · "+lvl.nextXp+" to "+lvl.nextTitle:""}</div></div><div className="xp-bar"><div className="xp-fill" style={{width:pct+"%"}}/></div></div>);})()}
        <div className="macros">
          {[["Protein","var(--pr)",tots.protein,mTgt.protein],["Carbs","var(--ca)",tots.carbs,mTgt.carbs],["Fat","var(--fa)",tots.fat,mTgt.fat]].map(([l,c,v,t])=>(
            <div key={l} className="macro">
              <div className="macro-l"><span className="mdot" style={{background:c}}/>{l}</div>
              <div className="macro-n" style={{color:c}}>{v}<small>g</small></div>
              <div className="macro-b"><div className="macro-bf" style={{width:`${Math.min(100,Math.round(v/t*100)||0)}%`,background:c}}/></div>
              <div className="macro-t">of {t}g</div>
            </div>
          ))}
        </div>

        {/* Daily micronutrient totals */}
        {(tots.fiber>0||tots.sodium>0||tots.sugar>0)&&(
          <div style={{padding:"0 20px 4px"}}>
            <div className="macro-micro-row">
              {[{l:"Fiber",v:tots.fiber||0,u:"g"},{l:"Sugar",v:tots.sugar||0,u:"g"},{l:"Sodium",v:tots.sodium||0,u:"mg"}].map(m=>(
                <div key={m.l} className="mm-tile">
                  <div className="mm-val">{m.v}<small style={{fontSize:".6rem",fontWeight:400}}>{m.u}</small></div>
                  <div className="mm-lbl">{m.l}</div>
                </div>
              ))}
            </div>
          </div>
        )}
        <div className="cta-w">
          <button className="btn-lime" onClick={()=>setShowCam(true)}><svg width="21" height="21" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>Snap your meal</button>
          <button className="btn-out" onClick={()=>setShowM(true)}><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>Type it manually</button>
        </div>

        {favs.length>0&&<div className="qa-w"><div className="qa-h"><div className="qa-l"><svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>Quick add</div><button className="qa-m" onClick={()=>setShowFavs(true)}>Manage</button></div><div className="qa-s">{favs.map(f=><button key={f.id} className="fav-chip" onClick={()=>quickAdd(f)}><span>{f.emoji}</span><span className="fav-nm">{f.name}</span><span className="fav-cal">{f.total}</span></button>)}</div></div>}

        {log.length===0?<div className="empty"><span className="empty-ic">📸</span>Snap or type to start logging.<br/><small>Your meals appear here.</small></div>
          :cats.filter(c=>c.meals.length>0).map(cat=>(
            <div key={cat.id} className="cat-s">
              <div className="cat-hd"><div className="cat-ti"><span>{cat.emoji}</span><span className="cat-nm">{cat.label}</span><span className="cat-cnt">{cat.meals.length}</span></div><div className="cat-tot">{cat.total.toLocaleString()}<small>kcal</small></div></div>
              <div className="cat-cs">{cat.meals.map(meal=>{const sv=favs.some(f=>itemsEq(f.items,meal.items));return(
                <div key={meal.id} className="mc">
                  <div className="mc-hd">
                    <div className="mc-tw"><span className="mc-si">{meal.imageUrl?<svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>:<svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>}</span><span className="mc-t">{meal.time}</span></div>
                    <div className="mc-r"><div className="mc-tot">{meal.total.toLocaleString()}<sup>kcal</sup></div><button className={`mc-btn ${sv?"saved":""}`} onClick={()=>saveFav(meal.items,meal.total)}><svg width="11" height="11" viewBox="0 0 24 24" fill={sv?"currentColor":"none"} stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg></button><button className="mc-btn del" onClick={()=>upd(log.filter(m=>m.id!==meal.id))}>×</button></div>
                  </div>
                  <div className="mc-its">{meal.items.map((it,i)=><div key={i} className="mc-it"><div className="mc-il"><span style={{fontSize:"1rem",lineHeight:1.4,flexShrink:0}}>{it.emoji}</span><div className="mc-in"><span>{it.name}</span>{(it.protein||it.carbs||it.fat)&&<span className="mc-im"><span>P {it.protein||0}g</span><span>C {it.carbs||0}g</span><span>F {it.fat||0}g</span></span>}</div></div><span className="mc-ic">{it.calories} kcal</span></div>)}</div>
                  {(meal.totalProtein||meal.totalCarbs||meal.totalFat)&&<div className="mc-ft"><span className="mc-fs"><span className="mdot" style={{background:"var(--pr)"}}/>P <b>{meal.totalProtein||0}g</b></span><span className="mc-fs"><span className="mdot" style={{background:"var(--ca)"}}/>C <b>{meal.totalCarbs||0}g</b></span><span className="mc-fs"><span className="mdot" style={{background:"var(--fa)"}}/>F <b>{meal.totalFat||0}g</b></span></div>}
                </div>
              );})}</div>
            </div>
          ))
        }

        {/* Meals section */}
        <MealsSection
          theme={theme}
          user={user}
          token={token}
          goal={goal}
          mTgt={mTgt}
          showToast={showToast}
          onQuickAdd={meal=>quickAdd(meal)}
        />
        {/* Weight tracking card */}
        <WeightCard theme={theme} weightUnit={weightUnit} setWeightUnit={setWeightUnit} user={user} token={token}/>

        <div className="wk-s">
          <div className="wk-c">
            <div className="wk-h"><div style={{display:"flex",alignItems:"center",gap:10}}><div className="wk-t">This week</div><button className="analytics-btn" onClick={()=>setShowAnalytics(true)}><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/></svg>Analytics</button></div><div className="wk-st">{dwd.length>0?<><div>Avg <b>{wavg.toLocaleString()} kcal</b></div><div><b>{otc}</b> of {dwd.length} on track</div></>:<div>No data yet</div>}</div></div>
            {dwd.length===0?<div className="wk-em">Log meals to see your week here.</div>:(
              <><div className="ch-w"><div className="gl" data-label={goal.toLocaleString()} style={{bottom:`${(goal/maxB)*100}%`}}/><div className="bars">{wData.map((d,i)=>{const isT=d.key===todayKey();const clr=!d.has?"var(--s2)":d.total>d.goal*1.05?"#ff4d4d":d.total>=d.goal*.85?"#c8f135":"var(--m2)";return<button key={i} className={`dc ${isT?"tdc":""}`} onClick={()=>setActiveDay(d.date)}><div className="bt"><div className={`bar ${!d.has?"em":""} ${isT?"td":""}`} style={{height:d.has?`${(d.total/maxB)*100}%`:"6px",background:clr,color:clr}}>{d.has&&<div className="bv">{d.total>=1000?(d.total/1000).toFixed(1)+"k":d.total}</div>}</div></div></button>;})}</div></div><div className="dl-r">{wData.map((d,i)=><div key={i} className={`dl ${d.key===todayKey()?"td":""}`}>{dayShort(d.date)}</div>)}</div></>
            )}
          </div>
        </div>
      </div>

      {showCam&&<SmartScanner theme={theme} onFoodCapture={handleFoodCapture} onBarcodeResult={handleBarcodeResult} onLabelCapture={handleLabelCapture} onClose={()=>setShowCam(false)}/>}
      {showScan&&<ScanResultSheet scan={scan} loading={loading} img={img} isGuest={false} onDismiss={dismiss} onAdd={(edited)=>addToLog(edited)} onSave={()=>scan&&!scan.error&&saveFav(scan.items,scan.total)} saved={isSaved} selCat={selCat} setSelCat={setSelCat} onSignIn={()=>{}} theme={theme}/>}
      {activeDay&&<DayDetail date={activeDay} hist={hist} goal={goal} theme={theme} onClose={()=>setActiveDay(null)}/>}
      {showAnalytics&&<AnalyticsSheet hist={hist} goal={goal} theme={theme} onClose={()=>setShowAnalytics(false)}/>}
      {showFavs&&<FavsSheet favs={favs} setFavs={setFavs} quickAdd={quickAdd} theme={theme} onClose={()=>setShowFavs(false)}/>}
      {showM&&<ManualModal theme={theme} onClose={()=>setShowM(false)} onSubmit={txt=>{setShowScan(true);setImg(null);setSelCat(getCat());callAI([{role:"user",content:txtP(txt)}],setScan,setLoading);}}/>}
      {showGoal&&<div className="sc overlay center" data-theme={theme} onClick={e=>e.target===e.currentTarget&&setShowGoal(false)}><div className="gbox"><div className="g-ti">Daily goal</div><div className="g-su">Override your calculated target</div><input className="g-inp" type="number" inputMode="numeric" value={goalInp} onChange={e=>setGoalInp(e.target.value)} onKeyDown={e=>{if(e.key==="Enter")saveGoal();}} autoFocus/><div className="m-ac"><button className="btn-dim" onClick={()=>setShowGoal(false)}>Cancel</button><button className="btn-act" onClick={saveGoal}>Save</button></div></div></div>}
    </>
  );
}

// ─── REFERRAL LANDING PAGE ───────────────────────────────────────────────────
function ReferralLanding({refCode, onSignUp, onSignIn, theme}){
  const [referrer,setReferrer]=useState(null);
  const [loading,setLoading]=useState(true);

  useEffect(()=>{
    if(API_URL!=="YOUR_WORKER_URL_HERE"){
      fetch(`${API_URL}/api/referral/lookup?code=${refCode}`)
        .then(r=>r.json())
        .then(d=>{ if(!d.error)setReferrer(d); })
        .catch(()=>{})
        .finally(()=>setLoading(false));
    }else{
      // Demo referrer
      setReferrer({name:"Jordan Kim",handle:"jordan.kim",picture:null});
      setLoading(false);
    }
  },[refCode]);

  const initials=n=>n?.split(" ").map(x=>x[0]).slice(0,2).join("").toUpperCase()||"?";

  return(
    <div className="ref-land-ov" data-theme={theme}>
      <div className="ref-land-hero">
        <div className="ref-land-logo">CalCheck<span>AI</span></div>
        {!loading&&(
          <div className="ref-land-inviter">
            <div className="ref-land-av">
              {referrer?.picture
                ?<img src={referrer.picture} alt={referrer.name} style={{width:"100%",height:"100%",objectFit:"cover",borderRadius:"50%"}}/>
                :initials(referrer?.name||"?")}
            </div>
            <div className="ref-land-invited-by">You were invited by</div>
            <div className="ref-land-name">{referrer?.name||"a friend"}</div>
            {referrer?.handle&&<div className="ref-land-handle">@{referrer.handle}</div>}
            <div className="ref-land-msg">
              {referrer?.name
                ?`${referrer.name.split(" ")[0]} is tracking calories, hitting streaks and building squad goals on CalCheckAI.`
                :"Join your friend on CalCheckAI — the free AI calorie tracker with a social twist."}
            </div>
          </div>
        )}
        {loading&&<div style={{color:"var(--mu)",fontSize:".84rem",padding:"20px 0"}}>Loading…</div>}
      </div>

      <div className="ref-land-body">
        {/* Reward callout */}
        <div className="ref-land-reward">
          <span className="ref-land-reward-ic">🎁</span>
          <div className="ref-land-reward-inf">
            <div className="ref-land-reward-ti">Joining bonus — for both of you</div>
            <div className="ref-land-reward-su">Sign up and log your first meal — you both instantly receive <strong style={{color:"var(--at)"}}>500 XP</strong> and the exclusive <strong style={{color:"var(--at)"}}>🤝 Ambassador badge</strong></div>
          </div>
        </div>

        {/* Feature highlights */}
        <div className="ref-land-features">
          {[
            {ic:"📸",txt:<><strong>AI food scanning</strong> — point your camera at any meal and get instant nutrition data</>},
            {ic:"🏆",txt:<><strong>Squad leaderboards</strong> — compete with your gym, office or friend group</>},
            {ic:"🔥",txt:<><strong>Streaks and XP</strong> — build daily logging habits and level up</>},
            {ic:"💬",txt:<><strong>Message friends</strong> — private messaging for mutual followers</>},
          ].map((f,i)=>(
            <div key={i} className="ref-land-feat">
              <span className="ref-land-feat-ic">{f.ic}</span>
              <span className="ref-land-feat-txt">{f.txt}</span>
            </div>
          ))}
        </div>

        {/* CTA */}
        <button className="ref-land-cta" onClick={onSignUp}>
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/><polyline points="10 17 15 12 10 7"/><line x1="15" y1="12" x2="3" y2="12"/></svg>
          Sign up free
        </button>
        <div className="ref-land-signin">
          Already have an account?{" "}
          <button onClick={onSignIn}>Sign in</button>
        </div>
      </div>
    </div>
  );
}

// ─── MEALS SECTION ───────────────────────────────────────────────────────────
function MealsSection({theme, user, token, goal, mTgt, showToast, onQuickAdd}){
  const [open,setOpen]=useState(false);
  const [tab,setTab]=useState("foods");
  // Custom foods
  const [foods,setFoods]=useState([]);
  const [showAddFood,setShowAddFood]=useState(false);
  const [foodForm,setFoodForm]=useState({name:"",emoji:"🍽️",calories:"",protein:"",carbs:"",fat:""});
  // Recipes
  const [recipes,setRecipes]=useState([]);
  // Meal plan
  const [plan,setPlan]=useState(null);
  const [planPref,setPlanPref]=useState("no_restriction");
  const [generating,setGenerating]=useState(false);
  const [expandedMeal,setExpandedMeal]=useState(null);

  useEffect(()=>{
    if(!open)return;
    if(API_URL==="YOUR_WORKER_URL_HERE"){
      // Demo data
      setFoods([
        {id:"f1",name:"Chicken Breast",emoji:"🍗",calories:165,protein:31,carbs:0,fat:4},
        {id:"f2",name:"Brown Rice",emoji:"🍚",calories:216,protein:5,carbs:45,fat:2},
        {id:"f3",name:"Greek Yogurt",emoji:"🥛",calories:100,protein:17,carbs:6,fat:1},
      ]);
      setRecipes([
        {id:"r1",name:"Protein Bowl",emoji:"🥗",calories:381,protein:36,carbs:45,fat:6,ingredients:["Chicken Breast","Brown Rice"],steps:["Cook chicken","Serve over rice"]},
      ]);
      return;
    }
    api("/api/foods").then(setFoods).catch(()=>{});
    api("/api/recipes").then(setRecipes).catch(()=>{});
    api("/api/meal-plan").then(d=>{if(d.plan)setPlan(d.plan);}).catch(()=>{});
  },[open]);

  const saveFood=async()=>{
    if(!foodForm.name||!foodForm.calories)return;
    const f={...foodForm,calories:parseInt(foodForm.calories),protein:parseFloat(foodForm.protein)||0,carbs:parseFloat(foodForm.carbs)||0,fat:parseFloat(foodForm.fat)||0};
    if(API_URL!=="YOUR_WORKER_URL_HERE"){
      const saved=await api("/api/foods",{method:"POST",body:JSON.stringify(f)}).catch(()=>null);
      if(saved)setFoods(prev=>[saved,...prev]);
    }else{
      setFoods(prev=>[{id:"d"+Date.now(),...f},...prev]);
    }
    setFoodForm({name:"",emoji:"🍽️",calories:"",protein:"",carbs:"",fat:""});
    setShowAddFood(false);
    showToast("Food saved to your library");
  };

  const delFood=async id=>{
    setFoods(prev=>prev.filter(f=>f.id!==id));
    if(API_URL!=="YOUR_WORKER_URL_HERE") api(`/api/foods/${id}`,{method:"DELETE"}).catch(()=>{});
  };

  const generatePlan=async()=>{
    setGenerating(true);
    if(API_URL==="YOUR_WORKER_URL_HERE"){
      setTimeout(()=>{
        setPlan({days:[
          {day:"Monday",meals:[
            {type:"Breakfast",name:"Oatmeal with berries",emoji:"🥣",calories:320,protein:12,carbs:58,fat:6,ingredients:["1 cup rolled oats","½ cup mixed berries","1 tbsp honey","1 cup oat milk"],steps:["Bring oat milk to a simmer","Add oats, cook 5 min stirring","Top with berries and honey"]},
            {type:"Lunch",name:"Grilled chicken salad",emoji:"🥗",calories:420,protein:38,carbs:22,fat:18,ingredients:["150g chicken breast","2 cups mixed greens","½ avocado","1 tbsp olive oil","Lemon juice"],steps:["Season and grill chicken 6-7 min per side","Slice and place on greens","Top with avocado and dress with oil and lemon"]},
            {type:"Dinner",name:"Salmon with roasted veg",emoji:"🐟",calories:530,protein:42,carbs:28,fat:24,ingredients:["180g salmon","1 cup broccoli","1 cup sweet potato cubed","1 tbsp olive oil","Garlic and seasoning"],steps:["Preheat oven 200°C","Toss veg in oil and seasoning, roast 20 min","Season salmon, bake 12 min alongside veg"]},
          ]},
          {day:"Tuesday",meals:[
            {type:"Breakfast",name:"Protein smoothie",emoji:"🥤",calories:280,protein:28,carbs:32,fat:5,ingredients:["1 scoop protein powder","1 banana","1 cup spinach","1 cup oat milk","Ice"],steps:["Add all to blender","Blend until smooth","Serve immediately"]},
            {type:"Lunch",name:"Turkey and avocado wrap",emoji:"🌯",calories:400,protein:35,carbs:38,fat:12,ingredients:["150g turkey breast","1 whole wheat wrap","Lettuce and tomato","½ avocado","Mustard"],steps:["Layer turkey and veg on wrap","Add avocado and mustard","Roll tightly and halve"]},
            {type:"Dinner",name:"Stir-fry rice bowl",emoji:"🥘",calories:480,protein:28,carbs:52,fat:16,ingredients:["200g firm tofu","2 cups mixed veg","2 tbsp soy sauce","1 tsp sesame oil","1 cup brown rice"],steps:["Press tofu and cube, fry until golden","Add veg, stir-fry 4 min","Toss with soy and sesame, serve on rice"]},
          ]},
        ]});
        setGenerating(false);
      },2200);
      return;
    }
    try{
      const p=await api("/api/meal-plan/generate",{method:"POST",body:JSON.stringify({preference:planPref,targets:{calories:goal,protein:mTgt.protein,carbs:mTgt.carbs,fat:mTgt.fat}})});
      if(p.error){showToast("Could not generate plan, try again","err");}
      else setPlan(p);
    }catch{showToast("Generation failed, try again","err");}
    setGenerating(false);
  };

  const PREFS=[{k:"no_restriction",l:"No restriction"},{k:"high_protein",l:"High protein"},{k:"vegetarian",l:"Vegetarian"},{k:"vegan",l:"Vegan"},{k:"low_carb",l:"Low carb"}];

  return(
    <div className="meals-card">
      <div className="meals-card-hd" onClick={()=>setOpen(o=>!o)}>
        <div className="meals-card-left">
          <div className="meals-card-ic">🍽️</div>
          <div>
            <div className="meals-card-title">Meals</div>
            <div className="meals-card-sub">My foods · Recipes · AI meal plan</div>
          </div>
        </div>
        <svg className={`meals-card-chev ${open?"open":""}`} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
      </div>

      {open&&(
        <>
          <div className="meals-inner-tabs">
            {[{k:"foods",l:"My Foods"},{k:"recipes",l:"My Recipes"},{k:"plan",l:"Meal Plan"}].map(t=>(
              <button key={t.k} className={`meals-inner-tab ${tab===t.k?"on":""}`} onClick={()=>setTab(t.k)}>{t.l}</button>
            ))}
          </div>
          <div className="meals-tab-body">
            {/* ── My Foods ── */}
            {tab==="foods"&&(
              <>
                {foods.map(f=>(
                  <div key={f.id} className="food-row">
                    <span className="food-em-ic">{f.emoji||"🍽️"}</span>
                    <div className="food-row-inf">
                      <div className="food-row-name">{f.name}</div>
                      <div className="food-row-mac">P {f.protein||0}g · C {f.carbs||0}g · F {f.fat||0}g</div>
                    </div>
                    <div style={{display:"flex",alignItems:"center",gap:8}}>
                      <span className="food-row-cal">{f.calories}</span>
                      <button className="food-row-del" onClick={()=>delFood(f.id)}>
                        <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 6h18M8 6V4h8v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/></svg>
                      </button>
                    </div>
                  </div>
                ))}
                {foods.length===0&&!showAddFood&&<div style={{textAlign:"center",padding:"16px 0",color:"var(--mu)",fontSize:".82rem"}}>No custom foods yet</div>}
                {!showAddFood?(
                  <button className="meals-add-btn" onClick={()=>setShowAddFood(true)}>
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
                    Add custom food
                  </button>
                ):(
                  <div className="meals-add-form">
                    <div className="meals-form-ti">New food</div>
                    <div style={{marginBottom:8}}>
                      <input className="meals-form-inp" style={{width:"100%"}} placeholder="Food name" value={foodForm.name} onChange={e=>setFoodForm(f=>({...f,name:e.target.value}))}/>
                    </div>
                    <div className="meals-form-grid">
                      <input type="number" className="meals-form-inp" placeholder="Calories" value={foodForm.calories} onChange={e=>setFoodForm(f=>({...f,calories:e.target.value}))}/>
                      <input type="number" className="meals-form-inp" placeholder="Protein (g)" value={foodForm.protein} onChange={e=>setFoodForm(f=>({...f,protein:e.target.value}))}/>
                      <input type="number" className="meals-form-inp" placeholder="Carbs (g)" value={foodForm.carbs} onChange={e=>setFoodForm(f=>({...f,carbs:e.target.value}))}/>
                      <input type="number" className="meals-form-inp" placeholder="Fat (g)" value={foodForm.fat} onChange={e=>setFoodForm(f=>({...f,fat:e.target.value}))}/>
                    </div>
                    <div className="meals-form-acts">
                      <button className="meals-form-cancel" onClick={()=>{setShowAddFood(false);setFoodForm({name:"",emoji:"🍽️",calories:"",protein:"",carbs:"",fat:""});}}>Cancel</button>
                      <button className="meals-form-save" onClick={saveFood} disabled={!foodForm.name||!foodForm.calories}>Save food</button>
                    </div>
                  </div>
                )}
              </>
            )}
            {/* ── My Recipes ── */}
            {tab==="recipes"&&(
              <>
                {recipes.map(r=>(
                  <div key={r.id} className="recipe-card">
                    <div className="recipe-card-row">
                      <span className="recipe-card-em">{r.emoji||"🥘"}</span>
                      <div className="recipe-card-inf">
                        <div className="recipe-card-name">{r.name}</div>
                        <div className="recipe-card-mac">P {r.protein||0}g · C {r.carbs||0}g · F {r.fat||0}g</div>
                      </div>
                      <span className="recipe-card-cal">{r.calories}</span>
                      <button className="recipe-add-log" onClick={()=>{onQuickAdd&&onQuickAdd({id:r.id,name:r.name,emoji:r.emoji||"🥘",total:r.calories,items:[{name:r.name,calories:r.calories,protein:r.protein||0,carbs:r.carbs||0,fat:r.fat||0,emoji:r.emoji||"🥘"}],totalProtein:r.protein||0,totalCarbs:r.carbs||0,totalFat:r.fat||0});showToast("Added to today ✓");}}>+ Add</button>
                    </div>
                  </div>
                ))}
                {recipes.length===0&&<div style={{textAlign:"center",padding:"16px 0",color:"var(--mu)",fontSize:".82rem"}}>No recipes yet.<br/><small>Recipes are built from your custom foods.</small></div>}
                <button className="meals-add-btn" onClick={()=>showToast("Recipe builder coming soon")}>
                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
                  Create recipe
                </button>
              </>
            )}
            {/* ── Meal Plan ── */}
            {tab==="plan"&&(
              <>
                <div className="plan-targets-row">
                  🎯 Your targets: <strong style={{color:"var(--at)"}}>{goal.toLocaleString()} kcal</strong> · P {mTgt.protein}g · C {mTgt.carbs}g · F {mTgt.fat}g
                </div>
                <div className="plan-prefs">
                  {PREFS.map(p=><button key={p.k} className={`plan-pref ${planPref===p.k?"on":""}`} onClick={()=>setPlanPref(p.k)}>{p.l}</button>)}
                </div>
                <div className="plan-hd">
                  <div className="plan-ti">{plan?"Your meal plan":"Generate a plan"}</div>
                  <button className="plan-gen-btn" onClick={generatePlan} disabled={generating}>
                    {generating?<div className="spinner" style={{width:12,height:12,borderWidth:2}}/>:
                      <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/></svg>}
                    {generating?"Generating…":plan?"Regenerate":"Generate"}
                  </button>
                </div>
                {generating&&<div className="plan-generating"><div className="spinner"/><span>AI is crafting your meal plan…</span></div>}
                {!generating&&!plan&&<div className="plan-empty">✨ Tap Generate for a personalised 7-day meal plan with recipes and ingredients</div>}
                {!generating&&plan&&plan.days?.map(day=>(
                  <div key={day.day}>
                    <div className="plan-day-label">{day.day}</div>
                    {day.meals?.map((meal,i)=>{
                      const key=day.day+i;
                      const expanded=expandedMeal===key;
                      return(
                        <div key={i} className="plan-meal-card" onClick={()=>setExpandedMeal(expanded?null:key)}>
                          <div className="plan-meal-hd">
                            <span className="plan-meal-em">{meal.emoji}</span>
                            <div className="plan-meal-inf">
                              <div className="plan-meal-type">{meal.type}</div>
                              <div className="plan-meal-name">{meal.name}</div>
                            </div>
                            <span className="plan-meal-kcal">{meal.calories} kcal</span>
                          </div>
                          <div className="plan-meal-macs"><span>P {meal.protein}g</span><span>C {meal.carbs}g</span><span>F {meal.fat}g</span></div>
                          {expanded&&(
                            <div className="plan-meal-detail" onClick={e=>e.stopPropagation()}>
                              <div className="plan-detail-ti">Ingredients</div>
                              {(meal.ingredients||[]).map((ing,j)=><div key={j} className="plan-ing">{ing}</div>)}
                              <div className="plan-detail-ti" style={{marginTop:10}}>Preparation</div>
                              {(meal.steps||[]).map((step,j)=>(
                                <div key={j} className="plan-step">
                                  <span className="plan-step-n">{j+1}</span>
                                  <span className="plan-step-txt">{step}</span>
                                </div>
                              ))}
                              <button className="plan-log-btn" onClick={()=>{
                                onQuickAdd&&onQuickAdd({id:"mp"+Date.now(),name:meal.name,emoji:meal.emoji,total:meal.calories,items:[{name:meal.name,calories:meal.calories,protein:meal.protein||0,carbs:meal.carbs||0,fat:meal.fat||0,emoji:meal.emoji}],totalProtein:meal.protein||0,totalCarbs:meal.carbs||0,totalFat:meal.fat||0});
                                showToast("Meal added to today ✓");
                              }}>Log this meal</button>
                            </div>
                          )}
                        </div>
                      );
                    })}
                  </div>
                ))}
              </>
            )}
          </div>
        </>
      )}
    </div>
  );
}

// ─── WEIGHT TRACKING CARD ────────────────────────────────────────────────────
function WeightCard({theme, weightUnit, setWeightUnit, user, token}){
  const [open,setOpen]=useState(false);
  const [inp,setInp]=useState("");
  const [logs,setLogs]=useState(()=>{
    try{return JSON.parse(localStorage.getItem("kcal_weight_logs")||"[]");}catch{return[];}
  });
  const [photos,setPhotos]=useState(()=>{
    try{return JSON.parse(localStorage.getItem("kcal_weight_photos")||"{}");}catch{return{};}
  });
  const [pendingPhoto,setPendingPhoto]=useState(null); // base64 preview before logging
  const [viewPhoto,setViewPhoto]=useState(null); // {date, src, kg}
  const imgInputRef=useRef(null);
  const camInputRef=useRef(null);

  const KG_KEY="kcal_weight_logs";
  const PH_KEY="kcal_weight_photos";

  const toDisplay=kg=>weightUnit==="lbs"?Math.round(kg*2.20462*10)/10:Math.round(kg*10)/10;
  const fromDisplay=val=>weightUnit==="lbs"?parseFloat(val)/2.20462:parseFloat(val);

  const handlePhotoFile=e=>{
    const f=e.target.files?.[0];
    if(!f)return;
    const reader=new FileReader();
    reader.onload=ev=>setPendingPhoto(ev.target.result);
    reader.readAsDataURL(f);
    e.target.value="";
  };

  const logWeight=()=>{
    const raw=parseFloat(inp);
    if(!raw||raw<20||raw>500)return;
    const kg=fromDisplay(raw);
    const today=new Date().toISOString().split("T")[0];
    const updated=[{date:today,kg:Math.round(kg*100)/100},...logs.filter(l=>l.date!==today)].sort((a,b)=>b.date.localeCompare(a.date)).slice(0,90);
    setLogs(updated);
    localStorage.setItem(KG_KEY,JSON.stringify(updated));
    // Save photo if pending
    if(pendingPhoto){
      const updPhotos={...photos,[today]:pendingPhoto};
      setPhotos(updPhotos);
      localStorage.setItem(PH_KEY,JSON.stringify(updPhotos));
      setPendingPhoto(null);
    }
    setInp("");
    if(API_URL!=="YOUR_WORKER_URL_HERE"&&token){
      fetch(API_URL+"/api/weight",{method:"POST",headers:{"Content-Type":"application/json","Authorization":"Bearer "+token},body:JSON.stringify({weight_kg:Math.round(kg*100)/100,date:today})}).catch(()=>{});
    }
  };

  const delLog=date=>{
    const updated=logs.filter(l=>l.date!==date);
    const updPhotos={...photos};delete updPhotos[date];
    setLogs(updated);setPhotos(updPhotos);
    localStorage.setItem(KG_KEY,JSON.stringify(updated));
    localStorage.setItem(PH_KEY,JSON.stringify(updPhotos));
  };

  const last30=logs.slice(0,30).reverse();
  const current=logs[0]?toDisplay(logs[0].kg):null;
  const start=logs[logs.length-1]?toDisplay(logs[logs.length-1].kg):null;
  const change=current&&start?Math.round((current-start)*10)/10:null;
  const highest=last30.length?Math.max(...last30.map(l=>toDisplay(l.kg))):0;
  const lowest=last30.length?Math.min(...last30.map(l=>toDisplay(l.kg))):0;

  // SVG line chart
  const chartH=80;const chartW=300;const pad=8;
  const points=last30.map((l,i)=>{
    const x=pad+(i/(Math.max(last30.length-1,1)))*(chartW-pad*2);
    const range=highest-lowest||1;
    const y=chartH-pad-(((toDisplay(l.kg)-lowest)/range)*(chartH-pad*2));
    return[Math.round(x),Math.round(y)];
  });
  const polyline=points.map(p=>p.join(",")).join(" ");
  const areaPath=points.length>1?`M${points[0][0]},${chartH} L${points.map(p=>p.join(",")).join(" L")} L${points[points.length-1][0]},${chartH} Z`:"";

  const unitLabel=weightUnit==="lbs"?"lbs":"kg";

  return(
    <div className="wt-card">
      <div className="wt-card-hd" onClick={()=>setOpen(o=>!o)}>
        <div className="wt-card-left">
          <div className="wt-card-ic">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 6l9-4 9 4v6c0 5.25-3.75 9.74-9 11-5.25-1.26-9-5.75-9-11V6z"/></svg>
          </div>
          <div>
            <div className="wt-card-title">Weight</div>
            <div className="wt-card-sub">
              {current?`Current: ${current} ${unitLabel}`:"Tap to log your weight"}
            </div>
          </div>
        </div>
        <div className="wt-card-right">
          {current&&<span className="wt-current">{current} {unitLabel}</span>}
          <svg className={`wt-chevron ${open?"open":""}`} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
        </div>
      </div>

      {open&&(
        <div className="wt-body">
          {/* Entry row */}
          <div className="wt-entry-row">
            <div className="wt-inp-wrap">
              <span className="wt-inp-label">Today's weight</span>
              <input className="wt-inp" type="number" inputMode="decimal" placeholder={weightUnit==="lbs"?"e.g. 165":"e.g. 75"} value={inp} onChange={e=>setInp(e.target.value)} onKeyDown={e=>e.key==="Enter"&&logWeight()}/>
            </div>
            <div className="wt-unit-toggle">
              {["kg","lbs"].map(u=><button key={u} className={`wt-unit-btn ${weightUnit===u?"on":""}`} onClick={()=>{setWeightUnit(u);localStorage.setItem("kcal_weight_unit",u);}}>
                {u}
              </button>)}
            </div>
            <button className="wt-log-btn" onClick={logWeight} disabled={!inp.trim()}>Log</button>
          </div>
          {/* Photo prompt — shown when weight is entered */}
          {inp.trim()&&(
            <div className="pp-prompt">
              {pendingPhoto?(
                <>
                  <div className="pp-thumb"><img src={pendingPhoto} alt="Progress"/><button className="pp-thumb-del" onClick={()=>setPendingPhoto(null)}>×</button></div>
                  <span className="pp-prompt-txt">Progress photo added ✓</span>
                </>
              ):(
                <>
                  <span className="pp-prompt-txt">Add a progress photo? (optional)</span>
                  <div className="pp-prompt-btns">
                    <button className="pp-prompt-btn" title="Take photo" onClick={()=>camInputRef.current?.click()}>
                      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
                    </button>
                    <button className="pp-prompt-btn" title="Choose from gallery" onClick={()=>imgInputRef.current?.click()}>
                      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
                    </button>
                  </div>
                </>
              )}
              <input ref={camInputRef} type="file" accept="image/*" capture="environment" style={{display:"none"}} onChange={handlePhotoFile}/>
              <input ref={imgInputRef} type="file" accept="image/*" style={{display:"none"}} onChange={handlePhotoFile}/>
            </div>
          )}

          {/* Chart */}
          {last30.length<2?(
            <div className="wt-chart-empty">📈 Log at least 2 entries to see your progress graph</div>
          ):(
            <>
              <div className="wt-chart-wrap">
                <svg className="wt-chart-svg" viewBox={`0 0 ${chartW} ${chartH}`} preserveAspectRatio="none" style={{height:80}}>
                  {areaPath&&<path className="wt-chart-area" d={areaPath} fill="var(--ac)"/>}
                  <polyline className="wt-chart-line" points={polyline}/>
                  {points.map((p,i)=>(
                    <circle key={i} className={i===points.length-1?"wt-chart-dot-latest":"wt-chart-dot"} cx={p[0]} cy={p[1]} r={i===points.length-1?4:2.5}/>
                  ))}
                </svg>
              </div>
              <div className="wt-stats-row">
                <div className="wt-stat">
                  <div className="wt-stat-val">{current} <small style={{fontSize:".6rem",fontWeight:400}}>{unitLabel}</small></div>
                  <div className="wt-stat-lbl">Current</div>
                </div>
                <div className="wt-stat">
                  <div className={`wt-stat-val ${change>0?"pos":change<0?"neg":""}`}>{change!==null?(change>0?"+":"")+change+" "+unitLabel:"—"}</div>
                  <div className="wt-stat-lbl">Change</div>
                </div>
                <div className="wt-stat">
                  <div className="wt-stat-val">{logs.length}</div>
                  <div className="wt-stat-lbl">Entries</div>
                </div>
              </div>
            </>
          )}

          {/* Recent history */}
          {logs.length>0&&(
            <div className="wt-history">
              {logs.slice(0,10).map(l=>(
                <div key={l.date} className="wt-hist-row">
                  {photos[l.date]&&(
                    <div className="pp-thumb" style={{width:36,height:36}}>
                      <img src={photos[l.date]} alt="Progress" style={{width:36,height:36,borderRadius:7}} onClick={()=>setViewPhoto({date:l.date,src:photos[l.date],kg:l.kg})}/>
                    </div>
                  )}
                  <span className="wt-hist-date">{new Date(l.date+"T12:00:00").toLocaleDateString(undefined,{month:"short",day:"numeric",year:"numeric"})}</span>
                  <span className="wt-hist-val">{toDisplay(l.kg)} {unitLabel}</span>
                  <button className="wt-hist-del" onClick={()=>delLog(l.date)}>
                    <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 6h18M8 6V4h8v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/></svg>
                  </button>
                </div>
              ))}
            </div>
          )}
        </div>
      )}
      {/* Photo full-screen viewer */}
      {viewPhoto&&(
        <div className="pp-fs-ov" onClick={e=>e.target===e.currentTarget&&setViewPhoto(null)}>
          <img className="pp-fs-img" src={viewPhoto.src} alt="Progress"/>
          <div className="pp-fs-meta">
            <div className="pp-fs-date">{new Date(viewPhoto.date+"T12:00:00").toLocaleDateString(undefined,{month:"long",day:"numeric",year:"numeric"})}</div>
            <div className="pp-fs-weight">{toDisplay(viewPhoto.kg)} {unitLabel}</div>
          </div>
          <div className="pp-fs-actions">
            <button className="pp-fs-close" onClick={()=>setViewPhoto(null)}>Close</button>
          </div>
        </div>
      )}
    </div>
  );
}

// ─── REFERRAL CARD ───────────────────────────────────────────────────────────
function ReferralCard({user, token, theme}){
  const [stats,setStats]=useState(null);
  const [copied,setCopied]=useState(false);
  const [loading,setLoading]=useState(true);

  useEffect(()=>{
    if(API_URL!=="YOUR_WORKER_URL_HERE"&&token){
      api("/api/referral/stats").then(d=>{setStats(d);setLoading(false);}).catch(()=>setLoading(false));
    }else{
      // Demo data
      setStats({
        ref_code:"alex1234",
        ref_url:"https://calcheckai.com?ref=alex1234",
        total_referrals:3,
        active_referrals:2,
        stealth_referrals:1,
        stealth_until:null,
        has_ambassador_badge:true,
      });
      setLoading(false);
    }
  },[token]);

  const copyLink=()=>{
    const url=stats?.ref_url||`https://calcheckai.com?ref=${stats?.ref_code}`;
    navigator.clipboard?.writeText(url).catch(()=>{});
    setCopied(true);
    setTimeout(()=>setCopied(false),2000);
  };

  const stealthActive=stats?.stealth_until&&new Date(stats.stealth_until)>new Date();
  const stealthDaysLeft=stealthActive?Math.ceil((new Date(stats.stealth_until)-new Date())/(1000*60*60*24)):0;

  if(loading)return null;

  return(
    <div className="ref-card">
      <div className="ref-card-ti">🤝 Invite friends</div>
      <div className="ref-card-sub">Share your link. When a friend signs up and logs their first meal, you both get 500 XP and the exclusive Ambassador badge.</div>

      {stats?.has_ambassador_badge&&(
        <div className="ref-ambassador">🤝 Ambassador badge earned</div>
      )}

      {/* Referral link */}
      <div className="ref-link-row">
        <span className="ref-link-txt">{stats?.ref_url||"Loading…"}</span>
        <button className="ref-copy-btn" onClick={copyLink}>{copied?"Copied ✓":"Copy"}</button>
      </div>

      {/* Stats */}
      <div className="ref-stats-row">
        <div className="ref-stat"><div className="ref-stat-val">{stats?.total_referrals||0}</div><div className="ref-stat-lbl">Invited</div></div>
        <div className="ref-stat"><div className="ref-stat-val">{stats?.active_referrals||0}</div><div className="ref-stat-lbl">Active</div></div>
        <div className="ref-stat"><div className="ref-stat-val">{stats?.stealth_referrals||0}</div><div className="ref-stat-lbl">Stealth earned</div></div>
      </div>

      {/* Rewards explained */}
      <div className="ref-rewards">
        <div className="ref-reward-row">
          <span className="ref-reward-ic">⚡</span>
          <div className="ref-reward-inf">
            <div className="ref-reward-ti">When they log their first meal</div>
            <div className="ref-reward-su">You both get 500 XP and the exclusive 🤝 Ambassador badge</div>
          </div>
        </div>
        <div className="ref-reward-row">
          <span className="ref-reward-ic">🕵️</span>
          <div className="ref-reward-inf">
            <div className="ref-reward-ti">After 7 days of active use</div>
            <div className="ref-reward-su">You earn 7 days of free stealth profile visit mode — visit profiles without triggering notifications</div>
          </div>
        </div>
      </div>

      {/* Active stealth mode banner */}
      {stealthActive&&(
        <div className="ref-stealth-active">
          🕵️ Stealth mode active · {stealthDaysLeft} {stealthDaysLeft===1?"day":"days"} remaining
        </div>
      )}
    </div>
  );
}

// ─── DETAILED ANALYTICS ──────────────────────────────────────────────────────
function AnalyticsSheet({hist, goal, theme, onClose}){
  const [view,setView]=useState("week"); // 'week' | '90'
  const last7=getLast7();
  const last90=getLastN(90);

  const wData=last7.map(d=>{const k=dayKey(d),m=hist[k]?.meals||[],g=hist[k]?.goal||goal,t=sumM(m);return{...t,date:d,key:k,goal:g,has:m.length>0};});
  const dwd=wData.filter(d=>d.has);
  const wavg=dwd.length>0?Math.round(dwd.reduce((s,d)=>s+d.total,0)/dwd.length):0;
  const otc=dwd.filter(d=>d.total>=d.goal*.85&&d.total<=d.goal*1.15).length;
  const wProtein=dwd.reduce((s,d)=>s+(d.protein||0),0);
  const wCarbs=dwd.reduce((s,d)=>s+(d.carbs||0),0);
  const wFat=dwd.reduce((s,d)=>s+(d.fat||0),0);
  const wMacroTotal=wProtein+wCarbs+wFat||1;

  // 90-day data
  const d90=last90.map(d=>{const k=dayKey(d),m=hist[k]?.meals||[],g=hist[k]?.goal||goal,t=sumM(m);return{...t,date:d,key:k,goal:g,has:m.length>0};});
  const d90logged=d90.filter(d=>d.has);
  const d90avg=d90logged.length>0?Math.round(d90logged.reduce((s,d)=>s+d.total,0)/d90logged.length):0;
  const d90max=Math.max(...d90.map(d=>d.total),goal*1.1,1);
  const d90otc=d90logged.filter(d=>d.total>=d.goal*.85&&d.total<=d.goal*1.15).length;
  const d90streak=()=>{let s=0;for(let i=d90.length-1;i>=0;i--){if(d90[i].has)s++;else break;}return s;};

  // Group 90-day by month for section labels
  const months=[];
  let lastMonth=null;
  d90.forEach((d,i)=>{
    const m=d.date.toLocaleDateString(undefined,{month:"short"});
    if(m!==lastMonth){months.push({label:m,start:i});lastMonth=m;}
  });

  const barColor=d=>!d.has?"var(--s2)":d.total>d.goal*1.05?"#ff4d4d":d.total>=d.goal*.85?"#c8f135":"var(--m2)";
  const [activeDay,setActiveDay]=useState(null);

  return(
    <div className="sc overlay" data-theme={theme} onClick={e=>e.target===e.currentTarget&&onClose()}>
      <div className="sheet">
        <div className="handle"/>
        <div className="sh-ti">Analytics</div>
        <div className="an-tabs">
          {[{k:"week",l:"This week"},{k:"90",l:"Last 90 days"}].map(t=>(
            <button key={t.k} className={`an-tab ${view===t.k?"on":""}`} onClick={()=>setView(t.k)}>{t.l}</button>
          ))}
        </div>

        {view==="week"&&(
          <>
            <div className="an-week-stats">
              <div className="an-week-stat"><div className="an-week-stat-val">{wavg.toLocaleString()}</div><div className="an-week-stat-lbl">Daily avg</div></div>
              <div className="an-week-stat"><div className="an-week-stat-val">{dwd.length}/7</div><div className="an-week-stat-lbl">Days logged</div></div>
              <div className="an-week-stat"><div className="an-week-stat-val">{otc}/{dwd.length}</div><div className="an-week-stat-lbl">On target</div></div>
            </div>
            {/* Weekly bar chart (reuse existing style inline) */}
            {dwd.length>0&&(
              <div style={{background:"var(--s2)",borderRadius:14,padding:"14px 14px 8px",marginBottom:16}}>
                <div style={{display:"flex",alignItems:"flex-end",gap:"3%",height:80,position:"relative"}}>
                  {wData.map((d,i)=>{
                    const isT=d.key===todayKey();
                    const h=d.has?Math.max(8,Math.round((d.total/Math.max(...wData.map(x=>x.total),goal))*76)):6;
                    return(
                      <div key={i} style={{flex:1,display:"flex",flexDirection:"column",alignItems:"center",gap:3,cursor:"pointer"}} onClick={()=>setActiveDay(d.date)}>
                        <div style={{fontSize:".62rem",color:"var(--mu)",marginBottom:2}}>{d.has?d.total>=1000?(d.total/1000).toFixed(1)+"k":d.total:""}</div>
                        <div style={{width:"100%",borderRadius:"4px 4px 0 0",background:barColor(d),height:h+"px",transition:"height .5s ease",minHeight:6,border:isT?"2px solid var(--ac)":"none"}}/>
                        <div style={{fontSize:".62rem",color:isT?"var(--at)":"var(--mu)",fontWeight:isT?700:400}}>{d.date.toLocaleDateString(undefined,{weekday:"short"}).slice(0,3)}</div>
                      </div>
                    );
                  })}
                </div>
              </div>
            )}
            {/* Macro distribution */}
            {dwd.length>0&&(
              <div className="an-macro-dist">
                <div className="an-macro-dist-ti">Macro distribution (weekly avg)</div>
                {[{l:"Protein",val:wProtein,color:"var(--pr)"},{l:"Carbs",val:wCarbs,color:"var(--ca)"},{l:"Fat",val:wFat,color:"var(--fa)"}].map(m=>(
                  <div key={m.l} className="an-macro-bar-row">
                    <div className="an-macro-bar-label"><span className="mdot" style={{background:m.color}}/>{m.l}</div>
                    <div className="an-macro-bar-track"><div className="an-macro-bar-fill" style={{width:`${Math.round(m.val/wMacroTotal*100)}%`,background:m.color}}/></div>
                    <div className="an-macro-bar-val">{Math.round(m.val/wMacroTotal*100)}%</div>
                  </div>
                ))}
              </div>
            )}
            {dwd.length===0&&<div style={{textAlign:"center",padding:"24px 0",color:"var(--mu)",fontSize:".86rem"}}>Log meals this week to see analytics</div>}
          </>
        )}

        {view==="90"&&(
          <>
            <div className="an-90-stats">
              <div className="an-week-stat"><div className="an-week-stat-val">{d90avg.toLocaleString()}</div><div className="an-week-stat-lbl">Daily avg</div></div>
              <div className="an-week-stat"><div className="an-week-stat-val">{d90logged.length}</div><div className="an-week-stat-lbl">Days logged</div></div>
              <div className="an-week-stat"><div className="an-week-stat-val">{d90otc}</div><div className="an-week-stat-lbl">On target</div></div>
            </div>
            <div className="an-90-legend">
              {[{c:"#c8f135",l:"On target"},{c:"#ff4d4d",l:"Over goal"},{c:"var(--m2)",l:"Under goal"},{c:"var(--s2)",l:"Not logged"}].map(x=>(
                <span key={x.l} className="an-90-legend-item"><span className="an-90-legend-dot" style={{background:x.c}}/>{x.l}</span>
              ))}
            </div>
            <div className="an-90-wrap">
              <div style={{display:"flex",alignItems:"flex-end",gap:2,minWidth:"max-content"}}>
                {d90.map((d,i)=>{
                  const h=d.has?Math.max(6,Math.round((d.total/d90max)*76)):4;
                  return(
                    <div key={i} style={{display:"flex",flexDirection:"column",alignItems:"center"}}>
                      {i===0||d.date.getDate()===1?(
                        <div style={{fontSize:".58rem",color:"var(--mu)",marginBottom:3,writingMode:"horizontal-tb",whiteSpace:"nowrap",textAlign:"center"}}>
                          {d.date.toLocaleDateString(undefined,{month:"short"})}
                        </div>
                      ):<div style={{height:14}}/>}
                      <div
                        className="an-day-bar"
                        style={{height:h,background:barColor(d)}}
                        title={d.date.toLocaleDateString()+": "+(d.has?d.total+" kcal":"not logged")}
                        onClick={()=>d.has&&setActiveDay(d.date)}
                      />
                      {d.date.getDate()===1||i===0?(
                        <div style={{fontSize:".55rem",color:"var(--mu)",marginTop:3,whiteSpace:"nowrap"}}>{d.date.getDate()}</div>
                      ):<div style={{height:12}}/>}
                    </div>
                  );
                })}
              </div>
            </div>
            {d90logged.length===0&&<div style={{textAlign:"center",padding:"24px 0",color:"var(--mu)",fontSize:".86rem"}}>Log meals to see your 90-day history</div>}
          </>
        )}

        {activeDay&&(
          <div style={{background:"var(--s2)",borderRadius:14,padding:"12px 14px",marginTop:12,animation:"fadeIn .2s ease"}}>
            <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:6}}>
              <div style={{fontFamily:"'Syne',sans-serif",fontWeight:700,fontSize:".88rem"}}>{activeDay.toLocaleDateString(undefined,{weekday:"long",month:"short",day:"numeric"})}</div>
              <button style={{background:"none",border:"none",color:"var(--mu)",fontSize:".8rem",cursor:"pointer"}} onClick={()=>setActiveDay(null)}>✕</button>
            </div>
            {(()=>{
              const k=dayKey(activeDay),meals=hist[k]?.meals||[],t=sumM(meals),g=hist[k]?.goal||goal;
              return meals.length===0?<div style={{color:"var(--mu)",fontSize:".82rem"}}>No meals logged this day.</div>:(
                <div>
                  <div style={{display:"flex",justifyContent:"space-between",marginBottom:8}}>
                    <span style={{fontSize:".82rem",color:"var(--mu)"}}>{t.total.toLocaleString()} kcal / {g.toLocaleString()} goal</span>
                    <span style={{fontSize:".78rem",fontWeight:600,color:t.total>g*1.05?"var(--re)":t.total>=g*.85?"var(--at)":"var(--mu)"}}>{t.total>g*1.05?"Over":"On track"}</span>
                  </div>
                  {meals.map(m=><div key={m.id} style={{display:"flex",justifyContent:"space-between",fontSize:".78rem",color:"var(--m2)",padding:"3px 0",borderBottom:"1px solid var(--bd)"}}><span>{m.items?.map(i=>i.name).join(", ").slice(0,40)}</span><span style={{fontWeight:600,color:"var(--tx)",flexShrink:0,marginLeft:8}}>{m.total} kcal</span></div>)}
                </div>
              );
            })()}
          </div>
        )}

        <div className="r-acts" style={{marginTop:16}}>
          <button className="btn-act1" onClick={onClose}>Done</button>
        </div>
      </div>
    </div>
  );
}

// ─── COMMUNITY / DISCOVER ─────────────────────────────────────────────────────
function PostCard({post, currentUserId, onReact, onDelete, theme}){
  const [showReactions, setShowReactions] = useState(false);
  const initials = post.name?.split(' ').map(n=>n[0]).slice(0,2).join('').toUpperCase()||'?';
  const timeAgo = t => {
    const d = Math.floor((Date.now()-new Date(t).getTime())/1000);
    if(d<60)return d+'s';if(d<3600)return Math.floor(d/60)+'m';if(d<86400)return Math.floor(d/3600)+'h';
    return Math.floor(d/86400)+'d';
  };
  const reactionTotals = post.reaction_counts||{};
  const totalReactions = Object.values(reactionTotals).reduce((s,n)=>s+(n||0),0);

  return(
    <div className={`post-card ${post.is_archived?"archived":""}`}>
      <div className="post-hd">
        <div className="post-av">
          {post.picture?<img src={post.picture} alt={post.name}/>:initials}
        </div>
        <div className="post-user">
          <div className="post-name">{post.name}</div>
          <div className="post-handle">@{post.handle||'—'}</div>
        </div>
        <div className="post-time">{timeAgo(post.created_at)}</div>
        {post.is_archived&&<span className="post-archived-badge">Archived</span>}
        {post.user_id===currentUserId&&<button className="post-del" onClick={()=>onDelete(post.id)}>×</button>}
      </div>

      {post.text&&<div className="post-text">{post.text}</div>}

      {post.post_type==='image'&&post.image_url&&(
        <img className="post-img" src={post.image_url} alt="" loading="lazy"/>
      )}

      {post.post_type==='scan'&&post.scan_data&&(
        <div className="post-scan-card">
          <span className="post-scan-em">{post.scan_data.emoji||'🍽️'}</span>
          <div className="post-scan-inf">
            <div className="post-scan-name">{post.scan_data.name||'Meal'}</div>
            <div className="post-scan-stats">
              <span>P {post.scan_data.protein||0}g</span>
              <span>C {post.scan_data.carbs||0}g</span>
              <span>F {post.scan_data.fat||0}g</span>
            </div>
          </div>
          <div className="post-scan-kcal">{post.scan_data.calories||0}<small style={{fontSize:".6rem",color:"var(--mu)",fontWeight:400}}> kcal</small></div>
        </div>
      )}

      <div className="reactions-bar">
        {REACTIONS.map(r=>{
          const cnt = reactionTotals[r.id]||0;
          const ismine = post.my_reaction===r.id;
          return(
            <button key={r.id} className={`reaction-btn ${ismine?"mine":""}`}
              onClick={()=>onReact(post.id, r.id)}
              title={r.label}>
              {r.emoji}{cnt>0&&<span className="cnt">{cnt}</span>}
            </button>
          );
        })}
        {totalReactions>0&&<span style={{fontSize:".72rem",color:"var(--mu)",marginLeft:"auto"}}>{totalReactions} reaction{totalReactions!==1?"s":""}</span>}
      </div>
    </div>
  );
}

function PostComposer({user, lastScan, onPost, postCounts, theme}){
  const [text,setText] = useState("");
  const [attachScan,setAttachScan] = useState(false);
  const [imgFile,setImgFile] = useState(null);
  const [imgPreview,setImgPreview] = useState(null);
  const [posting,setPosting] = useState(false);
  const imgRef = useRef(null);

  const handleImage = e => {
    const f = e.target.files[0]; if(!f)return;
    const r = new FileReader();
    r.onload = ev => { setImgPreview(ev.target.result); setImgFile(f); };
    r.readAsDataURL(f); e.target.value="";
  };

  const canPost = (text.trim()||attachScan||imgFile)&&!posting;
  const imageWarn = postCounts?.image_count>=45;
  const totalWarn = postCounts?.total_count>=240;

  const submit = async () => {
    if(!canPost)return;
    setPosting(true);
    try{
      let post_type='text', image_url=null, scan_data=null;
      if(imgFile){ post_type='image'; image_url=imgPreview; } // demo: use preview URL
      if(attachScan&&lastScan&&!imgFile){
        post_type='scan';
        scan_data={name:lastScan.items?.[0]?.name||'Meal', calories:lastScan.total, protein:lastScan.items?.[0]?.protein||0, carbs:lastScan.items?.[0]?.carbs||0, fat:lastScan.items?.[0]?.fat||0, emoji:lastScan.items?.[0]?.emoji||'🍽️'};
      }
      const token=getToken();
      if(token&&API_URL!=="YOUR_WORKER_URL_HERE"){
        const r=await api("/api/posts",{method:"POST",body:JSON.stringify({text:text.trim(),image_url,scan_data,post_type})});
        if(r.warnings?.length) r.warnings.forEach(w=>setTimeout(()=>alert(w),300));
      }
      onPost({id:Date.now().toString(),user_id:user?.id,name:user?.name,handle:user?.profile?.handle,picture:user?.picture,text:text.trim(),image_url:imgPreview,scan_data,post_type,is_archived:0,reaction_counts:{},my_reaction:null,total_reactions:0,created_at:new Date().toISOString()});
      setText(""); setAttachScan(false); setImgFile(null); setImgPreview(null);
    }catch{}
    setPosting(false);
  };

  return(
    <div>
      <div className="composer">
        <textarea className="composer-ta" placeholder="What's on your mind? Share a win, a meal, a thought…" value={text} onChange={e=>setText(e.target.value)} rows={3} maxLength={500}/>
        {imgPreview&&<img src={imgPreview} style={{width:"100%",borderRadius:10,maxHeight:160,objectFit:"cover",marginBottom:8}} alt=""/>}
        {attachScan&&lastScan&&<div className="post-scan-card" style={{marginBottom:8}}><span className="post-scan-em">{lastScan.items?.[0]?.emoji||'🍽️'}</span><div className="post-scan-inf"><div className="post-scan-name">{lastScan.items?.[0]?.name||'Last scan'}</div></div><div className="post-scan-kcal">{lastScan.total} kcal</div></div>}
        <div className="composer-ft">
          <div className="composer-actions">
            <input ref={imgRef} type="file" accept="image/*" style={{display:"none"}} onChange={handleImage}/>
            <button className="composer-ic-btn" onClick={()=>imgRef.current?.click()} title="Attach photo">
              <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
            </button>
            {lastScan&&<button className={`composer-ic-btn ${attachScan?"":""}`} style={attachScan?{borderColor:"var(--ac)",color:"var(--at)"}:{}} onClick={()=>{setAttachScan(s=>!s);setImgFile(null);setImgPreview(null);}} title="Attach last meal scan">
              <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
            </button>}
          </div>
          <span style={{fontSize:".7rem",color:text.length>450?"var(--ye)":"var(--mu)"}}>{text.length}/500</span>
          <button className="composer-submit" onClick={submit} disabled={!canPost}>{posting?"Posting…":"Post"}</button>
        </div>
      </div>
      {(imageWarn||totalWarn)&&(
        <div className="post-limits">
          {imageWarn&&<span className="post-limit-warn">📸 {50-(postCounts?.image_count||0)} photo posts remaining</span>}
          {totalWarn&&<span className="post-limit-warn">📝 {250-(postCounts?.total_count||0)} posts remaining</span>}
        </div>
      )}
    </div>
  );
}

function LeaderboardSection({labelId,currentUserId}){
  const [board,setBoard]=useState([]);
  const [loading,setLoading]=useState(true);
  useEffect(()=>{
    if(!labelId)return;
    setLoading(true);
    if(API_URL==="YOUR_WORKER_URL_HERE"){
      // Demo data
      setBoard([
        {rank:1,medal:"🥇",name:"Alex Rivera",handle:"alex.rivera",current_streak:18,total_xp:1420,level:5,isMe:true},
        {rank:2,medal:"🥈",name:"Jordan Kim",handle:"jordan.kim",current_streak:12,total_xp:980,level:4,isMe:false},
        {rank:3,medal:"🥉",name:"Maria Santos",handle:"maria.santos",current_streak:9,total_xp:740,level:4,isMe:false},
        {rank:4,medal:null,name:"Liam Chen",handle:"liam.chen",current_streak:6,total_xp:520,level:3,isMe:false},
        {rank:5,medal:null,name:"Priya Sharma",handle:"priya.sharma",current_streak:4,total_xp:310,level:2,isMe:false},
      ]);
      setLoading(false);return;
    }
    api(`/api/labels/${labelId}/leaderboard`).then(d=>{setBoard(d);setLoading(false);}).catch(()=>setLoading(false));
  },[labelId]);
  if(loading)return <div style={{padding:"12px 0",textAlign:"center",color:"var(--mu)",fontSize:".8rem"}}>Loading leaderboard…</div>;
  if(!board.length)return null;
  return(
    <div className="lb-w" style={{marginBottom:8}}>
      {board.slice(0,10).map(u=>(
        <div key={u.rank} className={`lb-entry ${u.isMe?"me":""}`}>
          {u.medal?<span className="lb-medal">{u.medal}</span>:<span className="lb-rank">{u.rank}</span>}
          <div className="lb-u-av">{u.name?.split(" ").map(n=>n[0]).slice(0,2).join("")||"?"}</div>
          <div className="lb-u-info">
            <div className="lb-u-name">{u.name}{u.isMe&&<span style={{fontSize:".68rem",color:"var(--at)",marginLeft:6}}>· you</span>}</div>
            <div className="lb-u-handle">@{u.handle}</div>
          </div>
          <div className="lb-streak">
            <span>🔥 {u.current_streak}d</span>
            <small>Lv.{u.level}</small>
          </div>
        </div>
      ))}
    </div>
  );
}

function FeedScreen({theme, currentUser, showToast}){
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [page, setPage] = useState(0);
  const [hasMore, setHasMore] = useState(true);
  const [lastScan, setLastScan] = useState(null);
  const [postCounts, setPostCounts] = useState(null);

  useEffect(()=>{
    loadFeed(0);
  },[]);

  const loadFeed = async pg => {
    setLoading(true);
    try{
      if(API_URL!=="YOUR_WORKER_URL_HERE"){
        const data = await api(`/api/posts/feed?page=${pg}`);
        if(pg===0) setPosts(data); else setPosts(p=>[...p,...data]);
        setHasMore(data.length===20);
        setPage(pg);
      }else{
        // Demo posts
        setPosts([
          {id:"1",user_id:"other",name:"Jordan Kim",handle:"jordan.kim",picture:null,text:"Just hit my protein goal for the 7th day in a row 💪 Consistency is everything.",post_type:"text",is_archived:0,reaction_counts:{fire:3,strong:5},my_reaction:null,total_reactions:8,created_at:new Date(Date.now()-3600000).toISOString()},
          {id:"2",user_id:"other",name:"Maria Santos",handle:"maria.santos",picture:null,text:"Grilled chicken prep for the week done!",post_type:"scan",scan_data:{name:"Grilled Chicken Breast",calories:165,protein:31,carbs:0,fat:4,emoji:"🍗"},is_archived:0,reaction_counts:{heart:2,respect:4},my_reaction:null,total_reactions:6,created_at:new Date(Date.now()-7200000).toISOString()},
          {id:"3",user_id:"other",name:"Liam Chen",handle:"liam.chen",picture:null,text:"Morning run done ✅ 5km before breakfast feels unbeatable.",post_type:"text",is_archived:0,reaction_counts:{fire:7,inspire:3},my_reaction:"fire",total_reactions:10,created_at:new Date(Date.now()-86400000).toISOString()},
        ]);
        setHasMore(false);
      }
    }catch{} finally{setLoading(false);}
  };

  const handlePost = newPost => setPosts(p=>[newPost,...p]);

  const handleReact = async(postId, reactionType) => {
    if(!currentUser){showToast("Sign in to react","inf");return;}
    setPosts(p=>p.map(post=>{
      if(post.id!==postId)return post;
      const counts={...post.reaction_counts};
      const prev=post.my_reaction;
      if(prev===reactionType){counts[prev]=Math.max(0,(counts[prev]||0)-1);return{...post,my_reaction:null,reaction_counts:counts};}
      if(prev){counts[prev]=Math.max(0,(counts[prev]||0)-1);}
      counts[reactionType]=(counts[reactionType]||0)+1;
      return{...post,my_reaction:reactionType,reaction_counts:counts};
    }));
    if(API_URL!=="YOUR_WORKER_URL_HERE"){
      try{await api("/api/reactions",{method:"POST",body:JSON.stringify({post_id:postId,reaction_type:reactionType})});}catch{}
    }
  };

  const handleDelete = async postId => {
    if(!window.confirm("Delete this post?"))return;
    setPosts(p=>p.filter(x=>x.id!==postId));
    if(API_URL!=="YOUR_WORKER_URL_HERE"){
      try{await fetch(API_URL+"/api/posts/"+postId,{method:"DELETE",headers:{Authorization:"Bearer "+getToken()}});}catch{}
    }
    showToast("Post deleted");
  };

  return(
    <div className="sc feed-w" data-theme={theme}>
      <div className="feed-hdr">
        <div className="feed-ti">Feed</div>
      </div>

      {/* Conversations row — horizontal avatar scroll */}
      {currentUser&&(
        <div className="conv-row-w">
          <div className="conv-row">
            <div className="new-msg-chip" onClick={()=>{}}>
              <div className="new-msg-av">
                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
              </div>
              <span className="new-msg-label">Messages</span>
            </div>
            {DEMO_CONVS.map(c=>{
              const initials=n=>n?.split(" ").map(x=>x[0]).slice(0,2).join("").toUpperCase()||"?";
              return(
                <div key={c.id} className={`conv-chip ${c.my_unread>0?"unread":""}`} onClick={()=>{}}>
                  <div className="conv-chip-av-w">
                    <div className="conv-chip-av">{initials(c.other_user?.name)}</div>
                    {c.my_unread>0&&<div className="conv-chip-unread-dot"/>}
                  </div>
                  <div className="conv-chip-name">{c.other_user?.name?.split(" ")[0]}</div>
                </div>
              );
            })}
          </div>
        </div>
      )}
      {currentUser&&<PostComposer user={currentUser} lastScan={lastScan} onPost={handlePost} postCounts={postCounts} theme={theme}/>}

      {loading&&posts.length===0&&(
        <div style={{textAlign:"center",padding:40,color:"var(--mu)"}}>
          <div style={{width:32,height:32,border:"3px solid var(--s2)",borderTopColor:"var(--ac)",borderRadius:"50%",animation:"spin .7s linear infinite",margin:"0 auto 14px"}}/>
          Loading your feed…
        </div>
      )}

      {!loading&&posts.length===0&&(
        <div className="empty-feed">
          <span className="empty-feed-ic">🌱</span>
          <div className="empty-feed-ti">Your feed is empty</div>
          <div className="empty-feed-su">Follow people in your squad to see their posts here. Or be the first to post something.</div>
        </div>
      )}

      {posts.map(post=>(
        <PostCard key={post.id} post={post} currentUserId={currentUser?.id} onReact={handleReact} onDelete={handleDelete} theme={theme}/>
      ))}

      {hasMore&&!loading&&(
        <div style={{textAlign:"center",padding:"16px 0"}}>
          <button className="btn-dim" style={{width:"auto",padding:"0 24px"}} onClick={()=>loadFeed(page+1)}>Load more</button>
        </div>
      )}
    </div>
  );
}

function CommunityScreen({theme,currentUser,showToast,onViewUser}){
  const [search,setSearch]=useState("");
  const [results,setResults]=useState([]);
  const [trending,setTrending]=useState([]);
  const [newest,setNewest]=useState([]);
  const [loading,setLoading]=useState(true);
  const [activeLabel,setActiveLabel]=useState(null);
  const [labelDetail,setLabelDetail]=useState(null);
  const [breadcrumb,setBreadcrumb]=useState([]);

  useEffect(()=>{
    (async()=>{
      try{
        if(API_URL==="YOUR_WORKER_URL_HERE"){
          setTrending([{id:"l-usa",name:"USA",type:"country",member_count:0,parent_id:null},{id:"l-uk",name:"UK",type:"country",member_count:0,parent_id:null},{id:"l-india",name:"India",type:"country",member_count:0,parent_id:null}]);
          setNewest([]);
        }else{
          const d=await api("/api/discover");
          setTrending(d.trending||[]);setNewest(d.newest||[]);
        }
      }catch{}finally{setLoading(false);}
    })();
  },[]);

  useEffect(()=>{
    if(!search.trim()){setResults([]);return;}
    const t=setTimeout(async()=>{
      try{
        if(API_URL!=="YOUR_WORKER_URL_HERE"){const d=await api(`/api/labels?search=${encodeURIComponent(search)}`);setResults(d);}
        else setResults(trending.filter(l=>l.name.toLowerCase().includes(search.toLowerCase())));
      }catch{}
    },300);
    return()=>clearTimeout(t);
  },[search,trending]);

  const openLabel=async label=>{
    setActiveLabel(label.id);
    try{
      if(API_URL!=="YOUR_WORKER_URL_HERE"){const d=await api(`/api/labels/${label.id}`);setLabelDetail(d);}
      else setLabelDetail({...label,members:[],children:[],parent:null,isMember:false});
    }catch{}
    setBreadcrumb(b=>[...b.filter(x=>x.id!==label.id),label]);
  };

  const drillUp=idx=>{
    const item=breadcrumb[idx];setBreadcrumb(b=>b.slice(0,idx+1));
    if(item)openLabel(item);else{setActiveLabel(null);setLabelDetail(null);setBreadcrumb([]);}
  };

  const displayList=search.trim()?results:activeLabel?labelDetail?.children||[]:trending;

  return(
    <div className="sc comm-w" data-theme={theme}>
      <div className="comm-hdr">
        <div className="comm-ti">Discover</div>
      </div>

      <div className="search-bar">
        <svg className="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
        <input className="search-input" placeholder="Search labels, places, orgs…" value={search} onChange={e=>setSearch(e.target.value)}/>
      </div>

      {breadcrumb.length>0&&!search&&(
        <div className="breadcrumb">
          <button className="bc-item" onClick={()=>{setActiveLabel(null);setLabelDetail(null);setBreadcrumb([]);}}>All</button>
          {breadcrumb.map((b,i)=><span key={b.id} style={{display:"flex",alignItems:"center",gap:4}}><span className="bc-sep">›</span><button className="bc-item" style={i===breadcrumb.length-1?{color:"var(--tx)",fontWeight:600}:{}} onClick={()=>drillUp(i)}>{b.name}</button></span>)}
        </div>
      )}

      {/* Label detail */}
      {activeLabel&&labelDetail&&(
        <div style={{padding:"0 24px",marginBottom:18}}>
          <div style={{background:"var(--s1)",border:"1px solid var(--bd)",borderRadius:16,padding:16,marginBottom:14,boxShadow:"var(--sh)"}}>
            <div style={{display:"flex",justifyContent:"space-between",alignItems:"flex-start"}}>
              <div>
                <div style={{fontFamily:"'Syne',sans-serif",fontWeight:700,fontSize:"1.15rem",color:"var(--tx)"}}>{labelDetail.name}</div>
                <div style={{fontSize:".75rem",color:"var(--mu)",marginTop:2}}>{labelDetail.member_count} members · {labelDetail.type}</div>
              </div>
              {currentUser&&<button className={labelDetail.isMember?"btn-unfollow":"btn-follow"} onClick={async()=>{try{if(labelDetail.isMember){await api(`/api/user-labels/${labelDetail.id}`,{method:"DELETE"});setLabelDetail(d=>({...d,isMember:false,member_count:Math.max(0,d.member_count-1)}));showToast("Left label");}else{await api("/api/user-labels",{method:"POST",body:JSON.stringify({label_id:labelDetail.id})});setLabelDetail(d=>({...d,isMember:true,member_count:d.member_count+1}));showToast("Joined!");}}catch{showToast("Connect your backend first","inf");}}}>{labelDetail.isMember?"Leave":"Join"}</button>}
            </div>
          </div>
          {labelDetail.members?.length>0&&<><div className="section-head" style={{padding:0,marginBottom:10}}><div className="section-ti">Leaderboard 🏆</div></div><LeaderboardSection labelId={activeLabel} currentUserId={currentUser?.id}/><div className="section-head" style={{padding:0,marginBottom:10,marginTop:14}}><div className="section-ti">Members</div></div><div className="user-list" style={{padding:0,marginBottom:14}}>{labelDetail.members.slice(0,8).map(u=><div key={u.id} className="user-row" onClick={()=>onViewUser(u.id)}><Avatar user={u} size={40}/><div className="user-info"><div className="user-name">{u.name}</div><div className="user-bio">{u.bio||"CalCheckAI member"}</div></div></div>)}</div></>}
        </div>
      )}

      {/* Label grid */}
      {!search&&<div className="section-head"><div className="section-ti">{activeLabel?"Sub-labels":"Trending labels"}</div></div>}
      {search&&<div className="section-head"><div className="section-ti">Search results</div></div>}

      <div className="label-grid">
        {loading&&[1,2,3,4].map(i=><div key={i} className="label-card" style={{opacity:.4,minHeight:80,background:"var(--s2)"}}/>)}
        {!loading&&displayList.map(l=>(
          <div key={l.id} className="label-card" onClick={()=>openLabel(l)}>
            <div className="label-card-type">{l.type}</div>
            <div className="label-card-name">{l.name}</div>
            <div className="label-card-meta">{l.member_count} members</div>
          </div>
        ))}
        {!loading&&!displayList.length&&!activeLabel&&<div style={{gridColumn:"1/-1",textAlign:"center",padding:32,color:"var(--mu)",fontSize:".88rem"}}>No labels found.<br/>Sign in to create one!</div>}
      </div>

      {/* Newest members */}
      {!search&&!activeLabel&&newest.length>0&&(
        <>
          <div className="section-head" style={{marginTop:8}}><div className="section-ti">New members</div></div>
          <div className="user-list">
            {newest.map(u=>(
              <div key={u.id} className="user-row" onClick={()=>onViewUser(u.id)}>
                <Avatar user={u} size={40}/>
                <div className="user-info"><div className="user-name">{u.name}</div><div className="user-bio">{u.bio||"Just joined CalCheckAI"}</div></div>
                <div className="user-fc">{u.follower_count} followers</div>
              </div>
            ))}
          </div>
        </>
      )}
    </div>
  );
}

// ─── PROFILE SCREEN ───────────────────────────────────────────────────────────
function ConversationSheet({theme, conv, currentUserId, onClose}){
  const [msgs,setMsgs]=useState(DEMO_MSGS[conv.id]||[]);
  const [text,setText]=useState("");
  const [sending,setSending]=useState(false);
  const bottomRef=useRef(null);
  const initials=n=>n?.split(" ").map(x=>x[0]).slice(0,2).join("").toUpperCase()||"?";
  const tAgo=t=>{const d=Math.floor((Date.now()-new Date(t).getTime())/1000);if(d<60)return"just now";if(d<3600)return Math.floor(d/60)+"m";if(d<86400)return Math.floor(d/3600)+"h";return Math.floor(d/86400)+"d";};

  useEffect(()=>{bottomRef.current?.scrollIntoView({behavior:"smooth"});},[msgs]);

  const send=async()=>{
    if(!text.trim()||sending)return;
    setSending(true);
    const newMsg={id:"m"+Date.now(),sender_id:"me",text:text.trim(),created_at:new Date().toISOString()};
    setMsgs(m=>[...m,newMsg]);
    setText("");
    if(API_URL!=="YOUR_WORKER_URL_HERE"){
      try{await api("/api/messages",{method:"POST",body:JSON.stringify({recipient_id:conv.other_user.id,text:text.trim()})});}catch{}
    }
    setSending(false);
  };

  return(
    <div className="sc overlay" data-theme={theme} onClick={e=>e.target===e.currentTarget&&onClose()}>
      <div className="sheet" style={{paddingBottom:24}}>
        <div className="handle"/>
        <div className="conv-header">
          <div className="conv-header-av">{initials(conv.other_user?.name)}</div>
          <div>
            <div className="conv-header-name">{conv.other_user?.name}</div>
            <div className="conv-header-handle">@{conv.other_user?.handle}</div>
          </div>
        </div>
        <div className="msg-thread">
          {msgs.map(m=>(
            <div key={m.id} style={{display:"flex",flexDirection:"column",alignItems:m.sender_id==="me"||m.sender_id===currentUserId?"flex-end":"flex-start"}}>
              <div className={`msg-bubble ${m.sender_id==="me"||m.sender_id===currentUserId?"mine":"theirs"}`}>
                {m.text}
                <div className="msg-time">{tAgo(m.created_at)}</div>
              </div>
            </div>
          ))}
          <div ref={bottomRef}/>
        </div>
        <div className="msg-notice">Messages are kept for 4 weeks · Friends only</div>
        <div className="msg-input-row">
          <textarea className="msg-inp" placeholder="Message…" value={text} rows={1}
            onChange={e=>setText(e.target.value)}
            onKeyDown={e=>{if(e.key==="Enter"&&!e.shiftKey){e.preventDefault();send();}}}
            maxLength={1000}/>
          <button className="msg-send" onClick={send} disabled={!text.trim()||sending}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
          </button>
        </div>
      </div>
    </div>
  );
}

function ConversationsListSheet({theme, onSelectConv, onClose}){
  const [convs]=useState(DEMO_CONVS);
  const initials=n=>n?.split(" ").map(x=>x[0]).slice(0,2).join("").toUpperCase()||"?";
  const tAgo=t=>{const d=Math.floor((Date.now()-new Date(t).getTime())/1000);if(d<60)return"now";if(d<3600)return Math.floor(d/60)+"m";if(d<86400)return Math.floor(d/3600)+"h";return Math.floor(d/86400)+"d";};
  return(
    <div className="sc overlay" data-theme={theme} onClick={e=>e.target===e.currentTarget&&onClose()}>
      <div className="sheet">
        <div className="handle"/>
        <div className="sh-ti">Messages</div>
        <div className="sh-sub" style={{marginBottom:16}}>Friends only · kept for 4 weeks</div>
        <div className="conv-list">
          {convs.map(c=>(
            <div key={c.id} className={`conv-list-row ${c.my_unread>0?"unread":""}`} onClick={()=>onSelectConv(c)}>
              <div className="conv-list-av">{initials(c.other_user?.name)}</div>
              <div className="conv-list-inf">
                <div className="conv-list-name">{c.other_user?.name}</div>
                <div className="conv-list-preview">{c.last_message||"Start a conversation"}</div>
              </div>
              <div className="conv-list-right">
                <div className="conv-list-time">{tAgo(c.last_message_at)}</div>
                {c.my_unread>0&&<div className="conv-unread-badge">{c.my_unread}</div>}
              </div>
            </div>
          ))}
        </div>
        <button className="r-acts" style={{marginTop:14}} onClick={onClose}>
          <button className="btn-act1">Done</button>
        </button>
      </div>
    </div>
  );
}

function NotificationsSheet({theme, currentUserId, onViewUser, onClose}){
  const [visits,setVisits]=useState([]);
  const [loading,setLoading]=useState(true);

  useEffect(()=>{
    // Mark all as read when sheet opens
    if(API_URL!=="YOUR_WORKER_URL_HERE"){
      api("/api/visits").then(d=>{setVisits(d.visits||[]);setLoading(false);});
      api("/api/visits/read",{method:"POST"}).catch(()=>{});
    }else{
      // Demo data
      setVisits([
        {id:"v1",visitor_id:"u2",name:"Jordan Kim",handle:"jordan.kim",picture:null,visited_at:new Date(Date.now()-1800000).toISOString(),is_read:0},
        {id:"v2",visitor_id:"u3",name:"Maria Santos",handle:"maria.santos",picture:null,visited_at:new Date(Date.now()-7200000).toISOString(),is_read:0},
        {id:"v3",visitor_id:"u4",name:"Liam Chen",handle:"liam.chen",picture:null,visited_at:new Date(Date.now()-86400000).toISOString(),is_read:1},
        {id:"v4",visitor_id:"u5",name:"Priya Sharma",handle:"priya.sharma",picture:null,visited_at:new Date(Date.now()-172800000).toISOString(),is_read:1},
      ]);
      setLoading(false);
    }
  },[]);

  const [privError,setPrivError]=useState(null);
  const initials=name=>name?.split(" ").map(n=>n[0]).slice(0,2).join("").toUpperCase()||"?";
  const timeAgo=t=>{const d=Math.floor((Date.now()-new Date(t).getTime())/1000);if(d<60)return "just now";if(d<3600)return Math.floor(d/60)+"m ago";if(d<86400)return Math.floor(d/3600)+"h ago";return Math.floor(d/86400)+"d ago";};

  const handleVisitTap=async(visit)=>{
    // Navigate to visitor's profile — this itself triggers a visit notification back to them
    onViewUser&&onViewUser(visit.visitor_id);
    onClose();
  };

  return(
    <div className="sc overlay" data-theme={theme} onClick={e=>e.target===e.currentTarget&&onClose()}>
      <div className="sheet">
        <div className="handle"/>
        <div className="sh-ti">Profile Visitors</div>
        <div className="sh-su" style={{marginBottom:16}}>Who visited your profile recently</div>
        {loading&&<div style={{textAlign:"center",padding:32,color:"var(--mu)"}}>Loading…</div>}
        {!loading&&visits.length===0&&(
          <div className="notif-empty">
            👀 No profile visits yet<br/>
            <small>Share your profile to get visitors</small>
          </div>
        )}
        {!loading&&visits.length>0&&(
          <div className="notif-sheet">
            {visits.map(v=>(
              <div key={v.id} className={`notif-row ${!v.is_read?"unread":""}`} onClick={()=>handleVisitTap(v)}>
                <div className="notif-av">
                  {v.picture?<img src={v.picture} alt={v.name}/>:initials(v.name)}
                </div>
                <div className="notif-inf">
                  <div className="notif-text"><strong>{v.name}</strong> visited your profile</div>
                  <div className="notif-time">@{v.handle||"—"} · {timeAgo(v.visited_at)}</div>
                </div>
                {!v.is_read&&<div className="notif-dot"/>}
              </div>
            ))}
          </div>
        )}
        <div className="r-acts" style={{marginTop:12}}>
          <button className="btn-act1" onClick={onClose}>Done</button>
        </div>
      </div>
    </div>
  );
}

function FollowListSheet({theme, userId, type, currentUserId, onViewUser, onClose}){
  const [list,setList]=useState([]);
  const [loading,setLoading]=useState(true);
  const [followStates,setFollowStates]=useState({});

  useEffect(()=>{
    if(!userId)return;
    setLoading(true);
    if(API_URL!=="YOUR_WORKER_URL_HERE"){
      api(`/api/users/${userId}/${type}`)
        .then(d=>{ setList(d); setLoading(false); })
        .catch(e=>{
          setPrivError(e.message||"This list is private");
          setLoading(false);
        });
    }else{
      // Demo data
      const demo=[
        {id:"u2",name:"Jordan Kim",handle:"jordan.kim",picture:null,bio:"Running 🏃 · NYC Marathon finisher",follower_count:1203},
        {id:"u3",name:"Maria Santos",handle:"maria.santos",picture:null,bio:"Yoga + clean eating 🌿",follower_count:432},
        {id:"u4",name:"Liam Chen",handle:"liam.chen",picture:null,bio:"Marathon runner 🏅",follower_count:891},
        {id:"u5",name:"Priya Sharma",handle:"priya.sharma",picture:null,bio:"HIIT & meal prep 👑",follower_count:267},
      ];
      setList(demo);setLoading(false);
    }
  },[userId,type]);

  const toggleFollow=async(targetId,targetHandle)=>{
    const isF=followStates[targetId];
    setFollowStates(s=>({...s,[targetId]:!isF}));
    if(API_URL!=="YOUR_WORKER_URL_HERE"){
      try{
        if(isF) await api(`/api/follows/${targetId}`,{method:"DELETE"});
        else await api("/api/follows",{method:"POST",body:JSON.stringify({following_id:targetId})});
      }catch{ setFollowStates(s=>({...s,[targetId]:isF})); }
    }
  };

  const initials=name=>name?.split(" ").map(n=>n[0]).slice(0,2).join("").toUpperCase()||"?";
  const title=type==="followers"?"Followers":"Following";

  return(
    <div className="sc overlay" data-theme={theme} onClick={e=>e.target===e.currentTarget&&onClose()}>
      <div className="sheet">
        <div className="handle"/>
        <div className="sh-ti">{title}</div>
        {loading&&<div style={{textAlign:"center",padding:32,color:"var(--mu)"}}>Loading…</div>}
        {!currentUserId&&(
          <div className="guest-follow-gate">
            <div className="guest-follow-ic">🔒</div>
            <div className="guest-follow-ti">Sign in to view {type}</div>
            <div className="guest-follow-su">Create a free account to see who follows people and build your own community.</div>
          </div>
        )}
        {privError&&currentUserId&&(
          <div className="guest-follow-gate">
            <div className="guest-follow-ic">🔒</div>
            <div className="guest-follow-ti">List is private</div>
            <div className="guest-follow-su">{privError}</div>
          </div>
        )}
        {!loading&&!privError&&list.length===0&&(
          <div className="follow-empty">
            {type==="followers"?"No followers yet.":"Not following anyone yet."}<br/>
            <small style={{opacity:.7}}>{type==="followers"?"Share your profile to get followers.":"Find people in your squad to follow."}</small>
          </div>
        )}
        {!loading&&list.length>0&&(
          <div className="follow-list">
            {list.map(u=>(
              <div key={u.id} className="follow-row" onClick={()=>{onViewUser&&onViewUser(u.id);onClose();}}>
                <div className="follow-av">{u.picture?<img src={u.picture} alt={u.name} style={{width:"100%",height:"100%",objectFit:"cover",borderRadius:"50%"}}/>:initials(u.name)}</div>
                <div className="follow-info">
                  <div className="follow-name">{u.name}</div>
                  <div className="follow-handle">@{u.handle||"—"}</div>
                  {u.bio&&<div className="follow-bio">{u.bio}</div>}
                </div>
                {u.id!==currentUserId&&(
                  <button
                    className={`follow-btn-sm ${followStates[u.id]===true?"following":followStates[u.id]===false||followStates[u.id]===undefined?"follow":"follow"}`}
                    onClick={e=>{e.stopPropagation();toggleFollow(u.id,u.handle);}}
                  >
                    {followStates[u.id]===true?"Following":"Follow"}
                  </button>
                )}
              </div>
            ))}
          </div>
        )}
        <div className="r-acts" style={{marginTop:8}}>
          <button className="btn-act1" onClick={onClose}>Done</button>
        </div>
      </div>
    </div>
  );
}

function ProfilePostsFeed({userId, currentUserId, isOwn, theme, user}){
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [lastScan, setLastScan] = useState(null);

  useEffect(()=>{
    if(!userId)return;
    setLoading(true);
    if(API_URL!=="YOUR_WORKER_URL_HERE"){
      api(`/api/users/${userId}/posts`).then(d=>{setPosts(d);setLoading(false);}).catch(()=>setLoading(false));
    }else{
      setPosts(isOwn?[
        {id:"p1",user_id:userId,name:user?.name,handle:user?.profile?.handle,picture:user?.picture,text:"Starting my fitness journey with CalCheckAI 🚀 Day 1 done!",post_type:"text",is_archived:0,reaction_counts:{fire:2,strong:3},my_reaction:null,total_reactions:5,created_at:new Date(Date.now()-86400000).toISOString()},
      ]:[]);
      setLoading(false);
    }
  },[userId]);

  const handlePost=newPost=>setPosts(p=>[newPost,...p]);
  const handleReact=async(postId,rt)=>{
    setPosts(p=>p.map(post=>{if(post.id!==postId)return post;const counts={...post.reaction_counts};const prev=post.my_reaction;if(prev===rt){counts[prev]=Math.max(0,(counts[prev]||0)-1);return{...post,my_reaction:null,reaction_counts:counts};}if(prev){counts[prev]=Math.max(0,(counts[prev]||0)-1);}counts[rt]=(counts[rt]||0)+1;return{...post,my_reaction:rt,reaction_counts:counts};}));
    if(API_URL!=="YOUR_WORKER_URL_HERE"){try{await api("/api/reactions",{method:"POST",body:JSON.stringify({post_id:postId,reaction_type:rt})});}catch{}}
  };
  const handleDelete=async postId=>{if(!window.confirm("Delete?"))return;setPosts(p=>p.filter(x=>x.id!==postId));if(API_URL!=="YOUR_WORKER_URL_HERE"){try{await fetch(API_URL+"/api/posts/"+postId,{method:"DELETE",headers:{Authorization:"Bearer "+getToken()}});}catch{}}};

  return(
    <div style={{paddingTop:18}}>
      {isOwn&&(
        <div style={{padding:"0 20px",marginBottom:4}}>
          <div style={{fontFamily:"'Syne',sans-serif",fontWeight:700,fontSize:".76rem",textTransform:"uppercase",letterSpacing:"1.2px",color:"var(--mu)",marginBottom:12}}>Posts</div>
          <PostComposer user={user} lastScan={lastScan} onPost={handlePost} postCounts={null} theme={theme}/>
        </div>
      )}
      {!isOwn&&posts.length>0&&<div style={{padding:"0 20px",fontFamily:"'Syne',sans-serif",fontWeight:700,fontSize:".76rem",textTransform:"uppercase",letterSpacing:"1.2px",color:"var(--mu)",marginBottom:12}}>Posts</div>}
      {loading&&<div style={{textAlign:"center",padding:20,color:"var(--mu)",fontSize:".82rem"}}>Loading…</div>}
      {!loading&&posts.length===0&&isOwn&&<div style={{padding:"0 20px",fontSize:".82rem",color:"var(--mu)",marginBottom:8}}>No posts yet — share your first update above.</div>}
      {posts.map(post=>(
        <PostCard key={post.id} post={post} currentUserId={currentUserId} onReact={handleReact} onDelete={handleDelete} theme={theme}/>
      ))}
    </div>
  );
}

function ProfileScreen({theme,user,userId,currentUser,isOwn,showToast,onSignOut,onUpdateUser,onViewUser,onClose,gami}){
  const [profile,setProfile]=useState(isOwn?user:null);
  const [userLabels,setUserLabels]=useState(isOwn?[]:[]);
  const [isFollowing,setIsFollowing]=useState(false);
  const [showLabelPicker,setShowLabelPicker]=useState(false);
  const [followSheet,setFollowSheet]=useState(null);
  const [privFollowing,setPrivFollowing]=useState("everyone");
  const [showSettings,setShowSettings]=useState(false);
  const [remindersOn,setRemindersOn]=useState(()=>JSON.parse(localStorage.getItem("kcal_remind_on")||"false"));
  const [reminderTimes,setReminderTimes]=useState(()=>JSON.parse(localStorage.getItem("kcal_remind_times")||'["08:00","12:30","18:30"]'));
  const [newTime,setNewTime]=useState("");
  const [notifPerm,setNotifPerm]=useState(typeof Notification!=="undefined"?Notification.permission:"default");
  const [exporting,setExporting]=useState(false);
  const [showDelConfirm,setShowDelConfirm]=useState(false);
  const [delInput,setDelInput]=useState("");
  const [deleting,setDeleting]=useState(false);
  const [exportDate,setExportDate]=useState(null);

  useEffect(()=>{
    if(isOwn&&API_URL!=="YOUR_WORKER_URL_HERE"){
      api("/api/privacy").then(d=>setPrivFollowing(d.show_following||"everyone")).catch(()=>{});
    }
  },[isOwn]);

  const toggleReminders=async()=>{
    const next=!remindersOn;
    setRemindersOn(next);
    localStorage.setItem("kcal_remind_on",JSON.stringify(next));
    if(next&&typeof Notification!=="undefined"&&Notification.permission==="default"){
      const perm=await Notification.requestPermission();
      setNotifPerm(perm);
    }
    scheduleReminders(next?reminderTimes:[]);
  };

  const addTime=()=>{
    if(!newTime||reminderTimes.includes(newTime)||reminderTimes.length>=6)return;
    const updated=[...reminderTimes,newTime].sort();
    setReminderTimes(updated);
    localStorage.setItem("kcal_remind_times",JSON.stringify(updated));
    if(remindersOn)scheduleReminders(updated);
    setNewTime("");
  };

  const removeTime=t=>{
    const updated=reminderTimes.filter(x=>x!==t);
    setReminderTimes(updated);
    localStorage.setItem("kcal_remind_times",JSON.stringify(updated));
    if(remindersOn)scheduleReminders(updated);
  };

  const scheduleReminders=times=>{
    // Clear any existing intervals
    if(window._reminderIntervals){window._reminderIntervals.forEach(clearInterval);window._reminderIntervals=[];}
    if(!times.length)return;
    // Check every minute if we should show a reminder
    const check=()=>{
      if(!JSON.parse(localStorage.getItem("kcal_remind_on")||"false"))return;
      const now=new Date();
      const hhmm=now.getHours().toString().padStart(2,"0")+":"+now.getMinutes().toString().padStart(2,"0");
      const storedTimes=JSON.parse(localStorage.getItem("kcal_remind_times")||"[]");
      if(storedTimes.includes(hhmm)){
        const lastShown=localStorage.getItem("kcal_remind_last");
        if(lastShown!==hhmm){
          localStorage.setItem("kcal_remind_last",hhmm);
          if(typeof Notification!=="undefined"&&Notification.permission==="granted"){
            new Notification("CalCheckAI 🍽️",{body:"Time to log your meal! Keep your streak going.",icon:"/icons/icon-192.png",tag:"meal-reminder"});
          }
        }
      }
    };
    const id=setInterval(check,60000);
    window._reminderIntervals=[id];
  };

  // Start reminder check on mount if enabled
  useEffect(()=>{
    if(remindersOn)scheduleReminders(reminderTimes);
    return()=>{if(window._reminderIntervals)window._reminderIntervals.forEach(clearInterval);};
  },[]);

  const exportData=async()=>{
    setExporting(true);
    try{
      if(API_URL!=="YOUR_WORKER_URL_HERE"){
        const res=await fetch(API_URL+"/api/account/export",{headers:{"Authorization":"Bearer "+token}});
        const blob=await res.blob();
        const url=URL.createObjectURL(blob);
        const a=document.createElement("a");
        a.href=url;a.download=`calcheckai-data-${new Date().toISOString().split("T")[0]}.json`;
        document.body.appendChild(a);a.click();document.body.removeChild(a);URL.revokeObjectURL(url);
      }else{
        // Demo — download placeholder
        const data={export_note:"Demo export",generated_at:new Date().toISOString(),profile:{name:profile?.name}};
        const blob=new Blob([JSON.stringify(data,null,2)],{type:"application/json"});
        const url=URL.createObjectURL(blob);
        const a=document.createElement("a");a.href=url;a.download="calcheckai-demo-export.json";
        document.body.appendChild(a);a.click();document.body.removeChild(a);URL.revokeObjectURL(url);
      }
      setExportDate(new Date().toLocaleString());
    }catch{}
    setExporting(false);
  };

  const deleteAccount=async()=>{
    if(delInput!=="DELETE")return;
    setDeleting(true);
    try{
      if(API_URL!=="YOUR_WORKER_URL_HERE"){
        await api("/api/account/delete",{method:"POST",body:JSON.stringify({reason:"user_request"})});
      }
      // Clear local data and sign out
      localStorage.clear();
      window.location.reload();
    }catch{setDeleting(false);}
  };

  const updatePrivacy=async val=>{
    if(API_URL==="YOUR_WORKER_URL_HERE")return;
    try{await api("/api/privacy",{method:"PUT",body:JSON.stringify({show_following:val})});}catch{}
  }; // null | 'followers' | 'following' 
  const [showFavs,setShowFavs]=useState(false);
  const [showAnalytics,setShowAnalytics]=useState(false);
  const [loading,setLoading]=useState(!isOwn);
  const favs=loadF();const setFavs=f=>{try{localStorage.setItem("kcal_favorites",JSON.stringify(typeof f==="function"?f(favs):f));}catch{}};

  useEffect(()=>{
    if(isOwn){
      setProfile(user);
      const saved=user?.labels||[];
      setUserLabels(saved);
      return;
    }
    (async()=>{
      try{
        if(API_URL!=="YOUR_WORKER_URL_HERE"){
          const d=await api(`/api/users/${userId||currentUser?.id}`);
          setProfile(d);setUserLabels(d.labels||[]);setIsFollowing(d.isFollowing);
        }else{setProfile(currentUser);setUserLabels([]);}
      }catch{}finally{setLoading(false);}
    })();
  },[userId,isOwn]);

  const handleFollow=async()=>{
    if(!currentUser){showToast("Sign in to follow","inf");return;}
    try{
      if(isFollowing){await api(`/api/follows/${profile.id}`,{method:"DELETE"});setIsFollowing(false);setProfile(p=>({...p,follower_count:Math.max(0,(p.follower_count||0)-1)}));showToast("Unfollowed");}
      else{await api("/api/follows",{method:"POST",body:JSON.stringify({following_id:profile.id})});setIsFollowing(true);setProfile(p=>({...p,follower_count:(p.follower_count||0)+1}));showToast("Following!");}
    }catch{showToast("Connect your backend to follow","inf");}
  };

  const joinLabel=async label=>{
    try{
      if(API_URL!=="YOUR_WORKER_URL_HERE")await api("/api/user-labels",{method:"POST",body:JSON.stringify({label_id:label.id})});
      const ul=[...userLabels.filter(l=>l.id!==label.id),{...label}];
      setUserLabels(ul);
      const fu={...user,labels:ul};onUpdateUser(fu);showToast(`Joined ${label.name}`);
    }catch{showToast("Connect backend to join labels","inf");}
  };

  const leaveLabel=async label=>{
    try{
      if(API_URL!=="YOUR_WORKER_URL_HERE")await api(`/api/user-labels/${label.id}`,{method:"DELETE"});
      const ul=userLabels.filter(l=>l.id!==label.id);
      setUserLabels(ul);onUpdateUser({...user,labels:ul});showToast(`Left ${label.name}`);
    }catch{}
  };

  const initials=profile?.name?.split(" ").map(n=>n[0]).slice(0,2).join("").toUpperCase()||"?";

  if(loading||!profile)return(
    <div className="sc prof-scr" data-theme={theme}>
      <div style={{display:"flex",alignItems:"center",justifyContent:"center",minHeight:"50vh",color:"var(--mu)"}}>Loading…</div>
    </div>
  );

  return(
    <>
      <div className="sc prof-scr" data-theme={theme}>
        {!isOwn&&onClose&&(
          <div className="prof-hdr">
            <button style={{background:"none",border:"none",color:"var(--mu)",display:"flex",alignItems:"center",gap:6,fontSize:".88rem"}} onClick={onClose}><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="15 18 9 12 15 6"/></svg>Back</button>
          </div>
        )}

        <div className="prof-cover">
          <div className="prof-av">{profile.picture?<img src={profile.picture} alt={profile.name}/>:initials}</div>
          <div className="prof-name">{profile.name}</div>
          {profile.bio&&<div className="prof-bio">{profile.bio}</div>}
          <div className="prof-stats">
            <div className="prof-stat"><div className="prof-stat-n">{userLabels.length}</div><div className="prof-stat-l">Squad</div></div>
            <div className="prof-stat" style={{cursor:"pointer"}} onClick={()=>setFollowSheet("followers")}>
              <div className="prof-stat-n" style={{color:"var(--tx)"}}>{profile.follower_count||0}</div>
              <div className="prof-stat-l" style={{textDecoration:"underline",textDecorationColor:"var(--mu)",textUnderlineOffset:3}}>Followers</div>
            </div>
            <div className="prof-stat" style={{cursor:"pointer"}} onClick={()=>setFollowSheet("following")}>
              <div className="prof-stat-n" style={{color:"var(--tx)"}}>{profile.following_count||0}</div>
              <div className="prof-stat-l" style={{textDecoration:"underline",textDecorationColor:"var(--mu)",textUnderlineOffset:3}}>Following</div>
            </div>
          </div>
          <div className="prof-actions">
            {isOwn?(<><button className="prof-edit-btn">Edit profile</button><button className="prof-so-btn" onClick={onSignOut}>Sign out</button></>)
              :(<button className={isFollowing?"btn-unfollow":"btn-follow"} onClick={handleFollow}>{isFollowing?"Following":"Follow"}</button>)}
          </div>
          {isOwn&&<button className="prof-settings-btn" onClick={()=>setShowSettings(true)}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
            Settings
          </button>}
        </div>

        {/* Labels */}
        <div className="prof-section">
          <div className="prof-section-ti">Labels</div>
          <div className="label-chips">
            {userLabels.map(l=>(
              <div key={l.id} className="label-chip">
                <span>{l.name}</span>
                {isOwn&&<span className="label-chip-x" onClick={()=>leaveLabel(l)}>×</span>}
              </div>
            ))}
            {isOwn&&<button className="add-label-btn" onClick={()=>setShowLabelPicker(true)}><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Add label</button>}
            {userLabels.length===0&&!isOwn&&<div style={{fontSize:".83rem",color:"var(--mu)"}}>No labels yet</div>}
          </div>
        </div>

        {isOwn&&gami&&(
          <div className="prof-section" style={{marginTop:18}}>
            <div className="prof-section-ti">Weekly score</div>
            <div className="wk-score-card">
              <div><div className="wk-score-n" style={{color:gami.weeklyScore?.score>=70?"var(--at)":gami.weeklyScore?.score>=40?"var(--ye)":"var(--mu)"}}>{gami.weeklyScore?.score||0}</div><div className="wk-score-l">/ 100 this week</div></div>
              <div className="wk-score-sub">{gami.weeklyScore?.days_logged||0} days logged · {gami.streak?.current_streak||0} day streak · Lv.{gami.xp?.level||1} {getLevel(gami.xp?.total||0).title}
              <button className="share-btn" style={{marginTop:8,display:"inline-flex"}} onClick={()=>shareCard("weekly",{score:gami.weeklyScore?.score||0},user?.profile?.handle||user?.name?.split(" ")[0]||"")}>
                <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>
                Share score
              </button>
            </div>
            </div>
          </div>
        )}
        {isOwn&&gami&&(
          <div className="prof-section" style={{marginTop:18}}>
            <div className="prof-section-ti">Badges</div>
            <div className="badges-w">
              {(gami.badges||BADGES).map(b=>(
                <div key={b.id} className={`badge-item ${b.earned?"earned":"locked"}`}>
                  <span className="badge-em">{b.emoji}</span>
                  <span>{b.name}</span>
                </div>
              ))}
            </div>
          </div>
        )}
        {/* Profile posts feed */}
        <ProfilePostsFeed userId={profile?.id} currentUserId={currentUser?.id||user?.id} isOwn={isOwn} theme={theme} user={user}/>

        {isOwn&&(
          <div className="prof-section">
            <div className="prof-section-ti">Saved meals</div>
            {favs.length===0?<div style={{fontSize:".83rem",color:"var(--mu)"}}>No saved meals yet — star a meal to save it</div>:(
              <div className="fav-l">{favs.slice(0,5).map(f=><div key={f.id} className="fav-r"><span style={{fontSize:"1.2rem"}}>{f.emoji}</span><button className="fav-ri"><div className="fav-rn">{f.name}</div><div className="fav-rm"><span><b>{f.total}</b> kcal</span></div></button><button className="fav-rd" onClick={()=>setFavs(p=>p.filter(x=>x.id!==f.id))}><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/></svg></button></div>)}</div>
            )}
          </div>
        )}

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

      {showSettings&&(
        <div className="sc overlay" data-theme={theme} onClick={e=>e.target===e.currentTarget&&setShowSettings(false)}>
          <div className="sheet settings-sheet">
            <div className="handle"/>
            <div className="sh-ti">Settings</div>

            {/* Meal reminders */}
            <div className="set-section-ti">Meal reminders</div>
            <div className="set-row">
              <div className="set-inf">
                <div className="set-label">Remind me to log meals</div>
                <div className="set-sub">Get a notification when it's time to log</div>
              </div>
              <button className={`toggle-sw ${remindersOn?"on":"off"}`} onClick={toggleReminders}>
                <div className="toggle-knob"/>
              </button>
            </div>
            {remindersOn&&(
              <>
                {notifPerm==="denied"&&(
                  <div className="notif-perm-banner">⚠️ Notification permission was denied. Enable notifications for CalCheckAI in your device settings to receive meal reminders.</div>
                )}
                {notifPerm!=="denied"&&notifPerm!=="granted"&&(
                  <div className="notif-perm-banner">📲 Tap the toggle again to allow notifications, or check your browser settings.</div>
                )}
                <div className="time-row">
                  {reminderTimes.map(t=>(
                    <div key={t} className="time-chip on">
                      🔔 {t}
                      <span className="time-chip-del" onClick={()=>removeTime(t)}>×</span>
                    </div>
                  ))}
                </div>
                <div className="add-time-row">
                  <input type="time" className="time-inp" value={newTime} onChange={e=>setNewTime(e.target.value)}/>
                  <button className="add-time-btn" onClick={addTime} disabled={!newTime||reminderTimes.length>=6}>Add time</button>
                </div>
                <div className="reminder-note">Up to 6 reminder times. Tap a time to remove it. Reminders require the app to be open on some devices — full push notifications coming soon.</div>
              </>
            )}

            {/* Privacy settings */}
            <div className="set-section-ti">Privacy</div>
            <div className="set-row">
              <div className="set-inf">
                <div className="set-label">Followers list</div>
                <div className="set-sub">Always visible to logged-in users</div>
              </div>
              <select className="set-select" disabled style={{opacity:.5}}><option>Everyone</option></select>
            </div>
            <div className="set-row">
              <div className="set-inf">
                <div className="set-label">Following list</div>
                <div className="set-sub">Who can see who you follow</div>
              </div>
              <select className="set-select" value={privFollowing} onChange={e=>{setPrivFollowing(e.target.value);updatePrivacy(e.target.value);}}>
                <option value="everyone">Everyone</option>
                <option value="only_me">Only me</option>
              </select>
            </div>
            {privFollowing==="only_me"&&<div className="priv-warn" style={{marginBottom:8}}>⚠️ While set to Only me, you cannot view others' following lists. Friends (mutual follows) are always exempt.</div>}

            {/* Theme */}
            <div className="set-section-ti">Appearance</div>
            <div className="set-row">
              <div className="set-inf">
                <div className="set-label">Theme</div>
                <div className="set-sub">Choose your preferred look</div>
              </div>
              <div className="wt-unit-toggle" style={{borderRadius:9}}>
                {["dark","light"].map(t=><button key={t} className={`wt-unit-btn ${theme===t?"on":""}`} style={{padding:"6px 12px",fontSize:".78rem"}} onClick={()=>{if(typeof setTheme==="function")setTheme(t);localStorage.setItem("kcal_theme",t);}}>{t==="dark"?"🌙 Dark":"☀️ Light"}</button>)}
              </div>
            </div>

            {/* Privacy and Data */}
            <div className="set-section-ti">Privacy &amp; Data</div>
            <div className="set-row">
              <div className="set-inf">
                <div className="set-label">Export my data</div>
                <div className="set-sub">Download all your CalCheckAI data as JSON{exportDate&&<span style={{color:"var(--at)"}}> · Last exported {exportDate}</span>}</div>
              </div>
              <button className="set-export-btn" onClick={exportData} disabled={exporting}>{exporting?"Exporting…":"Export"}</button>
            </div>
            <div className="set-row">
              <div className="set-inf">
                <div className="set-label" style={{color:"var(--re)"}}>Delete my account</div>
                <div className="set-sub">Permanently deletes all your data. Export first.</div>
              </div>
              <button className="set-delete-btn" onClick={()=>setShowDelConfirm(true)}>Delete</button>
            </div>

            <div className="r-acts" style={{marginTop:16}}>
              <button className="btn-act1" onClick={()=>setShowSettings(false)}>Done</button>
            </div>
          </div>
        </div>
      )}
      {followSheet&&<FollowListSheet
        theme={theme}
        userId={profile?.id}
        type={followSheet}
        currentUserId={currentUser?.id||user?.id}
        onViewUser={onViewUser}
        onClose={()=>setFollowSheet(null)}
      />}
      {showDelConfirm&&(
        <div className="del-confirm-ov" onClick={e=>e.target===e.currentTarget&&setShowDelConfirm(false)}>
          <div className="del-confirm-box">
            <div className="del-confirm-ic">⚠️</div>
            <div className="del-confirm-ti">Delete account?</div>
            <div className="del-confirm-su">This permanently deletes your profile, all meal logs, posts, messages and progress data. There is no undo. Export your data first if you want a copy.</div>
            <input
              className="del-confirm-inp"
              placeholder='Type "DELETE" to confirm'
              value={delInput}
              onChange={e=>setDelInput(e.target.value)}
              autoFocus
            />
            <div className="del-confirm-acts">
              <button className="del-cancel-btn" onClick={()=>{setShowDelConfirm(false);setDelInput("");}}>Cancel</button>
              <button className="del-go-btn" onClick={deleteAccount} disabled={delInput!=="DELETE"||deleting}>{deleting?"Deleting…":"Delete forever"}</button>
            </div>
          </div>
        </div>
      )}
      {showLabelPicker&&<LabelPicker theme={theme} userLabels={userLabels} onJoin={joinLabel} onLeave={leaveLabel} onClose={()=>setShowLabelPicker(false)}/>}
    </>
  );
}

// ─── SHARED SCAN RESULT SHEET ─────────────────────────────────────────────────
function MicroToggle({mac}){
  const [open,setOpen]=useState(false);
  const hasMicro=(mac.totalFiber||0)+(mac.totalSodium||0)+(mac.totalSugar||0)>0;
  if(!hasMicro)return null;
  return(
    <>
      <button className={`micro-toggle ${open?"open":""}`} onClick={()=>setOpen(o=>!o)}>
        <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
        {open?"Hide":"See more nutrients"}
      </button>
      {open&&(
        <div className="micro-grid">
          {[
            {label:"Fiber",val:mac.totalFiber||0,unit:"g"},
            {label:"Sugar",val:mac.totalSugar||0,unit:"g"},
            {label:"Sodium",val:mac.totalSodium||0,unit:"mg"},
          ].map(m=>(
            <div key={m.label} className="micro-tile">
              <div className="micro-tile-val">{m.val}<span className="micro-tile-unit">{m.unit}</span></div>
              <div className="micro-tile-lbl">{m.label}</div>
            </div>
          ))}
        </div>
      )}
    </>
  );
}

function ScanResultSheet({scan,loading,img,isGuest,onDismiss,onAdd,onSave,saved,selCat,setSelCat,onSignIn,theme}){
  const [editMode,setEditMode]=useState(false);
  const [editItems,setEditItems]=useState(null);

  // Initialise edit items from scan
  useEffect(()=>{
    if(scan&&!scan.error&&scan.items){
      setEditItems(scan.items.map((it,i)=>({...it,_key:i+'-'+Date.now()})));
    }
  },[scan]);

  const activeItems = editMode && editItems ? editItems : scan?.items;
  const activeScan = editMode && editItems
    ? {...scan, items:editItems, total:editItems.reduce((s,i)=>s+(parseInt(i.calories)||0),0)}
    : scan;

  const updateItem=(key,field,val)=>{
    setEditItems(prev=>prev.map(it=>it._key===key?{...it,[field]:field==='calories'||field==='protein'||field==='carbs'||field==='fat'?parseInt(val)||0:val}:it));
  };
  const removeItem=key=>setEditItems(prev=>prev.filter(it=>it._key!==key));
  const addItem=()=>setEditItems(prev=>[...prev,{_key:Date.now()+'-new',emoji:'🍽️',name:'',calories:0,protein:0,carbs:0,fat:0}]);
  const applyEdits=()=>setEditMode(false);

  const editedTotal = editItems ? editItems.reduce((s,i)=>s+(parseInt(i.calories)||0),0) : scan?.total||0;
  const mac=activeScan&&!activeScan.error&&activeScan.type!=="barcode"&&activeScan.type!=="label"?sumI(activeItems||[]):{totalProtein:activeScan?.protein||0,totalCarbs:activeScan?.carbs||0,totalFat:activeScan?.fat||0};
  const isBarcode=scan?.type==="barcode";
  const isLabel=scan?.type==="label";
  return(
    <div className="sc overlay" data-theme={theme} onClick={e=>e.target===e.currentTarget&&onDismiss()}>
      <div className="sheet">
        <div className="handle"/>
        {img&&!loading&&<img src={img} alt="" className="preview"/>}
        {loading?(<div className="scanning">{img&&<img src={img} alt="" style={{width:"100%",borderRadius:16,maxHeight:200,objectFit:"cover"}}/>}<div className="spinner"/><div className="spin-t">Analysing your meal…</div></div>)
        :scan?.error?(<div className="err-st"><span className="err-ic">🤔</span><div className="err-ms">Couldn't process this. Try a different photo or description.</div><div className="r-acts" style={{justifyContent:"center"}}><button className="btn-dim" style={{flex:"none",padding:"0 28px"}} onClick={onDismiss}>Try again</button></div></div>)
        :scan?(<>
          {!isGuest&&<div className="sh-tr"><div><div className="sh-ti">{isBarcode?"Product found":isLabel?"Label scanned":"Meal detected"}</div><div className="sh-su">{isBarcode?"Per "+scan.per+" · "+scan.brand:"Review and add to today"}</div></div><button className={`sv-btn ${saved?"saved":""}`} onClick={onSave}><svg width="17" height="17" viewBox="0 0 24 24" fill={saved?"currentColor":"none"} stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg></button></div>}
          {isGuest&&<div><div className="sh-ti">{isBarcode?"Product found":isLabel?"Label scanned":"Meal detected"}</div><div className="sh-su" style={{marginBottom:20}}>{isBarcode?"Nutrition data from Open Food Facts":isLabel?"Extracted from nutrition panel":"Here's what I found"}</div></div>}

          {/* Edit mode banner */}
          {editMode&&editItems&&(
            <div className="edit-mode-banner">
              <span className="edit-mode-label">✏️ Editing your meal</span>
              <button className="edit-done-btn" onClick={applyEdits}>Done editing</button>
            </div>
          )}

          {/* Edit mode — editable item list */}
          {editMode&&editItems?(
            <>
              {editItems.map(item=>(
                <div key={item._key} className="edit-item">
                  <div className="edit-item-top">
                    <span className="edit-item-em">{item.emoji||"🍽️"}</span>
                    <input className="edit-item-name" value={item.name} placeholder="Food name" onChange={e=>updateItem(item._key,"name",e.target.value)}/>
                    <button className="edit-item-del" onClick={()=>removeItem(item._key)}>
                      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/></svg>
                    </button>
                  </div>
                  <div className="edit-macros">
                    {[{k:"calories",l:"Calories"},{k:"protein",l:"Protein"},{k:"carbs",l:"Carbs"},{k:"fat",l:"Fat"}].map(f=>(
                      <div key={f.k} className="edit-macro-field">
                        <span className="edit-macro-label">{f.l}</span>
                        <input type="number" className="edit-macro-inp" value={item[f.k]||0} min="0"
                          onChange={e=>updateItem(item._key,f.k,e.target.value)}/>
                      </div>
                    ))}
                  </div>
                </div>
              ))}
              <button className="add-item-btn" onClick={addItem}>
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
                Add missing item
              </button>
              <div className="edit-total">
                <span className="edit-total-label">Updated total</span>
                <span className="edit-total-val">{editedTotal.toLocaleString()} kcal</span>
              </div>
            </>
          ):(
            <>
              {/* Normal view mode with edit trigger */}
              {!isGuest&&!isBarcode&&!isLabel&&(
                <button className="edit-trigger-btn" onClick={()=>setEditMode(true)}>
                  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
                  Edit scan
                </button>
              )}
              <div className="r-items">{(activeItems||[]).map((item,i)=><div key={i} className="r-item"><div className="r-il"><span className="r-em">{item.emoji}</span><div className="r-blk"><span className="r-nm">{item.name}</span>{(item.protein||item.carbs||item.fat)&&<span className="r-mac"><span>P {item.protein||0}g</span><span>C {item.carbs||0}g</span><span>F {item.fat||0}g</span></span>}</div></div><span className="r-cal">{item.calories} kcal</span></div>)}</div>
              <div className="r-tot"><span className="r-tl">Total calories</span><span className="r-tn">{(activeScan?.total||0).toLocaleString()} kcal</span></div>
              {!isGuest&&(
            <>
              <div className="r-mr" style={{marginBottom:8}}><div className="r-mc"><span className="r-mcl"><span className="mdot" style={{background:"var(--pr)"}}/>Protein</span><span className="r-mcn">{mac.totalProtein}<span>g</span></span></div><div className="r-mc"><span className="r-mcl"><span className="mdot" style={{background:"var(--ca)"}}/>Carbs</span><span className="r-mcn">{mac.totalCarbs}<span>g</span></span></div><div className="r-mc"><span className="r-mcl"><span className="mdot" style={{background:"var(--fa)"}}/>Fat</span><span className="r-mcn">{mac.totalFat}<span>g</span></span></div></div>
              {(mac.totalFiber>0||mac.totalSodium>0||mac.totalSugar>0)&&(
                <>
                  <MicroToggle mac={mac}/>
                </>
              )}
            </>
          )}
            </>
          )}

          {!editMode&&!isGuest&&<><div className="cat-lbl">Add to</div><div className="cat-chips">{CATS.map(c=><button key={c.id} className={`cat-chip ${selCat===c.id?"on":""}`} onClick={()=>setSelCat(c.id)}><span className="cat-chip-em">{c.emoji}</span><span>{c.label}</span></button>)}</div></>}
          {!editMode&&scan.notes&&<div className="r-notes">💡 {scan.notes}</div>}
          {isGuest?(<><div className="nudge"><div className="nudge-ti"><svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>Want to track this?</div><div className="nudge-bo">Sign in free to log this meal, build your streak, earn XP and join your local squad.</div><button className="nudge-btn" onClick={()=>{onDismiss();onSignIn();}}>Sign in — it's free</button></div><div className="r-acts" style={{marginTop:12}}><button className="btn-dim" style={{flex:1}} onClick={onDismiss}>Dismiss</button></div></>)
            :(<div className="r-acts"><button className="btn-dim" onClick={onDismiss}>Dismiss</button><button className="btn-act" onClick={()=>onAdd(editMode&&editItems?{...scan,items:editItems,total:editedTotal}:null)}>Add to today</button></div>)}
        </>):null}
      </div>
    </div>
  );
}

// ─── HELPERS ──────────────────────────────────────────────────────────────────
function DayDetail({date,hist,goal,theme,onClose}){
  const k=dayKey(date),meals=hist[k]?.meals||[],g=hist[k]?.goal||goal,t=sumM(meals);
  const tgt={protein:Math.round(g*.25/4),carbs:Math.round(g*.5/4),fat:Math.round(g*.25/9)};
  const byCat=CATS.map(c=>({...c,meals:meals.filter(m=>(m.category||"snacks")===c.id),total:meals.filter(m=>(m.category||"snacks")===c.id).reduce((s,x)=>s+(x.total||0),0)}));
  return(
    <div className="sc overlay" data-theme={theme} onClick={e=>e.target===e.currentTarget&&onClose()}>
      <div className="sheet">
        <div className="handle"/>
        <div style={{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:16}}>
          <div><div style={{fontFamily:"'Syne',sans-serif",fontWeight:800,fontSize:"1.3rem",letterSpacing:"-.5px"}}>{dayFmt(date)}</div><div style={{fontSize:".76rem",color:"var(--mu)",marginTop:2}}>{date.toLocaleDateString(undefined,{month:"long",day:"numeric",year:"numeric"})}</div></div>
          <div style={{fontFamily:"'Syne',sans-serif",fontWeight:800,fontSize:"1.4rem"}}>{t.total.toLocaleString()}<small style={{fontSize:".68rem",color:"var(--mu)",fontWeight:400,marginLeft:2}}>kcal</small></div>
        </div>
        {meals.length===0?<div style={{textAlign:"center",padding:"32px 0",color:"var(--mu)"}}>🌙 No meals logged.</div>:(
          <><div className="r-mr" style={{marginBottom:20}}><div className="r-mc"><span className="r-mcl"><span className="mdot" style={{background:"var(--pr)"}}/>Protein</span><span className="r-mcn">{t.protein}<span>g/{tgt.protein}</span></span></div><div className="r-mc"><span className="r-mcl"><span className="mdot" style={{background:"var(--ca)"}}/>Carbs</span><span className="r-mcn">{t.carbs}<span>g/{tgt.carbs}</span></span></div><div className="r-mc"><span className="r-mcl"><span className="mdot" style={{background:"var(--fa)"}}/>Fat</span><span className="r-mcn">{t.fat}<span>g/{tgt.fat}</span></span></div></div>
            {byCat.filter(c=>c.meals.length>0).map(cat=>(
              <div key={cat.id} style={{marginBottom:16}}>
                <div style={{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:9,padding:"0 3px"}}><div style={{display:"flex",alignItems:"center",gap:7,fontFamily:"'Syne',sans-serif",fontWeight:700,fontSize:".76rem",textTransform:"uppercase",letterSpacing:"1px"}}><span>{cat.emoji}</span><span>{cat.label}</span><span className="cat-cnt">{cat.meals.length}</span></div><span style={{fontFamily:"'Syne',sans-serif",fontWeight:700,fontSize:".82rem",color:"var(--mu)"}}>{cat.total.toLocaleString()} kcal</span></div>
                {cat.meals.map(m=><div key={m.id} style={{background:"var(--s2)",borderRadius:11,padding:"9px 11px",marginBottom:5,display:"flex",alignItems:"center",justifyContent:"space-between",gap:9}}><div style={{display:"flex",flexDirection:"column",gap:2,minWidth:0,flex:1}}><div style={{fontSize:".8rem",color:"var(--tx)",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}}>{m.items.map(i=>`${i.emoji} ${i.name}`).join(" · ")}</div><div style={{fontSize:".65rem",color:"var(--mu)"}}>{m.time}</div></div><div style={{fontFamily:"'Syne',sans-serif",fontWeight:700,fontSize:".82rem",color:"var(--at)",flexShrink:0}}>{m.total} kcal</div></div>)}
              </div>
            ))}
          </>
        )}
        <div className="r-acts"><button className="btn-act1" onClick={onClose}>Close</button></div>
      </div>
    </div>
  );
}

function FavsSheet({favs,setFavs,quickAdd,theme,onClose}){
  return(
    <div className="sc overlay" data-theme={theme} onClick={e=>e.target===e.currentTarget&&onClose()}>
      <div className="sheet">
        <div className="handle"/>
        <div className="sh-ti">Your favorites</div>
        <div className="sh-su">Tap to add · trash to remove</div>
        {favs.length===0?<div style={{textAlign:"center",padding:"28px 0",color:"var(--mu)",fontSize:".88rem"}}>⭐ No favorites yet.</div>:(
          <div className="fav-l">{favs.map(f=><div key={f.id} className="fav-r"><span style={{fontSize:"1.2rem"}}>{f.emoji}</span><button className="fav-ri" onClick={()=>{quickAdd(f);onClose();}}><div className="fav-rn">{f.name}</div><div className="fav-rm"><span><b>{f.total}</b> kcal</span><span>P {f.totalProtein}g</span><span>C {f.totalCarbs}g</span><span>F {f.totalFat}g</span></div></button><button className="fav-rd" onClick={()=>setFavs(p=>p.filter(x=>x.id!==f.id))}><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/></svg></button></div>)}</div>
        )}
        <div className="r-acts"><button className="btn-act1" onClick={onClose}>Done</button></div>
      </div>
    </div>
  );
}
