/* Customer Mobile View — Settings → Customer Experience.
   Split-screen: left config, right LIVE phone preview. The phone renders
   CpPublicPage, the exact same component + data the public QR page uses. */
(function () {
  const { useState: cvUseState, useMemo: cvUseMemo, useEffect: cvUseEffect } = React;
  const CP = window.CustomerPortal;

  /* ── list every publishable offering across all types ── */
  function cvOfferings() {
    const out = [];
    try {
      const ofs = (typeof ofLoad === "function") ? ofLoad() : (window.__offerings || []);
      (ofs || []).forEach((o) => out.push({ kind: (o.type || "product").toLowerCase(), item: o, id: o.id, name: o.name || o.title, type: o.type || "Product" }));
    } catch (e) {}
    // recipes as menu items too
    try { (window.RECIPES || []).filter((r) => r.status === "published" || r.status === "approved").slice(0, 8).forEach((r) => out.push({ kind: "recipe", item: r, id: r.id, name: r.name, type: "Menu Item" })); } catch (e) {}
    return out;
  }

  /* ── deterministic decorative QR (points to the public URL) ── */
  function QrSvg({ text, size }) {
    size = size || 132;
    const n = 21; let h = 0; for (let i = 0; i < text.length; i++) h = (h * 31 + text.charCodeAt(i)) >>> 0;
    const cell = size / n;
    const rects = [];
    const on = (x, y) => {
      // finder patterns in 3 corners
      const fp = (cx, cy) => (x >= cx && x < cx + 7 && y >= cy && y < cy + 7 && (x === cx || x === cx + 6 || y === cy || y === cy + 6 || (x >= cx + 2 && x <= cx + 4 && y >= cy + 2 && y <= cy + 4)));
      if (fp(0, 0) || fp(n - 7, 0) || fp(0, n - 7)) return true;
      if (x < 8 && y < 8) return false; if (x > n - 9 && y < 8) return false; if (x < 8 && y > n - 9) return false;
      return ((h >> ((x * 3 + y * 7) % 31)) & 1) === 1 && ((x + y) % 2 === 0 || (h >> (y % 5)) & 1);
    };
    for (let y = 0; y < n; y++) for (let x = 0; x < n; x++) if (on(x, y)) rects.push(React.createElement("rect", { key: x + "-" + y, x: (x * cell).toFixed(1), y: (y * cell).toFixed(1), width: cell.toFixed(1), height: cell.toFixed(1), fill: "#0F1E16" }));
    return React.createElement("svg", { width: size, height: size, viewBox: "0 0 " + size + " " + size, style: { borderRadius: 12, background: "#fff", display: "block" } }, rects);
  }

  /* ── THE SHARED PUBLIC PAGE (customer view) ── */
  function CppCarousel({ photos }) {
    const [i, setI] = cvUseState(0);
    const go = (n) => setI((n + photos.length) % photos.length);
    return (
      <div className="cpp-carousel">
        <div className="cpp-carousel-track" style={{ transform: "translateX(-" + (i * 100) + "%)" }}>
          {photos.map((p, k) => <img key={k} className="cpp-img" src={p} alt="" />)}
        </div>
        <button className="cpp-car-arrow left" onClick={() => go(i - 1)} aria-label="Previous photo"><Icon name="chevron-left" size={18} /></button>
        <button className="cpp-car-arrow right" onClick={() => go(i + 1)} aria-label="Next photo"><Icon name="chevron-right" size={18} /></button>
        <div className="cpp-car-dots">{photos.map((_, k) => <button key={k} className={"cpp-car-dot" + (k === i ? " on" : "")} onClick={() => setI(k)} aria-label={"Photo " + (k + 1)} />)}</div>
      </div>
    );
  }
  function CpPublicPage({ resolved, settings, org, state, onEditSection }) {
    const edit = (t) => (onEditSection ? { onClick: (e) => { e.stopPropagation(); onEditSection(t); }, style: { cursor: "pointer" }, title: "Edit in settings" } : {});
    const [tab, setTab] = cvUseState("nutrition");
    const [prefAllergens, setPrefAllergens] = cvUseState([]);
    const [prefsOpen, setPrefsOpen] = cvUseState(true);
    const [prefDislikes, setPrefDislikes] = cvUseState([]);
    const [sides, setSides] = cvUseState([]);
    const d = settings.display || {};
    const dark = settings.theme === "dark";
    const brand = org.brandColor || "#1F8A5B";
    const r = resolved || {};
    const a11y = org.a11y || {};
    const cppCls = "cpp" + (dark ? " dark" : "") + (a11y.textScale && a11y.textScale !== "base" ? " ts-" + a11y.textScale : "") + (a11y.highContrast ? " hc" : "") + (org.font && org.font !== "system" ? " font-" + org.font : "");
    const NUTR = [["calories", "Calories", ""], ["protein", "Protein", "g"], ["carbs", "Carbs", "g"], ["fat", "Total fat", "g"], ["satFat", "Saturated fat", "g"], ["fiber", "Fibre", "g"], ["sugar", "Sugars", "g"], ["sodium", "Sodium", "mg"]];

    // Branded loading splash (shown briefly on scan, before the page content).
    const [loading, setLoading] = cvUseState(state === "published");
    cvUseEffect(() => { if (state !== "published") { setLoading(false); return; } setLoading(true); const t = setTimeout(() => setLoading(false), 1400); return () => clearTimeout(t); }, [state, r.id]);
    if (loading && state === "published") {
      const wl = org.whiteLabel && org.logo;
      return (
        <div className={"cpp cpp-loading" + (dark ? " dark" : "")} style={{ background: wl ? "#fff" : `linear-gradient(160deg, ${brand}, ${brand}CC)` }}>
          <div className="cpp-load-inner">
            {wl ? <img className="cpp-load-logo" src={org.logo} alt="" /> : <span className="cpp-load-orb"><span className="cpp-load-leaf">🍃</span></span>}
            <div className="cpp-load-spin" style={wl ? { borderTopColor: brand } : null} />
            <div className="cpp-load-txt" style={wl ? { color: "#1a2b22" } : null}>{wl ? "Loading nutrition & allergen information from " + (org.orgName || "us") : "Loading your meal information"}</div>
            {!wl && <div className="cpp-load-brand">NutriDMS</div>}
          </div>
        </div>
      );
    }

    // non-published states
    if (state === "unavailable") return <div className={"cpp" + (dark ? " dark" : "")}><div className="cpp-msg"><Icon name="pause-circle" size={30} /><h3>Item unavailable</h3><p>This item is currently paused and can't be viewed. Please check back later or ask staff.</p></div></div>;
    if (state === "expired") return <div className={"cpp" + (dark ? " dark" : "")}><div className="cpp-msg"><Icon name="qr-code" size={30} /><h3>QR code expired</h3><p>This QR code is no longer active. Ask staff for an up-to-date code.</p></div></div>;

    const missing = state === "missing";
    const val = (v, fallback) => (missing && Math.random() < 0.5) ? null : (v != null ? v : fallback);
    const has = (k) => d[k] !== false;

    const tabs = [
      has("show_nutrition") && ["nutrition", "Nutrition"],
      has("show_ingredients") && ["ingredients", "Ingredients"],
      has("show_allergens") && ["allergens", "Allergens"],
      has("show_dietary") && ["dietary", "Dietary"],
      (has("show_prep") || has("show_storage")) && ["prep", "Prep & storage"],
      ["details", "Details"],
    ].filter(Boolean);

    return (
      <div className={cppCls}>
        {state === "draft" && <div className="cpp-draftbar"><Icon name="eye-off" size={12} /> Draft preview — customers can't see these changes yet</div>}
        <div className="cpp-hero" style={{ background: org.headerStyle === "minimal" ? "transparent" : `linear-gradient(160deg, ${brand}, ${brand}CC)` }}>
          <div className="cpp-brandrow" {...edit("appearance")}>
            {org.logo ? <img className="cpp-logo" src={org.logo} alt="" /> : <span className="cpp-logo-mono" style={{ color: brand }}>{(org.orgName || "N")[0]}</span>}
            <span className="cpp-org">{org.orgName}</span>
          </div>
          {(() => {
            const photos = [r.image].concat(r.gallery || r.photos || []).filter(Boolean);
            if (!photos.length) return <div className="cpp-img cpp-img-ph"><Icon name="utensils-crossed" size={26} /></div>;
            if (photos.length === 1) return <img className="cpp-img" src={photos[0]} alt="" />;
            return <CppCarousel photos={photos} />;
          })()}
          <h1 className="cpp-name">{r.name}</h1>
          <div className="cpp-sub">{r.servings ? "Serves " + r.servings : (r.category || "")}{d.show_verified !== false && r.verifiedOn ? " · Verified " + r.verifiedOn : ""}</div>
        </div>

        {/* quick summary */}
        {(() => {
          const so = (org.sides && org.sides.options) || [];
          const add = so.filter((o) => sides.indexOf(o.id) >= 0).reduce((a, o) => ({ calories: a.calories + (+o.kcal || 0), protein: a.protein + (+o.protein || 0), carbs: a.carbs + (+o.carbs || 0), fat: a.fat + (+o.fat || 0) }), { calories: 0, protein: 0, carbs: 0, fat: 0 });
          const NUT = { calories: ["Calories", ""], protein: ["Protein", "g"], carbs: ["Carbs", "g"], fat: ["Fat", "g"], fiber: ["Fibre", "g"], sugar: ["Sugars", "g"], sodium: ["Sodium", "mg"] };
          const tiles = (org.quickTiles && org.quickTiles.length === 3) ? org.quickTiles : ["calories", "protein", "fat"];
          return (
            <div className="cpp-quick">
              {tiles.map((k) => {
                const base = +r.nutr[k] || 0; const tot = base + (add[k] || 0);
                const has = r.nutr[k] != null; const u = (NUT[k] || ["", ""])[1];
                return <div key={k} className="cpp-q"><b>{has ? tot + u : "—"}</b><span>{(NUT[k] || [k])[0]}</span></div>;
              })}
            </div>
          );
        })()}
        {has("show_dietary") && (r.dietary || []).length > 0 && (
          <div className="cpp-badges">{r.dietary.slice(0, 6).map((t) => <span key={t} className="cpp-badge" style={{ borderColor: brand, color: brand }}>{t}</span>)}</div>
        )}

        {(org.preferences && org.preferences.enabled) && !((org.moduleOff || {}).preferences) && (() => {
          const ALLERGENS = (org.preferences.allergens && org.preferences.allergens.length) ? org.preferences.allergens : ["Milk", "Eggs", "Wheat", "Soy", "Peanuts", "Tree nuts", "Fish", "Shellfish", "Sesame"];
          const DISLIKES = (org.preferences.dislikes && org.preferences.dislikes.length) ? org.preferences.dislikes : ["Mushrooms", "Onion", "Garlic", "Pork", "Spicy"];
          const contains = (r.contains || []).map((x) => String(x).toLowerCase());
          const may = (r.mayContain || []).map((x) => String(x).toLowerCase());
          const ingText = (r.ingredients || []).join(" ").toLowerCase();
          const hit = (name, pool) => pool.some((p) => p.indexOf(name.toLowerCase()) >= 0 || name.toLowerCase().indexOf(p) >= 0);
          const allgContains = prefAllergens.filter((a) => hit(a, contains));
          const allgMay = prefAllergens.filter((a) => !hit(a, contains) && hit(a, may));
          const dislikeHits = prefDislikes.filter((dk) => ingText.indexOf(dk.toLowerCase()) >= 0 || hit(dk, contains));
          const incomplete = missing && r.nutr.calories == null;
          const toggle = (v, set, list) => set(list.indexOf(v) >= 0 ? list.filter((x) => x !== v) : list.concat([v]));
          return (
            <div className="cpp-prefs">
              <div className="cpp-prefs-h" onClick={() => setPrefsOpen((o) => !o)} style={{ cursor: "pointer" }}>
                <Icon name="sliders-horizontal" size={13} /> <span style={{ flex: 1 }}>Your allergies &amp; preferences</span>
                <Icon name={prefsOpen ? "chevron-up" : "chevron-down"} size={15} />
              </div>
              {prefsOpen && <div className="cpp-prefs-inner">
              <div className="cpp-prefs-grp">
                <span className="cpp-prefs-lbl">Allergies</span>
                <div className="cpp-chips">{ALLERGENS.map((a) => <button key={a} className={"cpp-chip allg" + (prefAllergens.indexOf(a) >= 0 ? " on" : "")} onClick={() => toggle(a, setPrefAllergens, prefAllergens)}>{a}</button>)}</div>
              </div>
              {(org.preferences.dislikesEnabled !== false) && (
                <div className="cpp-prefs-grp">
                  <span className="cpp-prefs-lbl">Dislikes</span>
                  <div className="cpp-chips">{DISLIKES.map((a) => <button key={a} className={"cpp-chip" + (prefDislikes.indexOf(a) >= 0 ? " on" : "")} onClick={() => toggle(a, setPrefDislikes, prefDislikes)}>{a}</button>)}</div>
                </div>
              )}
              {(prefAllergens.length > 0 || prefDislikes.length > 0) && (
                <div className="cpp-prefwarn">
                  {allgContains.length > 0 && <div className="cpp-warn danger"><Icon name="alert-octagon" size={16} /><div><b>Contains {allgContains.join(", ")}</b><span>This item contains an allergen you selected. Do not consume if you are allergic — confirm with staff.</span></div></div>}
                  {allgMay.length > 0 && <div className="cpp-warn caution"><Icon name="alert-triangle" size={16} /><div><b>May contain {allgMay.join(", ")}</b><span>Possible cross-contact during preparation. Speak with staff before ordering.</span></div></div>}
                  {dislikeHits.length > 0 && <div className="cpp-warn notice"><Icon name="info" size={15} /><div><b>Contains {dislikeHits.join(", ")}</b><span>You marked this as a dislike (preference only, not an allergy).</span></div></div>}
                  {incomplete && <div className="cpp-warn caution"><Icon name="help-circle" size={15} /><div><b>Information incomplete</b><span>Some allergen data is unavailable. Please check with staff before ordering.</span></div></div>}
                  {allgContains.length === 0 && allgMay.length === 0 && dislikeHits.length === 0 && !incomplete && <div className="cpp-warn safe"><Icon name="check-circle-2" size={16} /><div><b>No selected allergens identified</b><span>Based on available data. Cross-contact can still occur — ask staff if unsure.</span></div></div>}
                </div>
              )}
              </div>}
            </div>
          );
        })()}

        {(org.sides && org.sides.enabled) && !((org.moduleOff || {}).sides) && (() => {
          const OPTS = (org.sides.options && org.sides.options.length) ? org.sides.options : [
            { id: "rice", name: "White rice", kcal: 200, protein: 4, carbs: 44, fat: 0 },
            { id: "salad", name: "Garden salad", kcal: 70, protein: 2, carbs: 8, fat: 4 },
            { id: "fries", name: "French fries", kcal: 320, protein: 4, carbs: 42, fat: 15 },
            { id: "veg", name: "Roasted vegetables", kcal: 110, protein: 3, carbs: 18, fat: 4 },
          ];
          const toggle = (id) => setSides(sides.indexOf(id) >= 0 ? sides.filter((x) => x !== id) : sides.concat([id]));
          const sel = OPTS.filter((o) => sides.indexOf(o.id) >= 0);
          const add = sel.reduce((a, o) => ({ kcal: a.kcal + (o.kcal || 0), protein: a.protein + (o.protein || 0), carbs: a.carbs + (o.carbs || 0), fat: a.fat + (o.fat || 0) }), { kcal: 0, protein: 0, carbs: 0, fat: 0 });
          const base = { kcal: +r.nutr.calories || 0, protein: +r.nutr.protein || 0, carbs: +r.nutr.carbs || 0, fat: +r.nutr.fat || 0 };
          return (
            <div className="cpp-sides">
              <div className="cpp-sec-h">Choose your sides</div>
              <div className="cpp-sidelist">
                {OPTS.map((o) => (
                  <button key={o.id} className={"cpp-side" + (sides.indexOf(o.id) >= 0 ? " on" : "")} onClick={() => toggle(o.id)}>
                    <span className="cpp-side-check">{sides.indexOf(o.id) >= 0 ? <Icon name="check" size={13} /> : null}</span>
                    <span className="cpp-side-nm">{o.name}</span>
                    <span className="cpp-side-kcal">+{o.kcal} kcal</span>
                  </button>
                ))}
              </div>
              {sel.length > 0 && (
                <div className="cpp-sides-calc">
                  <div className="cpp-sides-row"><span>Standard meal</span><b>{base.kcal} kcal</b></div>
                  <div className="cpp-sides-row your"><span>Your meal (+{sel.length} side{sel.length > 1 ? "s" : ""})</span><b>{base.kcal + add.kcal} kcal</b></div>
                  <div className="cpp-sides-delta">+{add.kcal} kcal · +{add.protein}g P · +{add.carbs}g C · +{add.fat}g F <em>vs standard</em></div>
                  <div className="cpp-sides-note"><Icon name="info" size={11} /> The approved standard record is unchanged; this is your customized selection.</div>
                </div>
              )}
            </div>
          );
        })()}

        {/* tabs */}
        <div className="cpp-tabs" {...(onEditSection ? { title: "Edit visible content in settings" } : {})}>{tabs.map(([id, l]) => <button key={id} className={"cpp-tab" + (tab === id ? " on" : "")} style={tab === id ? { color: brand, borderColor: brand } : null} onClick={() => setTab(id)} onDoubleClick={() => onEditSection && onEditSection("content")}>{l}</button>)}</div>

        <div className="cpp-body">
          {tab === "nutrition" && has("show_nutrition") && (
            <div>
              {has("show_energy") !== false && !((org.moduleOff || {}).energy) && (() => {
                const so = (org.sides && org.sides.options) || [];
                const sadd = so.filter((o) => sides.indexOf(o.id) >= 0).reduce((a, o) => ({ p: a.p + (+o.protein || 0), c: a.c + (+o.carbs || 0), f: a.f + (+o.fat || 0) }), { p: 0, c: 0, f: 0 });
                const p = (+r.nutr.protein || 0) + sadd.p, c = (+r.nutr.carbs || 0) + sadd.c, f = (+r.nutr.fat || 0) + sadd.f;
                const pk = p * 4, ck = c * 4, fk = f * 9, tot = pk + ck + fk;
                if (tot <= 0) return null;
                const pp = Math.round(pk / tot * 100), cp = Math.round(ck / tot * 100), fp = 100 - pp - cp;
                const segs = [["Protein", pp, "#2E6FD6", p], ["Carbs", cp, "#E0902A", c], ["Fat", fp, "#9B6BF0", f]];
                return (
                  <div className="cpp-energy">
                    <div className="cpp-sec-h">Energy distribution{sides.length > 0 && <span className="cpp-nft-note"> · incl. sides</span>}</div>
                    <div className="cpp-energy-bar">{segs.map(([l, pct, col]) => <span key={l} style={{ width: pct + "%", background: col }} title={l + " " + pct + "%"} />)}</div>
                    <div className="cpp-energy-legend">{segs.map(([l, pct, col, g]) => <span key={l} className="cpp-energy-leg"><i style={{ background: col }} />{l} <b>{pct}%</b><em>{g}g</em></span>)}</div>
                    <div className="cpp-energy-note">Educational summary of calories from each macronutrient — not a substitute for the Nutrition Facts below.</div>
                  </div>
                );
              })()}
              {has("show_nutrition_table") && (() => {
                const so = (org.sides && org.sides.options) || [];
                const add = so.filter((o) => sides.indexOf(o.id) >= 0).reduce((a, o) => ({ calories: a.calories + (+o.kcal || 0), protein: a.protein + (+o.protein || 0), carbs: a.carbs + (+o.carbs || 0), fat: a.fat + (+o.fat || 0) }), { calories: 0, protein: 0, carbs: 0, fat: 0 });
                const anySide = sides.length > 0;
                return (
                <div className="cpp-nft">
                  <div className="cpp-nft-h">Nutrition Facts{anySide && <span className="cpp-nft-note"> · incl. sides</span>}</div>
                  {NUTR.map(([k, l, u]) => (
                    <div key={k} className="cpp-nft-row"><span>{l}</span><b>{r.nutr[k] != null ? (r.nutr[k] + (add[k] || 0)) + u : "—"}</b></div>
                  ))}
                  {anySide && <div className="cpp-nft-std">Standard (no sides): {r.nutr.calories != null ? r.nutr.calories + " kcal" : "—"}</div>}
                </div>
                );
              })()}
              {missing && r.nutr.calories == null && <div className="cpp-info"><Icon name="info" size={14} /> Some nutrition information is not yet available for this item.</div>}
            </div>
          )}
          {tab === "ingredients" && has("show_ingredients") && (
            <div>
              <div className="cpp-sec-h">Ingredients</div>
              {r.ingredients.length ? <p className="cpp-ingtext">{r.ingredients.join(", ")}.</p> : <div className="cpp-info"><Icon name="info" size={14} /> Ingredient statement unavailable.</div>}
            </div>
          )}
          {tab === "allergens" && has("show_allergens") && (
            <div>
              <div className="cpp-sec-h">Allergens</div>
              {(r.contains || []).length ? (
                <div className="cpp-allg">{r.contains.map((a) => <span key={a} className="cpp-allg-tag">Contains {a}</span>)}</div>
              ) : <div className="cpp-allg"><span className="cpp-allg-free">Free from major declared allergens</span></div>}
              {(r.freeFrom || []).length > 0 && <div className="cpp-allg">{r.freeFrom.map((a) => <span key={a} className="cpp-allg-free">{a}-free</span>)}</div>}
              {has("show_may_contain") && (r.mayContain || []).length > 0 && <div className="cpp-maycontain"><Icon name="alert-triangle" size={13} /> May contain: {r.mayContain.join(", ")}</div>}
              {has("show_may_contain") && (r.mayContain || []).length === 0 && <div className="cpp-maycontain"><Icon name="alert-triangle" size={13} /> May contain traces depending on preparation.</div>}
              <div className="cpp-disc" {...edit("legal")}><Icon name="shield-alert" size={13} /> {org.disclaimers.allergen}</div>
            </div>
          )}
          {tab === "dietary" && has("show_dietary") && (
            <div>
              <div className="cpp-sec-h">Dietary & health</div>
              {(r.dietary || []).length ? <div className="cpp-badges left">{r.dietary.map((t) => <span key={t} className="cpp-badge" style={{ borderColor: brand, color: brand }}>{t}</span>)}</div> : <div className="cpp-info"><Icon name="info" size={14} /> No verified dietary attributes.</div>}
            </div>
          )}
          {tab === "prep" && (
            <div>
              {has("show_prep") && <div><div className="cpp-sec-h">Preparation</div><p className="cpp-ingtext">{r.prep || "Prepared fresh to the approved recipe."}</p></div>}
              {has("show_storage") && <div><div className="cpp-sec-h">Storage</div><p className="cpp-ingtext">{r.storage}</p></div>}
              {r.reheat && <div><div className="cpp-sec-h">Reheating</div><p className="cpp-ingtext">{r.reheat}</p></div>}
            </div>
          )}
          {tab === "details" && (
            <div>
              {has("show_description") && r.description && <p className="cpp-ingtext">{r.description}</p>}
              <div className="cpp-details">
                {r.category && <div><span>Category</span><b>{r.category}</b></div>}
                {r.cuisine && <div><span>Cuisine</span><b>{r.cuisine}</b></div>}
                {has("show_batch") && r.batch && <div><span>Batch</span><b>{r.batch}</b></div>}
                {has("show_dates") && r.bestBefore && <div><span>Best before</span><b>{r.bestBefore}</b></div>}
                {has("show_verified") && <div><span>Last verified</span><b>{r.verifiedOn}</b></div>}
              </div>
            </div>
          )}
        </div>

        {/* actions */}
        <div className="cpp-actions">
          {org.actions.order && <button className="cpp-btn primary" style={{ background: brand }}>Order now</button>}
          {org.actions.reorder && <button className="cpp-btn ghost">Reorder</button>}
          <div className="cpp-linkrow">
            {org.actions.contact && <button className="cpp-link"><Icon name="phone" size={12} /> Contact</button>}
            {org.actions.report && <button className="cpp-link"><Icon name="flag" size={12} /> Report issue</button>}
            {org.actions.share && <button className="cpp-link"><Icon name="share-2" size={12} /> Share</button>}
          </div>
        </div>
        <div className="cpp-foot">
          <div>{org.contact.email}</div>
          {!org.whiteLabel && <div className="cpp-powered"><span className="cpp-powered-orb" /> Powered by NutriDMS</div>}
        </div>
      </div>
    );
  }
  window.CpPublicPage = CpPublicPage;

  window.CustomerMobileView = CustomerMobileView;
  function CustomerMobileView() {
    const app = (typeof useApp === "function") ? useApp() : { role: "super-admin" };
    const org0 = CP.org();
    const [, bump] = cvUseState(0);
    cvUseEffect(() => { const h = () => bump((n) => n + 1); window.addEventListener("nutridms-customer-portal", h); return () => window.removeEventListener("nutridms-customer-portal", h); }, []);
    const offerings = cvUseMemo(cvOfferings, []);
    const [selId, setSelId] = cvUseState(offerings[0] ? offerings[0].id : null);
    const sel = offerings.find((o) => o.id === selId) || offerings[0];
    const [device, setDevice] = cvUseState("mobile");
    const [state, setState] = cvUseState("published");
    const [theme, setTheme] = cvUseState("light");
    const [confTab, setConfTab] = cvUseState("appearance");
    const [showQr, setShowQr] = cvUseState(false);
    const [showAnalytics, setShowAnalytics] = cvUseState(false);
    const [compare, setCompare] = cvUseState(false);
    const [pubCheck, setPubCheck] = cvUseState(null);
    const org = CP.org();

    if (!org.enabled) {
      return (
        <div className="page-inner">
          <div className="page-head"><div><h1 className="page-title">Customer Mobile View</h1><p className="page-sub">Preview & configure the experience customers see when they scan your product, meal, or menu QR codes.</p></div></div>
          <div className="cv-block card pad">
            <span className="cv-block-orb"><Icon name="qr-code" size={26} /></span>
            <h3>Public QR portal is off</h3>
            <p>Turn on the Customer Experience portal to publish scannable pages for your products, meals and menu items. Available on Professional and above.</p>
            <button className="btn primary" onClick={() => { CP.setOrg({ enabled: true }); }}>Enable public QR portal</button>
          </div>
        </div>
      );
    }
    if (!sel) {
      return (
        <div className="page-inner">
          <div className="page-head"><div><h1 className="page-title">Customer Mobile View</h1><p className="page-sub">Preview & configure the customer-facing mobile experience.</p></div></div>
          <div className="cv-block card pad"><span className="cv-block-orb"><Icon name="package" size={26} /></span><h3>No published items yet</h3><p>Publish a product, recipe, meal or menu item to preview its customer-facing page.</p></div>
        </div>
      );
    }

    const pubId = CP.pubId(sel.kind, sel.id);
    const settings = Object.assign(CP.itemSettings(pubId), { theme });
    const resolved = CP.resolve(sel.item, sel.kind);
    const publicUrl = CP.publicUrl(pubId);
    const setDisp = (k, v) => CP.saveItem(pubId, { display: Object.assign({}, settings.display, { [k]: v }) });
    const an = CP.analytics(pubId);

    const DISP_GROUPS = [
      ["Nutrition", [["show_nutrition", "Nutrition summary"], ["show_nutrition_table", "Full Nutrition Facts"]]],
      ["Ingredients", [["show_ingredients", "Ingredients"], ["show_subingredients", "Sub-ingredients"], ["show_source", "Ingredient source"]]],
      ["Allergens", [["show_allergens", "Allergens"], ["show_may_contain", "May-contain statement"]]],
      ["Dietary", [["show_dietary", "Dietary attributes"]]],
      ["Prep & storage", [["show_prep", "Preparation"], ["show_storage", "Storage"], ["show_batch", "Batch number"], ["show_dates", "Prepared / best-before"]]],
      ["Details", [["show_description", "Product description"], ["show_verified", "Verification date"], ["show_compliance", "Compliance info"]]],
    ];

    return (
      <div className="page-inner cv-page">
        <div className="page-head">
          <div><h1 className="page-title">Customer Mobile View</h1><p className="page-sub">Preview & configure the experience customers see when they scan your QR codes.</p></div>
          <div className="cv-head-actions">
            <button className="btn secondary" onClick={() => { navigator.clipboard && navigator.clipboard.writeText(location.origin + publicUrl); window.__toast && window.__toast("Public link copied"); }}><Icon name="link" size={15} /> Copy link</button>
            <button className="btn secondary" onClick={() => setShowQr(true)}><Icon name="qr-code" size={15} /> QR code</button>
            <button className="btn secondary" onClick={() => setShowAnalytics(true)}><Icon name="bar-chart-3" size={15} /> Analytics</button>
            {settings.status !== "published" && <button className="btn primary" onClick={() => {
              const c = [];
              c.push({ ok: !!resolved.servings, crit: true, label: "Serving size defined" });
              c.push({ ok: resolved.nutr && resolved.nutr.calories != null, crit: true, label: "Nutrition calculation complete" });
              c.push({ ok: (resolved.ingredients || []).length > 0, crit: true, label: "Ingredients present (mapped)" });
              c.push({ ok: (resolved.contains || []).length > 0 || (resolved.freeFrom || []).length > 0, crit: false, label: "Allergen status declared" });
              c.push({ ok: !!(org.disclaimers && org.disclaimers.allergen), crit: false, label: "Allergen disclaimer set" });
              c.push({ ok: !(org.actions && org.actions.order) || !!(org.contact && org.contact.email), crit: false, label: "Order/contact destination configured" });
              const fails = c.filter((x) => !x.ok);
              if (fails.length === 0) { CP.publish(pubId); window.__toast && window.__toast("Published — customers can now see this page"); }
              else setPubCheck(c);
            }}><Icon name="globe" size={15} /> Publish</button>}
          </div>
        </div>

        <div className="cv-split">
          {/* LEFT: config */}
          <div className="cv-config">
            <div className="cv-fld"><label>Item</label>
              <select value={sel.id} onChange={(e) => setSelId(e.target.value)}>
                {offerings.map((o) => <option key={o.id} value={o.id}>{o.type} · {o.name}</option>)}
              </select>
            </div>
            <div className="cv-fld-row">
              <div className="cv-fld"><label>Preview state</label>
                <select value={state} onChange={(e) => setState(e.target.value)}>
                  <option value="published">Published</option><option value="draft">Draft</option>
                  <option value="unavailable">Unavailable</option><option value="missing">Missing info</option><option value="expired">Expired QR</option>
                </select>
              </div>
              <div className="cv-fld"><label>Theme</label>
                <select value={theme} onChange={(e) => setTheme(e.target.value)}><option value="light">Light</option><option value="dark">Dark</option></select>
              </div>
            </div>
            <div className="cv-conftabs">
              {[["appearance", "Appearance"], ["layout", "Layout"], ["content", "Content"], ["actions", "Actions"], ["legal", "Legal"], ["a11y", "Language & A11y"]].map(([id, l]) => <button key={id} className={confTab === id ? "on" : ""} onClick={() => setConfTab(id)}>{l}</button>)}
            </div>

            {confTab === "appearance" && (
              <div className="cv-conf-body">
                <div className="cv-fld"><label>Organization name</label><input value={org.orgName} onChange={(e) => CP.setOrg({ orgName: e.target.value })} /></div>
                <div className="cv-fld"><label>Brand color</label>
                  <div className="cv-swatches">{["#1F8A5B", "#2A6FDB", "#D97757", "#6938EF", "#0E9384", "#B54708"].map((c) => <button key={c} className={"cv-sw" + (org.brandColor === c ? " on" : "")} style={{ background: c }} onClick={() => CP.setOrg({ brandColor: c })} />)}</div>
                </div>
                <div className="cv-fld"><label>Header style</label>
                  <select value={org.headerStyle} onChange={(e) => CP.setOrg({ headerStyle: e.target.value })}><option value="brand">Brand band</option><option value="minimal">Minimal</option></select>
                </div>
                <div className="cv-fld-row">
                  <div className="cv-fld"><label>Button style</label>
                    <select value={org.buttonStyle || "rounded"} onChange={(e) => CP.setOrg({ buttonStyle: e.target.value })}><option value="rounded">Rounded</option><option value="pill">Pill</option><option value="square">Square</option></select>
                  </div>
                  <div className="cv-fld"><label>Corner radius</label>
                    <select value={org.radius || "md"} onChange={(e) => CP.setOrg({ radius: e.target.value })}><option value="sm">Small</option><option value="md">Medium</option><option value="lg">Large</option></select>
                  </div>
                </div>
                <div className="cv-fld"><label>Font</label>
                  <select value={org.font || "system"} onChange={(e) => CP.setOrg({ font: e.target.value })}><option value="system">System</option><option value="serif">Serif</option><option value="rounded">Rounded sans</option></select>
                </div>
                <div className="cv-fld"><label>Default theme</label>
                  <select value={org.defaultTheme || "light"} onChange={(e) => CP.setOrg({ defaultTheme: e.target.value })}><option value="light">Light</option><option value="dark">Dark</option><option value="auto">Match device</option></select>
                </div>
                <label className="cv-toggle"><span>White-label (hide “Powered by NutriDMS”)</span><input type="checkbox" checked={org.whiteLabel} disabled={org.tier !== "enterprise"} onChange={(e) => CP.setOrg({ whiteLabel: e.target.checked })} /></label>
                {org.tier !== "enterprise" && <div className="cv-hint"><Icon name="lock" size={12} /> White-label is an Enterprise feature.</div>}
              </div>
            )}
            {confTab === "layout" && (() => {
              const DEF = [["welcome", "Welcome"], ["preferences", "Preferences"], ["menu", "Today's Menu"], ["gallery", "Photo gallery"], ["sides", "Sides & customization"], ["nutrition", "Nutrition summary"], ["energy", "Energy distribution"], ["nutrition_table", "Nutrition Facts"], ["ingredients", "Ingredients"], ["allergens", "Allergens"], ["dietary", "Dietary claims"], ["prep", "Preparation & storage"], ["reviews", "Reviews"], ["loyalty", "Loyalty"], ["promo", "Promotions"], ["thankyou", "Thank you"]];
              const saved = (org.moduleOrder && org.moduleOrder.length) ? org.moduleOrder : DEF.map((d) => d[0]);
              const label = (id) => (DEF.find((d) => d[0] === id) || [id, id])[1];
              const move = (i, dir) => { const n = saved.slice(); const j = i + dir; if (j < 0 || j >= n.length) return; const t = n[i]; n[i] = n[j]; n[j] = t; CP.setOrg({ moduleOrder: n }); };
              const off = org.moduleOff || {};
              const toggle = (id) => CP.setOrg({ moduleOff: Object.assign({}, off, { [id]: !off[id] }) });
              return (
                <div className="cv-conf-body">
                  <div className="cv-hint" style={{ marginBottom: 10 }}><Icon name="grip-vertical" size={12} /> Reorder how modules appear in the customer journey. Drag with the arrows; toggle to hide.</div>
                  <div className="cv-modlist">
                    {saved.map((id, i) => (
                      <div key={id} className={"cv-modrow" + (off[id] ? " off" : "")}>
                        <span className="cv-mod-ord">{i + 1}</span>
                        <span className="cv-mod-nm">{label(id)}</span>
                        <span className="cv-mod-arrows">
                          <button disabled={i === 0} onClick={() => move(i, -1)} title="Up"><Icon name="chevron-up" size={14} /></button>
                          <button disabled={i === saved.length - 1} onClick={() => move(i, 1)} title="Down"><Icon name="chevron-down" size={14} /></button>
                        </span>
                        <button className="cv-mod-eye" onClick={() => toggle(id)} title={off[id] ? "Show" : "Hide"}><Icon name={off[id] ? "eye-off" : "eye"} size={15} /></button>
                      </div>
                    ))}
                  </div>
                  <button className="btn ghost sm" style={{ marginTop: 10 }} onClick={() => CP.setOrg({ moduleOrder: DEF.map((d) => d[0]), moduleOff: {} })}>Reset to default order</button>
                </div>
              );
            })()}
            {confTab === "content" && (
              <div className="cv-conf-body">
                {DISP_GROUPS.map(([grp, rows]) => (
                  <div key={grp} className="cv-cgroup">
                    <div className="cv-cgroup-h">{grp}</div>
                    {rows.map(([k, l]) => <label key={k} className="cv-toggle"><span>{l}</span><input type="checkbox" checked={settings.display[k] !== false} onChange={(e) => setDisp(k, e.target.checked)} /></label>)}
                  </div>
                ))}
              </div>
            )}
            {confTab === "actions" && (
              <div className="cv-conf-body">
                {[["order", "Order now"], ["reorder", "Reorder"], ["viewmenu", "View menu"], ["contact", "Contact restaurant"], ["askallergens", "Ask staff about allergens"], ["report", "Report incorrect info"], ["share", "Share product"], ["save", "Save item"], ["download", "Download nutrition"]].map(([k, l]) => (
                  <label key={k} className="cv-toggle"><span>{l}</span><input type="checkbox" checked={org.actions[k] !== false} onChange={(e) => CP.setOrg({ actions: Object.assign({}, org.actions, { [k]: e.target.checked }) })} /></label>
                ))}
                <div className="cv-cgroup">
                  <div className="cv-cgroup-h">Customer preferences (scan-time)</div>
                  <label className="cv-toggle"><span>Ask allergies & preferences on scan</span><input type="checkbox" checked={!!(org.preferences && org.preferences.enabled)} onChange={(e) => CP.setOrg({ preferences: Object.assign({}, org.preferences, { enabled: e.target.checked }) })} /></label>
                  <label className="cv-toggle"><span>Include ingredient dislikes</span><input type="checkbox" checked={!(org.preferences && org.preferences.dislikesEnabled === false)} onChange={(e) => CP.setOrg({ preferences: Object.assign({}, org.preferences, { dislikesEnabled: e.target.checked }) })} /></label>
                  <div className="cv-hint"><Icon name="shield-alert" size={12} /> Allergy matches show a strong warning; dislikes show a soft notice — never as an allergy.</div>
                  {org.preferences && org.preferences.enabled && (() => {
                    const DEF_A = ["Milk", "Eggs", "Wheat", "Soy", "Peanuts", "Tree nuts", "Fish", "Shellfish", "Sesame"];
                    const DEF_D = ["Mushrooms", "Onion", "Garlic", "Pork", "Spicy"];
                    const A = (org.preferences.allergens && org.preferences.allergens.length) ? org.preferences.allergens : DEF_A;
                    const D = (org.preferences.dislikes && org.preferences.dislikes.length) ? org.preferences.dislikes : DEF_D;
                    const saveA = (n) => CP.setOrg({ preferences: Object.assign({}, org.preferences, { allergens: n }) });
                    const saveD = (n) => CP.setOrg({ preferences: Object.assign({}, org.preferences, { dislikes: n }) });
                    const add = (list, save, val) => { val = (val || "").trim(); if (val && list.indexOf(val) < 0) save(list.concat([val])); };
                    const Editor = (title, list, save) => (
                      <div className="cv-chipedit">
                        <div className="cv-chipedit-h">{title}</div>
                        <div className="cv-chipedit-chips">
                          {list.map((x) => <span key={x} className="cv-editchip">{x}<button onClick={() => save(list.filter((y) => y !== x))} aria-label={"Remove " + x}><Icon name="x" size={11} /></button></span>)}
                        </div>
                        <input className="cv-chipedit-in" placeholder={"Add " + title.toLowerCase() + " + Enter"} onKeyDown={(e) => { if (e.key === "Enter") { add(list, save, e.target.value); e.target.value = ""; } }} />
                      </div>
                    );
                    return <div className="cv-prefedit">{Editor("Allergies", A, saveA)}{(org.preferences.dislikesEnabled !== false) && Editor("Dislikes", D, saveD)}</div>;
                  })()}
                </div>
                <div className="cv-cgroup">
                  <div className="cv-cgroup-h">Nutrition display</div>
                  <label className="cv-toggle"><span>Energy-distribution chart</span><input type="checkbox" checked={settings.display.show_energy !== false} onChange={(e) => setDisp("show_energy", e.target.checked)} /></label>
                  <label className="cv-toggle"><span>Side selection + live recalculation</span><input type="checkbox" checked={!!(org.sides && org.sides.enabled)} onChange={(e) => CP.setOrg({ sides: Object.assign({}, org.sides, { enabled: e.target.checked }) })} /></label>
                  {org.sides && org.sides.enabled && (() => {
                    const opts = (org.sides.options && org.sides.options.length) ? org.sides.options : [
                      { id: "rice", name: "White rice", kcal: 200, protein: 4, carbs: 44, fat: 0 },
                      { id: "salad", name: "Garden salad", kcal: 70, protein: 2, carbs: 8, fat: 4 },
                      { id: "fries", name: "French fries", kcal: 320, protein: 4, carbs: 42, fat: 15 },
                      { id: "veg", name: "Roasted vegetables", kcal: 110, protein: 3, carbs: 18, fat: 4 },
                    ];
                    const save = (next) => CP.setOrg({ sides: Object.assign({}, org.sides, { options: next }) });
                    const upd = (i, k, v) => { const n = opts.slice(); n[i] = Object.assign({}, n[i], { [k]: k === "name" ? v : (+v || 0) }); save(n); };
                    return (
                      <div className="cv-sides-editor">
                        <div className="cv-sides-head"><span>Side</span><span>kcal</span><span>P</span><span>C</span><span>F</span><span /></div>
                        {opts.map((o, i) => (
                          <div key={o.id || i} className="cv-sides-row">
                            <input value={o.name} onChange={(e) => upd(i, "name", e.target.value)} placeholder="Side name" />
                            <input type="number" value={o.kcal} onChange={(e) => upd(i, "kcal", e.target.value)} />
                            <input type="number" value={o.protein} onChange={(e) => upd(i, "protein", e.target.value)} />
                            <input type="number" value={o.carbs} onChange={(e) => upd(i, "carbs", e.target.value)} />
                            <input type="number" value={o.fat} onChange={(e) => upd(i, "fat", e.target.value)} />
                            <button className="cv-sides-del" onClick={() => save(opts.filter((_, j) => j !== i))} aria-label="Remove"><Icon name="x" size={13} /></button>
                          </div>
                        ))}
                        <div className="cv-sides-actions">
                          <button className="cv-sides-add" onClick={() => save(opts.concat([{ id: "s" + Date.now(), name: "New side", kcal: 0, protein: 0, carbs: 0, fat: 0 }]))}><Icon name="plus" size={13} /> Add manual side</button>
                            <label className="cv-sides-fetch">
                            <Icon name="book-open" size={13} /> Add from recipe library or offers
                            <select value="" onChange={(e) => {
                              const rid = e.target.value; if (!rid) return;
                              const rec = (window.RECIPES || []).find((x) => x.id === rid);
                              let offs = []; try { offs = (typeof ofLoad === "function") ? ofLoad() : (window.__offerings || []); } catch (er) {}
                              const off = (offs || []).find((x) => x.id === rid);
                              if (rec) save(opts.concat([{ id: "s" + Date.now(), name: rec.name, kcal: +rec.calories || 0, protein: +rec.protein || 0, carbs: +rec.carbs || 0, fat: +rec.fat || 0, fromRecipe: rec.id }]));
                              else if (off) { const n = off.nutrition || off.nutr || {}; save(opts.concat([{ id: "s" + Date.now(), name: off.name || off.title, kcal: +n.calories || +n.kcal || 0, protein: +n.protein || 0, carbs: +n.carbs || 0, fat: +n.fat || 0, fromOffer: off.id }])); }
                              e.target.value = "";
                            }}>
                              <option value="">Select approved recipe or offer…</option>
                              <optgroup label="Recipes">
                                {(window.RECIPES || []).filter((x) => x.status === "approved" || x.status === "published").map((x) => <option key={x.id} value={x.id}>{x.name}{x.category === "Beverage" ? " (drink)" : ""}</option>)}
                              </optgroup>
                              {(() => { let offs = []; try { offs = (typeof ofLoad === "function") ? ofLoad() : (window.__offerings || []); } catch (er) {} return (offs && offs.length) ? <optgroup label="Offers">{offs.map((o) => <option key={o.id} value={o.id}>{o.name || o.title}{o.type ? " (" + o.type + ")" : ""}</option>)}</optgroup> : null; })()}
                            </select>
                          </label>
                        </div>
                      </div>
                    );
                  })()}
                </div>
              </div>
            )}
            {confTab === "legal" && (
              <div className="cv-conf-body">
                <div className="cv-fld"><label>Allergen disclaimer</label><textarea rows="4" value={org.disclaimers.allergen} onChange={(e) => CP.setOrg({ disclaimers: Object.assign({}, org.disclaimers, { allergen: e.target.value }) })} /></div>
                <div className="cv-fld"><label>Nutrition disclaimer</label><textarea rows="2" value={org.disclaimers.nutrition} onChange={(e) => CP.setOrg({ disclaimers: Object.assign({}, org.disclaimers, { nutrition: e.target.value }) })} /></div>
                <div className="cv-fld"><label>Cross-contamination warning</label><textarea rows="2" value={org.disclaimers.crossContact || "Prepared in a kitchen that also handles common allergens. Cross-contact may occur."} onChange={(e) => CP.setOrg({ disclaimers: Object.assign({}, org.disclaimers, { crossContact: e.target.value }) })} /></div>
                <div className="cv-fld"><label>Medical advice disclaimer</label><textarea rows="2" value={org.disclaimers.medical || "This information is for general guidance only and is not medical or dietary advice."} onChange={(e) => CP.setOrg({ disclaimers: Object.assign({}, org.disclaimers, { medical: e.target.value }) })} /></div>
                <div className="cv-fld"><label>Regional compliance notice</label><textarea rows="2" value={org.disclaimers.regional || ""} placeholder="e.g. Prepared under CFIA-regulated food-safety standards." onChange={(e) => CP.setOrg({ disclaimers: Object.assign({}, org.disclaimers, { regional: e.target.value }) })} /></div>
                <div className="cv-fld"><label>Contact email</label><input value={org.contact.email} onChange={(e) => CP.setOrg({ contact: Object.assign({}, org.contact, { email: e.target.value }) })} /></div>
              </div>
            )}
            {confTab === "a11y" && (
              <div className="cv-conf-body">
                <div className="cv-cgroup">
                  <div className="cv-cgroup-h">Languages</div>
                  {[["en", "English"], ["fr", "French"], ["es", "Spanish"], ["zh", "Chinese"]].map(([k, l]) => {
                    const langs = org.languages || ["en"];
                    const on = langs.indexOf(k) >= 0;
                    return <label key={k} className="cv-toggle"><span>{l}</span><input type="checkbox" checked={on} disabled={k === "en"} onChange={(e) => { const next = e.target.checked ? langs.concat([k]) : langs.filter((x) => x !== k); CP.setOrg({ languages: next }); }} /></label>;
                  })}
                  <label className="cv-toggle"><span>Auto-detect device language</span><input type="checkbox" checked={org.autoLang !== false} onChange={(e) => CP.setOrg({ autoLang: e.target.checked })} /></label>
                </div>
                <div className="cv-cgroup">
                  <div className="cv-cgroup-h">Accessibility</div>
                  <div className="cv-fld"><label>Base text size</label>
                    <select value={(org.a11y && org.a11y.textScale) || "base"} onChange={(e) => CP.setOrg({ a11y: Object.assign({}, org.a11y, { textScale: e.target.value }) })}><option value="base">Standard</option><option value="lg">Large</option><option value="xl">Extra large</option></select>
                  </div>
                  <label className="cv-toggle"><span>High-contrast mode</span><input type="checkbox" checked={!!(org.a11y && org.a11y.highContrast)} onChange={(e) => CP.setOrg({ a11y: Object.assign({}, org.a11y, { highContrast: e.target.checked }) })} /></label>
                  <label className="cv-toggle"><span>Screen-reader labels</span><input type="checkbox" checked={!(org.a11y && org.a11y.srLabels === false)} onChange={(e) => CP.setOrg({ a11y: Object.assign({}, org.a11y, { srLabels: e.target.checked }) })} /></label>
                  <label className="cv-toggle"><span>Accessible allergen indicators (icon + text)</span><input type="checkbox" checked={!(org.a11y && org.a11y.allergenIcons === false)} onChange={(e) => CP.setOrg({ a11y: Object.assign({}, org.a11y, { allergenIcons: e.target.checked }) })} /></label>
                  <label className="cv-toggle"><span>Keyboard navigation hints</span><input type="checkbox" checked={!!(org.a11y && org.a11y.keyboard)} onChange={(e) => CP.setOrg({ a11y: Object.assign({}, org.a11y, { keyboard: e.target.checked }) })} /></label>
                </div>
              </div>
            )}
          </div>

          {/* RIGHT: live phone preview */}
          <div className="cv-preview">
            <div className="cv-preview-bar">
              <span className={"cv-state-tag " + state}>{state === "published" ? "Live" : state[0].toUpperCase() + state.slice(1)}</span>
              <span className="cv-preview-url">{publicUrl}</span>
              <button className={"cv-compare-btn" + (compare ? " on" : "")} onClick={() => setCompare((c) => !c)}><Icon name="columns-2" size={13} /> Compare draft vs published</button>
            </div>
            {compare ? (
              <div className="cv-compare">
                <div className="cv-compare-col">
                  <div className="cv-compare-tag draft">Draft</div>
                  <div className="cv-phone cv-phone-small"><div className="cv-phone-notch" /><div className="cv-phone-screen"><CpPublicPage resolved={resolved} settings={settings} org={org} state="draft" /></div></div>
                </div>
                <div className="cv-compare-col">
                  <div className="cv-compare-tag live">Published</div>
                  <div className="cv-phone cv-phone-small"><div className="cv-phone-notch" /><div className="cv-phone-screen"><CpPublicPage resolved={resolved} settings={settings} org={org} state="published" /></div></div>
                </div>
              </div>
            ) : (
              <div className={"cv-phone cv-phone-" + device}>
                <div className="cv-phone-notch" />
                <div className="cv-phone-screen">
                  <CpPublicPage resolved={resolved} settings={settings} org={org} state={state} onEditSection={(t) => setConfTab(t)} />
                </div>
              </div>
            )}
            {!compare && <div className="cv-devices">
              {[["small", "Small"], ["mobile", "Mobile"], ["large", "Large"], ["tablet", "Tablet"]].map(([id, l]) => <button key={id} className={device === id ? "on" : ""} onClick={() => setDevice(id)}>{l}</button>)}
            </div>}
          </div>
        </div>

        {showQr && (
          <div className="cv-modal-scrim" onMouseDown={(e) => { if (e.target === e.currentTarget) setShowQr(false); }}>
            <div className="cv-modal">
              <div className="cv-modal-head"><strong>QR code · {sel.name}</strong><button onClick={() => setShowQr(false)}><Icon name="x" size={18} /></button></div>
              <div className="cv-qr-wrap"><QrSvg text={publicUrl} size={180} /></div>
              <p className="cv-qr-note">Scan to view nutrition facts, ingredients, allergens & dietary info. The code points to a permanent URL, so you can update data without reprinting.</p>
              <code className="cv-qr-url">{location.origin + publicUrl}</code>
              <div className="cv-modal-foot"><button className="btn secondary" onClick={() => { navigator.clipboard && navigator.clipboard.writeText(location.origin + publicUrl); window.__toast && window.__toast("Link copied"); }}>Copy link</button><button className="btn primary" onClick={() => setShowQr(false)}>Done</button></div>
            </div>
          </div>
        )}
        {showAnalytics && (
          <div className="cv-modal-scrim" onMouseDown={(e) => { if (e.target === e.currentTarget) setShowAnalytics(false); }}>
            <div className="cv-modal cv-modal-lg">
              <div className="cv-modal-head"><strong>Scan analytics · {sel.name}</strong><button onClick={() => setShowAnalytics(false)}><Icon name="x" size={18} /></button></div>
              <div className="cv-an-kpis">
                <div className="cv-an-k"><b>{an.scans}</b><span>Total scans</span></div>
                <div className="cv-an-k"><b>{an.unique}</b><span>Unique visitors</span></div>
                <div className="cv-an-k"><b>{an.reorders}</b><span>Reorder clicks</span></div>
                <div className="cv-an-k"><b>{an.reports}</b><span>Reported issues</span></div>
              </div>
              <div className="cv-an-bars">
                {an.byDay.map((v, i) => <div key={i} className="cv-an-bar"><span style={{ height: (v / Math.max.apply(null, an.byDay) * 90 + 8) + "px" }} /><em>{["M", "T", "W", "T", "F", "S", "S"][i]}</em></div>)}
              </div>
              <div className="cv-an-rows">
                <div className="cv-an-row"><span>Most-viewed allergen</span><b>{an.topAllergen}</b></div>
                <div className="cv-an-row"><span>Most-viewed section</span><b>{an.topSection}</b></div>
                <div className="cv-an-row"><span>Devices</span><b>{an.devices.mobile}% mobile · {an.devices.tablet}% tablet</b></div>
              </div>
              <div className="cv-modal-foot"><button className="btn primary" onClick={() => setShowAnalytics(false)}>Close</button></div>
            </div>
          </div>
        )}
        {pubCheck && (
          <div className="cv-modal-scrim" onMouseDown={(e) => { if (e.target === e.currentTarget) setPubCheck(null); }}>
            <div className="cv-modal">
              <div className="cv-modal-head"><strong>Publish check · {sel.name}</strong><button onClick={() => setPubCheck(null)}><Icon name="x" size={18} /></button></div>
              <div className="cv-pubcheck">
                {pubCheck.map((c, i) => (
                  <div key={i} className={"cv-pubrow " + (c.ok ? "ok" : c.crit ? "fail" : "warn")}>
                    <Icon name={c.ok ? "check-circle-2" : c.crit ? "x-circle" : "alert-triangle"} size={16} />
                    <span>{c.label}</span>
                    <em>{c.ok ? "Pass" : c.crit ? "Required" : "Recommended"}</em>
                  </div>
                ))}
              </div>
              <p className="cv-pubcheck-note">{pubCheck.some((c) => !c.ok && c.crit) ? "Resolve the required items before publishing." : "Recommended items are missing — you can publish anyway or fix them first."}</p>
              <div className="cv-modal-foot">
                <button className="btn secondary" onClick={() => setPubCheck(null)}>Cancel</button>
                {!pubCheck.some((c) => !c.ok && c.crit) && <button className="btn primary" onClick={() => { CP.publish(pubId); setPubCheck(null); window.__toast && window.__toast("Published"); }}>Publish anyway</button>}
              </div>
            </div>
          </div>
        )}
      </div>
    );
  }
})();
