/* global React */
/* Drømme Cookies — ordering overlay (integrated into the single-page site).
   Opened by any CTA via: window.dispatchEvent(new Event("dc-open-order")). */
(function () {
  const { useState, useEffect, useCallback, useRef } = React;

  const C = {
    cream: "var(--cream)", paper: "var(--paper)", soft: "var(--cream-soft)",
    caramel: "var(--caramel)", caramelDk: "var(--caramel-dk)",
    choc: "var(--chocolate)", choc2: "var(--chocolate-2)", espresso: "var(--espresso)",
    serif: "var(--serif)", sans: "var(--sans)", mono: "var(--mono)",
    rule: "rgba(139,90,60,0.14)", ruleStrong: "rgba(139,90,60,0.30)",
  };

  function fmtKr(n) { return n.toLocaleString("no-NO") + " kr"; }

  const SALT_OPTIONS = [
    { value: "",     label: "Vanlig" },
    { value: "uten", label: "Uten salt" },
  ];

  // Cookies that don't have salt at all — no point asking the customer.
  const SALT_FREE = new Set(["Kaneldrøm", "Kaneldrøm Mini", "Biscoffen", "Biscoffen Mini"]);
  const hasSalt = (p) => !SALT_FREE.has(p.name);

  // Finished (filled) weight per regular cookie. Most are ~110 g; a couple are
  // bigger. Minis are ~50 g across the board. Display-only — never affects price.
  const REG_WEIGHTS = { "Kaneldrøm": 130, "Biscoffen": 120 };
  const regWeight = (name) => REG_WEIGHTS[name] || 110;

  function todayPlus(days) {
    const d = new Date();
    d.setDate(d.getDate() + days);
    return d.toISOString().slice(0, 10);
  }

  // Sundays are not available for pickup. Parse YYYY-MM-DD as a local date
  // so getDay() reflects the day the customer actually picked.
  function isSunday(ymd) {
    if (!ymd) return false;
    const parts = ymd.split("-");
    if (parts.length !== 3) return false;
    const d = new Date(Number(parts[0]), Number(parts[1]) - 1, Number(parts[2]));
    return d.getDay() === 0;
  }

  function Stepper({ value, onDec, onInc, disabledInc }) {
    const btn = (active) => ({
      width: 38, height: 38, borderRadius: "50%",
      border: "1px solid " + C.ruleStrong,
      display: "flex", alignItems: "center", justifyContent: "center",
      fontFamily: C.sans, fontSize: 20, lineHeight: 1,
      color: active ? C.choc : "rgba(139,90,60,0.3)",
      background: C.paper, flexShrink: 0,
      cursor: active ? "pointer" : "not-allowed",
    });
    return React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 12 } },
      React.createElement("button", { type: "button", onClick: onDec, disabled: value <= 0, style: btn(value > 0), "aria-label": "Færre" }, "–"),
      React.createElement("span", { style: { minWidth: 22, textAlign: "center", fontFamily: C.mono, fontSize: 16, color: C.choc } }, value),
      React.createElement("button", { type: "button", onClick: onInc, disabled: disabledInc, style: btn(!disabledInc), "aria-label": "Flere" }, "+")
    );
  }

  function OrderOverlay() {
    const [open, setOpen] = useState(false);
    const [loading, setLoading] = useState(false);
    const [data, setData] = useState(null);   // { products, deliveryZones, pickupWindows, maxQty }
    const [qty, setQty] = useState({});        // productId -> n
    const [salt, setSalt] = useState({});      // productId -> "" | "uten"
    const [sizeSel, setSizeSel] = useState({});// groupKey -> "regular" | "mini" (UI only)
    const [flavorSel, setFlavorSel] = useState({});// groupKey -> flavor index (UI only)
    const [discountInput, setDiscountInput] = useState("");
    const [discountInfo, setDiscountInfo] = useState(null); // { code, type, value } when applied
    const [discountErr, setDiscountErr] = useState("");
    const [applyingCode, setApplyingCode] = useState(false);
    const [fulfill, setFulfill] = useState("pickup");
    const [pickupDate, setPickupDate] = useState("");
    const [pickupWindow, setPickupWindow] = useState("");
    const [address, setAddress] = useState("");
    const [postnummer, setPostnummer] = useState("");
    const [kommune, setKommune] = useState("");
    const [name, setName] = useState("");
    const [phone, setPhone] = useState("");
    const [comment, setComment] = useState("");
    const [company, setCompany] = useState(""); // honeypot
    const [submitting, setSubmitting] = useState(false);
    const [error, setError] = useState("");
    const [done, setDone] = useState(null);    // success payload
    const scrollRef = useRef(null);

    const maxQty = (data && data.maxQty) || 24;

    const loadData = useCallback(() => {
      setLoading(true);
      fetch("/api/public/products")
        .then((r) => r.json())
        .then((d) => { setData(d); setLoading(false); })
        .catch(() => { setError("Kunne ikke laste menyen. Prøv igjen."); setLoading(false); });
    }, []);

    useEffect(() => {
      const onOpen = () => {
        setOpen(true);
        setDone(null);
        setError("");
        if (!data) loadData();
      };
      window.addEventListener("dc-open-order", onOpen);
      return () => window.removeEventListener("dc-open-order", onOpen);
    }, [data, loadData]);

    useEffect(() => {
      document.body.style.overflow = open ? "hidden" : "";
      return () => { document.body.style.overflow = ""; };
    }, [open]);

    const close = () => setOpen(false);

    const products = (data && data.products) || [];
    const zones = (data && data.deliveryZones) || [];
    const pickupWindows = (data && data.pickupWindows) || [];
    const blockedDates = (data && data.blockedDates) || []; // days the bakery is closed

    // Group each full-size cookie with its mini variant (a separate 35 kr
    // product named "<base> Mini"). Cart state stays keyed by the real product
    // id, so submit/pricing/admin are all unchanged — this is purely how we
    // present the choice.
    const MINI_SUFFIX = " Mini";
    // displayName → ordered flavours. Each flavour's `base` is its full-size
    // product name (mini is "<base> Mini"). These are real, separate products,
    // so cart/pricing/submit/admin are unchanged — the flavour toggle is purely
    // how we present the choice (and it swaps the displayed image).
    const FLAVOR_GROUPS = {
      "Vegandrøm": [
        { flavor: "Kanel",   base: "Vegandrøm" },
        { flavor: "Sitron",  base: "Vegandrøm Sitron" },
        { flavor: "Vanilje", base: "Vegandrøm Vanilje" },
      ],
    };
    const groups = (() => {
      const byName = new Map(products.map((p) => [p.name, p]));
      const seen = new Set();
      const out = [];

      // Names claimed by a flavour group, so they don't also render standalone.
      const flavorMember = new Set();
      Object.values(FLAVOR_GROUPS).forEach((defs) => defs.forEach((d) => {
        flavorMember.add(d.base); flavorMember.add(d.base + MINI_SUFFIX);
      }));

      // Flavour groups first (only flavours whose product actually exists).
      for (const displayName of Object.keys(FLAVOR_GROUPS)) {
        const flavors = FLAVOR_GROUPS[displayName].map((d) => {
          const regular = byName.get(d.base);
          if (!regular) return null;
          return { flavor: d.flavor, regular, mini: byName.get(d.base + MINI_SUFFIX) || null };
        }).filter(Boolean);
        if (!flavors.length) continue;
        flavors.forEach((f) => { seen.add(f.regular.id); if (f.mini) seen.add(f.mini.id); });
        out.push({ key: "flav:" + displayName, isFlavor: true, displayName, base: flavors[0].regular, flavors });
      }

      // Normal base+mini groups, skipping flavour members.
      for (const p of products) {
        if (p.name.endsWith(MINI_SUFFIX) || flavorMember.has(p.name) || seen.has(p.id)) continue;
        const mini = byName.get(p.name + MINI_SUFFIX) || null;
        out.push({ key: p.id, base: p, regular: p, mini });
        seen.add(p.id);
        if (mini) seen.add(mini.id);
      }
      // Orphan minis with no matching base — show standalone so nothing vanishes.
      for (const p of products) {
        if (p.name.endsWith(MINI_SUFFIX) && !seen.has(p.id) && !flavorMember.has(p.name)) {
          out.push({ key: p.id, base: p, regular: p, mini: null });
        }
      }
      return out;
    })();

    // Same category grouping as the public menu, so the order list isn't one
    // long flat scroll. Headers are presentational only — cart, pricing and
    // submit are completely unchanged.
    const CATEGORIES = [
      { label: "Lyse & klassiske", names: ["Klassikeren", "Dobbel Sjokkis", "Melkesjokkis", "Kaneldrøm", "Biscoffen", "Peanøttdrøm"] },
      { label: "Mørke & fudgy", names: ["Brownien", "Brookie", "Trippelen"] },
      { label: "Vegansk", names: ["Vegandrøm"] },
    ];
    const sections = (() => {
      const catName = (g) => g.isFlavor ? g.displayName : g.base.name;
      const byCatName = new Map(groups.map((g) => [catName(g), g]));
      const usedKeys = new Set();
      const out = [];
      for (const c of CATEGORIES) {
        const gs = c.names.map((nm) => byCatName.get(nm)).filter(Boolean);
        gs.forEach((g) => usedKeys.add(g.key));
        if (gs.length) out.push({ label: c.label, groups: gs });
      }
      const rest = groups.filter((g) => !usedKeys.has(g.key));
      if (rest.length) out.push({ label: out.length ? "Mer" : null, groups: rest });
      return out;
    })();

    const totalQty = Object.values(qty).reduce((s, n) => s + n, 0);
    const subtotal = products.reduce((s, p) => s + (qty[p.id] || 0) * p.price_nok, 0);

    // Applied discount code — recomputed from the live subtotal for display.
    // The server re-validates and re-computes on submit, so this is preview only.
    const discount = discountInfo
      ? (discountInfo.type === "fixed"
          ? Math.min(discountInfo.value, subtotal)
          : Math.round(subtotal * (discountInfo.value / 100)))
      : 0;

    let deliveryFee = 0;
    let deliveryKnown = true;
    if (fulfill === "delivery" && kommune) {
      const z = zones.find((zn) => zn.kommuner.includes(kommune));
      if (z) deliveryFee = z.fee_nok; else deliveryKnown = false;
    }
    const total = subtotal - discount + (fulfill === "delivery" ? deliveryFee : 0);
    const atMax = totalQty >= maxQty;

    const applyCode = () => {
      const code = (discountInput || "").trim();
      if (!code || applyingCode) return;
      setApplyingCode(true);
      setDiscountErr("");
      fetch("/api/public/discounts", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ code: code, subtotal: subtotal }),
      })
        .then((r) => r.json())
        .then((d) => {
          if (d && d.valid) { setDiscountInfo({ code: d.code, type: d.type, value: d.value }); setDiscountErr(""); }
          else { setDiscountInfo(null); setDiscountErr((d && d.error) || "Ugyldig rabattkode."); }
        })
        .catch(() => setDiscountErr("Kunne ikke sjekke koden akkurat nå."))
        .then(() => setApplyingCode(false));
    };
    const clearCode = () => { setDiscountInfo(null); setDiscountInput(""); setDiscountErr(""); };

    function setItem(id, delta) {
      setQty((q) => {
        const cur = q[id] || 0;
        if (delta > 0 && totalQty >= maxQty) return q;
        const next = Math.max(0, cur + delta);
        const copy = { ...q };
        if (next === 0) delete copy[id]; else copy[id] = next;
        return copy;
      });
      // Wipe salt choice when the cookie is no longer in the cart
      if (delta < 0) {
        setSalt((s) => {
          const cur = (qty[id] || 0) + delta;
          if (cur > 0) return s;
          if (!(id in s)) return s;
          const copy = { ...s }; delete copy[id]; return copy;
        });
      }
    }

    function setSaltFor(id, value) {
      setSalt((s) => {
        const copy = { ...s };
        if (!value) delete copy[id]; else copy[id] = value;
        return copy;
      });
    }

    function submit() {
      setError("");
      if (totalQty === 0) { setError("Velg minst én cookie."); return; }
      if (!name.trim()) { setError("Fyll inn navn."); return; }
      if (!/^(\+47|0047)?\d{8}$/.test(phone.replace(/[\s\-()]/g, ""))) {
        setError("Oppgi et gyldig norsk mobilnummer (8 siffer)."); return;
      }
      if (fulfill === "pickup" && (!pickupDate || !pickupWindow)) {
        setError("Velg dato og tidsvindu for henting."); return;
      }
      if (fulfill === "pickup" && isSunday(pickupDate)) {
        setError("Vi har dessverre ikke henting på søndager. Velg en annen dag."); return;
      }
      if (fulfill === "pickup" && blockedDates.includes(pickupDate)) {
        setError("Vi har dessverre stengt denne dagen. Velg en annen 🤎"); return;
      }
      if (fulfill === "delivery") {
        if (!address.trim() || !postnummer.trim() || !kommune) {
          setError("Fyll inn adresse, postnummer og kommune."); return;
        }
        if (!deliveryKnown) { setError("Vi leverer ikke til denne kommunen ennå. Ta kontakt 🤎"); return; }
      }

      const lines = products.filter((p) => qty[p.id] > 0).map((p) => ({
        product_id: p.id,
        quantity: qty[p.id],
        // Force null for salt-free cookies even if a salt value somehow exists in state
        salt_preference: hasSalt(p) ? (salt[p.id] || null) : null,
      }));
      const payload = {
        customer_name: name.trim(),
        customer_phone: phone.trim(),
        notes: comment.trim() || null,
        company, // honeypot
        fulfillment_type: fulfill,
        pickup_slot: fulfill === "pickup" ? pickupDate + " kl " + pickupWindow : null,
        delivery_address: address.trim(),
        delivery_postnummer: postnummer.trim(),
        delivery_kommune: kommune,
        discount_code: discountInfo ? discountInfo.code : null,
        lines,
      };

      setSubmitting(true);
      (async () => {
        try {
          let res;
          try {
            res = await fetch("/api/public/orders", {
              method: "POST",
              headers: { "Content-Type": "application/json" },
              body: JSON.stringify(payload),
            });
          } catch (netErr) {
            // Network down / offline / DNS — never leave the user with a dead button.
            throw new Error("Vi får ikke kontakt akkurat nå. Sjekk nettet og prøv igjen, eller ta kontakt på Instagram 🤎");
          }

          // Parse defensively — a 500 may return HTML, not JSON.
          let d = {};
          try { d = await res.json(); } catch (parseErr) { d = {}; }

          if (!res.ok) {
            throw new Error(d.error || "Noe gikk galt. Prøv igjen om litt, eller ta kontakt på Instagram 🤎");
          }

          setDone(d);
          // reset cart but keep contact details out for privacy
          setQty({});
          setSalt({});
          setDiscountInfo(null);
          setDiscountInput("");
          setDiscountErr("");
          if (scrollRef.current) scrollRef.current.scrollTop = 0;
        } catch (e) {
          setError((e && e.message) ? e.message : "Noe gikk galt. Prøv igjen 🤎");
        } finally {
          setSubmitting(false);
        }
      })();
    }

    if (!open) return null;

    // ── Styles ──
    const overlay = {
      position: "fixed", inset: 0, zIndex: 200,
      background: "rgba(61,34,17,0.46)", backdropFilter: "blur(6px)",
      WebkitBackdropFilter: "blur(6px)",
      display: "flex", justifyContent: "center", alignItems: "flex-end",
    };
    const sheet = {
      background: C.cream, width: "100%", maxWidth: 560,
      maxHeight: "94vh", display: "flex", flexDirection: "column",
      borderRadius: "20px 20px 0 0", overflow: "hidden",
      boxShadow: "0 -20px 80px rgba(61,34,17,0.4)",
    };
    const header = {
      display: "flex", alignItems: "center", justifyContent: "space-between",
      padding: "18px 22px", borderBottom: "1px solid " + C.rule,
      background: C.paper, flexShrink: 0,
    };
    const body = { overflowY: "auto", padding: "20px 22px 28px", flex: 1 };
    const label = { fontFamily: C.mono, fontSize: 10, letterSpacing: "0.18em", textTransform: "uppercase", color: C.caramelDk, marginBottom: 10, display: "block" };
    const input = {
      width: "100%", padding: "13px 14px", border: "1px solid " + C.ruleStrong,
      borderRadius: 12, background: C.paper, fontFamily: C.sans, fontSize: 16,
      color: C.choc, marginBottom: 12,
    };
    const sectionGap = { marginTop: 26 };

    const e = React.createElement;

    // ── Thank-you screen ──
    if (done) {
      const depositLine = done.payment_plan === "deposit"
        ? "Bestillingen er over 500 kr, så vi ber om 50 % depositum via Vipps dagen før — resten betales ved henting/levering."
        : "Vi sender Vipps-betaling dagen før henting/levering for å reservere bestillingen.";
      return e("div", { style: overlay, onClick: close },
        e("div", { style: sheet, onClick: (ev) => ev.stopPropagation() },
          e("div", { style: header },
            e("span", { style: { fontFamily: C.serif, fontSize: 24, color: C.choc } }, "Takk 🍪"),
            e("button", { onClick: close, style: { fontSize: 26, color: C.choc, lineHeight: 1 }, "aria-label": "Lukk" }, "×")
          ),
          e("div", { style: { padding: "32px 26px 40px", textAlign: "center" } },
            e("div", { style: { fontFamily: C.serif, fontSize: 30, lineHeight: 1.1, color: C.choc, marginBottom: 16 } }, "Bestillingen er mottatt"),
            e("p", { style: { fontFamily: C.sans, fontSize: 16, lineHeight: 1.6, color: C.caramelDk, maxWidth: "34ch", margin: "0 auto 20px" } },
              "Bestillingen bekreftes manuelt. Vi kontakter deg på " + phone + " for å avtale detaljer."),
            e("div", { style: { background: C.paper, border: "1px solid " + C.rule, borderRadius: 14, padding: "18px 20px", margin: "0 auto", maxWidth: 380 } },
              e("div", { style: { fontFamily: C.mono, fontSize: 10, letterSpacing: "0.16em", textTransform: "uppercase", color: C.caramelDk, marginBottom: 10 } }, "Betaling"),
              e("p", { style: { fontFamily: C.sans, fontSize: 15, lineHeight: 1.6, color: C.choc, margin: 0 } }, depositLine + " 🤎"),
              e("div", { style: { marginTop: 14, fontFamily: C.mono, fontSize: 14, color: C.choc } }, "Totalt: " + fmtKr(done.total))
            ),
            e("button", { onClick: close, style: { marginTop: 28, padding: "14px 34px", background: C.choc, color: C.paper, borderRadius: 999, fontFamily: C.sans, fontSize: 11, fontWeight: 600, letterSpacing: "0.12em", textTransform: "uppercase" } }, "Lukk")
          )
        )
      );
    }

    return e("div", { style: overlay, onClick: close },
      e("div", { style: sheet, onClick: (ev) => ev.stopPropagation() },
        // Header
        e("div", { style: header },
          e("span", { style: { fontFamily: C.serif, fontSize: 26, color: C.choc } }, "Bestill cookies"),
          e("button", { onClick: close, style: { fontSize: 28, color: C.choc, lineHeight: 1 }, "aria-label": "Lukk" }, "×")
        ),

        // Body
        e("div", { style: body, ref: scrollRef },
          loading && e("div", { style: { padding: "40px 0", textAlign: "center", fontFamily: C.mono, fontSize: 12, color: C.caramelDk } }, "Laster meny…"),

          !loading && groups.some((g) => g.mini) && e("div", { style: {
            fontFamily: C.sans, fontSize: 13, color: C.caramelDk, lineHeight: 1.5,
            marginBottom: 6,
          } }, "Velg størrelse på hver cookie – vanlig eller mini (ca. 50 g) 🍪"),

          !loading && sections.map((sec, si) =>
            e(React.Fragment, { key: "sec" + si },
              sec.label && e("div", { style: {
                fontFamily: C.mono, fontSize: 10, letterSpacing: "0.22em",
                textTransform: "uppercase", color: C.caramelDk, opacity: 0.7,
                margin: si === 0 ? "2px 0 4px" : "22px 0 4px",
              } }, sec.label),
              sec.groups.map((g) => {
            const isFlavor = !!g.isFlavor;
            const flavorIdx = isFlavor ? Math.min(flavorSel[g.key] || 0, g.flavors.length - 1) : 0;
            const fl = isFlavor ? g.flavors[flavorIdx] : null;
            const regular = isFlavor ? fl.regular : g.regular;
            const mini = isFlavor ? fl.mini : g.mini;
            const displayName = isFlavor ? g.displayName : g.base.name;
            const hasMini = !!mini;
            const sel = hasMini && sizeSel[g.key] === "mini" ? "mini" : "regular";
            const active = sel === "mini" ? mini : regular;
            const soldOut = active.availability === "sold_out";
            const n = qty[active.id] || 0;
            const nReg = qty[regular.id] || 0;
            const nMini = hasMini ? (qty[mini.id] || 0) : 0;
            const saltVal = salt[active.id] || "";
            const saltable = hasSalt(regular);

            // One half of the size toggle.
            const sizeBtn = (variant, prod, count) => {
              const isSel = sel === variant;
              const isSoldOut = prod.availability === "sold_out";
              return e("button", {
                key: variant, type: "button",
                onClick: () => setSizeSel((s) => ({ ...s, [g.key]: variant })),
                "aria-pressed": isSel,
                style: {
                  flex: 1, padding: "9px 12px", borderRadius: 12,
                  border: "1px solid " + (isSel ? C.choc : C.ruleStrong),
                  background: isSel ? C.choc : C.paper,
                  color: isSel ? C.paper : C.choc,
                  cursor: "pointer", textAlign: "left",
                  display: "flex", flexDirection: "column", gap: 2,
                  transition: "background 0.18s ease, color 0.18s ease, border-color 0.18s ease",
                },
              },
                e("span", { style: { fontFamily: C.sans, fontSize: 13, fontWeight: 600 } },
                  variant === "mini" ? "Mini" : "Vanlig",
                  count > 0 ? e("span", { style: { marginLeft: 6, fontFamily: C.mono, fontSize: 11, fontWeight: 400, opacity: 0.85 } }, "· " + count + " i kurv") : null
                ),
                e("span", { style: { fontFamily: C.mono, fontSize: 11, opacity: isSel ? 0.85 : 0.6 } },
                  (variant === "mini" ? "50 g" : regWeight(prod.name) + " g") + " · " + fmtKr(prod.price_nok) + (isSoldOut ? " · utsolgt" : ""))
              );
            };

            // One flavour chip (flavour groups only).
            const flavorBtn = (f, idx) => {
              const isSel = flavorIdx === idx;
              const cnt = (qty[f.regular.id] || 0) + (f.mini ? (qty[f.mini.id] || 0) : 0);
              return e("button", {
                key: f.flavor, type: "button",
                onClick: () => setFlavorSel((s) => ({ ...s, [g.key]: idx })),
                "aria-pressed": isSel,
                style: {
                  flex: 1, padding: "8px 6px", borderRadius: 10,
                  border: "1px solid " + (isSel ? C.choc : C.ruleStrong),
                  background: isSel ? C.choc : C.paper,
                  color: isSel ? C.paper : C.choc,
                  cursor: "pointer", textAlign: "center",
                  fontFamily: C.sans, fontSize: 12, fontWeight: 600,
                  transition: "background 0.18s ease, color 0.18s ease, border-color 0.18s ease",
                },
              },
                f.flavor,
                cnt > 0 ? e("span", { style: { marginLeft: 5, fontFamily: C.mono, fontSize: 10, fontWeight: 400, opacity: 0.85 } }, "·" + cnt) : null
              );
            };

            return e("div", { key: g.key, style: {
              padding: "14px 0", borderBottom: "1px solid " + C.rule,
            } },
              e("div", { style: { display: "flex", alignItems: "center", gap: 14 } },
                active.image_url && e("img", { src: active.image_url, alt: "", style: { width: 60, height: 60, borderRadius: "50%", objectFit: "cover", flexShrink: 0, background: C.soft } }),
                e("div", { style: { flex: 1, minWidth: 0 } },
                  e("div", { style: { fontFamily: C.serif, fontSize: 22, color: C.choc, lineHeight: 1.05 } }, displayName),
                  !hasMini && e("div", { style: { fontFamily: C.mono, fontSize: 12, color: C.choc, marginTop: 5 } }, soldOut ? "Utsolgt" : fmtKr(regular.price_nok))
                ),
                !soldOut && e(Stepper, { value: n, onDec: () => setItem(active.id, -1), onInc: () => setItem(active.id, 1), disabledInc: atMax })
              ),
              // Flavour toggle (flavour groups only) — swaps the image above.
              isFlavor && e("div", { style: { display: "flex", gap: 6, marginTop: 12 } },
                g.flavors.map((f, idx) => flavorBtn(f, idx))
              ),
              // Size toggle — when a mini variant exists.
              hasMini && e("div", { style: { display: "flex", gap: 8, marginTop: 8 } },
                sizeBtn("regular", regular, nReg),
                sizeBtn("mini", mini, nMini)
              ),
              // Salt picker — for the SELECTED size, when it's in the cart, for salted cookies.
              !soldOut && n > 0 && saltable && e("div", { style: {
                marginTop: 10, paddingTop: 10,
                borderTop: "1px dashed " + C.rule,
                display: "flex", alignItems: "center", flexWrap: "wrap", gap: 8,
              } },
                e("span", { style: {
                  fontFamily: C.mono, fontSize: 10, letterSpacing: "0.16em",
                  textTransform: "uppercase", color: C.caramelDk, opacity: 0.78,
                  marginRight: 4,
                } }, hasMini ? "🧂 Salt · " + (sel === "mini" ? "mini" : "vanlig") : "🧂 Salt"),
                SALT_OPTIONS.map((opt) => {
                  const isOn = saltVal === opt.value;
                  return e("button", {
                    key: opt.value || "vanlig",
                    type: "button",
                    onClick: () => setSaltFor(active.id, opt.value),
                    "aria-pressed": isOn,
                    style: {
                      padding: "6px 12px",
                      borderRadius: 999,
                      border: "1px solid " + (isOn ? C.choc : C.ruleStrong),
                      background: isOn ? C.choc : C.paper,
                      color: isOn ? C.paper : C.choc,
                      fontFamily: C.sans, fontSize: 12, fontWeight: 500,
                      lineHeight: 1.2,
                      transition: "background 0.18s ease, color 0.18s ease, border-color 0.18s ease",
                    },
                  }, opt.label);
                })
              )
            );
              })
            )
          ),

          !loading && atMax && e("div", { style: { marginTop: 12, fontFamily: C.sans, fontSize: 13, color: C.caramelDk, lineHeight: 1.5 } },
            "Maks " + maxQty + " cookies per bestilling. Ønsker du større bestilling? Ta kontakt 🤎"),

          // Fulfillment
          !loading && e("div", { style: sectionGap },
            e("span", { style: label }, "Henting eller levering"),
            e("div", { style: { display: "flex", gap: 10, marginBottom: 6 } },
              ["pickup", "delivery"].map((mode) => e("button", {
                key: mode, type: "button", onClick: () => setFulfill(mode),
                style: {
                  flex: 1, padding: "12px 0", borderRadius: 12,
                  border: "1px solid " + (fulfill === mode ? C.choc : C.ruleStrong),
                  background: fulfill === mode ? C.choc : C.paper,
                  color: fulfill === mode ? C.paper : C.choc,
                  fontFamily: C.sans, fontSize: 14, fontWeight: 500,
                },
              }, mode === "pickup" ? "Henting" : "Levering"))
            )
          ),

          // Pickup fields
          !loading && fulfill === "pickup" && e("div", { style: { marginTop: 14 } },
            e("div", { style: { position: "relative", marginBottom: 12 } },
              e("input", {
                type: "date",
                min: todayPlus(2),
                value: pickupDate,
                // Desktop Chrome only opens the picker from the calendar icon;
                // calling showPicker() on click opens it from anywhere. Wrapped
                // in try/catch because some browsers throw (and mobile already
                // opens the native picker on tap without this).
                onClick: (ev) => { try { ev.currentTarget.showPicker && ev.currentTarget.showPicker(); } catch (e) {} },
                onChange: (ev) => {
                  const v = ev.target.value;
                  if (isSunday(v)) {
                    setPickupDate("");
                    setError("Vi har dessverre ikke henting på søndager. Velg en annen dag.");
                  } else if (blockedDates.includes(v)) {
                    setPickupDate("");
                    setError("Vi har dessverre stengt denne dagen. Velg en annen 🤎");
                  } else {
                    setPickupDate(v);
                    setError("");
                  }
                },
                style: { ...input, marginBottom: 0, position: "relative", zIndex: 1 },
              }),
              // Placeholder is PURELY visual (pointer-events: none) so taps pass
              // straight through to the native date input underneath. On mobile,
              // tapping anywhere on a date input opens the picker — the earlier
              // click-catching overlay broke that and left users stuck.
              !pickupDate && e("div", {
                "aria-hidden": "true",
                style: {
                  position: "absolute",
                  top: 1, left: 1, right: 1, bottom: 1,
                  borderRadius: 11,
                  background: C.paper,
                  pointerEvents: "none",
                  zIndex: 2,
                  display: "flex", alignItems: "center",
                  padding: "0 14px",
                  fontFamily: C.sans, fontSize: 16,
                  color: "rgba(139, 90, 60, 0.55)",
                },
              }, "Velg dato")
            ),
            e("select", { value: pickupWindow, onChange: (ev) => setPickupWindow(ev.target.value), style: input },
              e("option", { value: "" }, "Velg tidsvindu…"),
              pickupWindows.map((w) => e("option", { key: w, value: w }, "kl " + w))
            ),
            e("p", { style: { fontFamily: C.sans, fontSize: 12, color: C.caramelDk, lineHeight: 1.5, margin: "2px 0 0" } },
              "Henting i Lier, mandag–lørdag. Vi bekrefter tidspunkt manuelt. Bestill minst 2 dager før – større bestillinger (selskap, dåp, konfirmasjon) gjerne en uke i forveien.")
          ),

          // Delivery fields
          !loading && fulfill === "delivery" && e("div", { style: { marginTop: 14 } },
            e("input", { type: "text", placeholder: "Adresse", value: address, onChange: (ev) => setAddress(ev.target.value), style: input }),
            e("input", { type: "text", inputMode: "numeric", placeholder: "Postnummer", value: postnummer, onChange: (ev) => setPostnummer(ev.target.value), style: input }),
            e("select", { value: kommune, onChange: (ev) => setKommune(ev.target.value), style: input },
              e("option", { value: "" }, "Velg kommune…"),
              zones.map((z) => e("optgroup", { key: z.zone, label: z.label + " — " + (z.fee_nok === 0 ? "gratis" : fmtKr(z.fee_nok)) },
                z.kommuner.map((k) => e("option", { key: k, value: k }, k))
              ))
            ),
            kommune && deliveryKnown && e("div", { style: { fontFamily: C.mono, fontSize: 13, color: C.choc, marginBottom: 6 } },
              "Leveringspris: " + (deliveryFee === 0 ? "gratis" : fmtKr(deliveryFee))),
            e("p", { style: { fontFamily: C.sans, fontSize: 12, color: C.caramelDk, lineHeight: 1.5, margin: "2px 0 0" } },
              "Levering bekreftes manuelt. Vi kontakter deg om tidspunkt.")
          ),

          // Customer
          !loading && e("div", { style: sectionGap },
            e("span", { style: label }, "Dine opplysninger"),
            e("input", { type: "text", placeholder: "Navn *", value: name, onChange: (ev) => setName(ev.target.value), style: input }),
            e("input", { type: "tel", inputMode: "tel", placeholder: "Mobilnummer *", value: phone, onChange: (ev) => setPhone(ev.target.value), style: input }),
            e("textarea", { placeholder: "Kommentar (valgfritt)", value: comment, onChange: (ev) => setComment(ev.target.value), rows: 2, style: { ...input, resize: "vertical" } }),
            // Honeypot — visually hidden
            e("input", { type: "text", tabIndex: -1, autoComplete: "off", value: company, onChange: (ev) => setCompany(ev.target.value), style: { position: "absolute", left: "-9999px", width: 1, height: 1, opacity: 0 }, "aria-hidden": "true" })
          ),

          // Discount code
          !loading && totalQty > 0 && e("div", { style: { marginTop: 16 } },
            e("span", { style: label }, "Rabattkode"),
            discountInfo
              ? e("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10, background: C.paper, border: "1px solid " + C.choc, borderRadius: 12, padding: "10px 14px" } },
                  e("span", { style: { fontFamily: C.sans, fontSize: 14, color: C.choc, fontWeight: 600 } }, discountInfo.code + " · −" + fmtKr(discount)),
                  e("button", { type: "button", onClick: clearCode, style: { fontFamily: C.sans, fontSize: 12, color: C.caramelDk, textDecoration: "underline", cursor: "pointer", background: "none", border: "none" } }, "Fjern")
                )
              : e("div", { style: { display: "flex", gap: 8 } },
                  e("input", {
                    type: "text", placeholder: "Har du en kode?", value: discountInput,
                    onChange: (ev) => setDiscountInput(ev.target.value),
                    onKeyDown: (ev) => { if (ev.key === "Enter") { ev.preventDefault(); applyCode(); } },
                    style: { ...input, marginBottom: 0, flex: 1, textTransform: "uppercase" },
                  }),
                  e("button", {
                    type: "button", onClick: applyCode, disabled: applyingCode || !discountInput.trim(),
                    style: { flexShrink: 0, padding: "0 18px", borderRadius: 12, border: "1px solid " + C.ruleStrong, background: C.paper, color: C.choc, fontFamily: C.sans, fontSize: 13, fontWeight: 600, cursor: "pointer" },
                  }, applyingCode ? "…" : "Bruk")
                ),
            discountErr && e("div", { style: { marginTop: 6, fontFamily: C.sans, fontSize: 12, color: "#a23a1f" } }, discountErr)
          ),

          // Payment note
          !loading && e("div", { style: { marginTop: 22, background: C.paper, border: "1px solid " + C.rule, borderRadius: 14, padding: "14px 16px" } },
            e("p", { style: { fontFamily: C.sans, fontSize: 13, lineHeight: 1.55, color: C.choc, margin: 0 } },
              "Vi sender Vipps dagen før 🤎"),
            e("p", { style: { fontFamily: C.sans, fontSize: 13, lineHeight: 1.55, color: C.choc, margin: "10px 0 0" } },
              "På bestillinger over 500 kr tar vi 50 % i depositum på forhånd, og resten ved henting eller levering 🍪✨")
          ),

          // Allergen note
          !loading && e("div", { style: { marginTop: 12, fontFamily: C.sans, fontSize: 12, lineHeight: 1.55, color: C.caramelDk } },
            "Alle cookies inneholder hvete, egg og melk – kun Vegandrøm er vegansk og helt uten egg og melk. Alt bakes på et kjøkken med peanøtter, så spor av nøtter kan forekomme. 🤎"),

          error && e("div", { style: { marginTop: 16, color: "#a23a1f", fontFamily: C.sans, fontSize: 14, lineHeight: 1.5 } }, error)
        ),

        // Footer / submit
        e("div", { style: { padding: "14px 22px", borderTop: "1px solid " + C.rule, background: C.paper, flexShrink: 0 } },
          discount > 0 && e("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 6, fontFamily: C.mono, fontSize: 12, color: C.caramelDk } },
            e("span", null, "Rabatt" + (discountInfo ? " · " + discountInfo.code : "")),
            e("span", null, "−" + fmtKr(discount))
          ),
          e("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12, fontFamily: C.mono, fontSize: 13, color: C.choc } },
            e("span", null, totalQty + " cookies"),
            e("span", null, "Totalt " + fmtKr(total))
          ),
          e("button", {
            onClick: submit, disabled: submitting || loading,
            style: {
              width: "100%", padding: "16px 0", borderRadius: 999,
              background: C.choc, color: C.paper, fontFamily: C.sans,
              fontSize: 13, fontWeight: 600, letterSpacing: "0.1em", textTransform: "uppercase",
              opacity: submitting ? 0.6 : 1,
            },
          }, submitting ? "Sender…" : "Send bestilling 🍪")
        )
      )
    );
  }

  window.OrderOverlay = OrderOverlay;
  window.openOrder = function () { window.dispatchEvent(new Event("dc-open-order")); };
})();
