/* Yan's Cakes desktop — Home below-fold sections, Category, Product, Search. */

function DHomeSections({ onNav, onAdd }) {
  const { categories, byId, fmt } = window.YANS;
  const best = ["sr-pandan", "t-egg-box", "c-cotton", "t-fruit", "h-signature", "s-pineapple", "sr-oreo", "b-rum"].map(byId);
  return (
    <main>
      {/* categories */}
      <section style={{ maxWidth: 1240, margin: "0 auto", padding: "var(--section-y) 32px var(--space-8)" }}>
        <div style={{ textAlign: "center", marginBottom: "var(--space-7)" }}>
          <DEyebrow center>Shop by category</DEyebrow>
          <h2 style={{ fontFamily: "var(--font-display)", fontWeight: 400, fontSize: "clamp(2rem,3.4vw,3rem)", color: "var(--text-primary)", margin: "14px 0 0" }}>From our kitchen to your table</h2>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(6,1fr)", gap: 18 }}>
          {categories.map((c) => (
            <button key={c.name} onClick={() => onNav({ s: "category", cat: c.name })} style={{ border: 0, background: "transparent", cursor: "pointer", textAlign: "center", padding: 0 }}>
              <div style={{ aspectRatio: "1", borderRadius: "50%", overflow: "hidden", marginBottom: 12, boxShadow: "var(--shadow-md)", border: "1px solid var(--border-subtle)" }}>
                <img src={window.catThumb(c.name)} alt={c.name} style={{ width: "100%", height: "100%", objectFit: "cover" }} />
              </div>
              <div style={{ fontFamily: "var(--font-display)", fontSize: 18, color: "var(--text-primary)" }}>{c.name}</div>
            </button>
          ))}
        </div>
      </section>

      {/* bestsellers */}
      <section style={{ background: "var(--surface-sunken)" }}>
        <div style={{ maxWidth: 1240, margin: "0 auto", padding: "var(--section-y) 32px" }}>
          <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", marginBottom: "var(--space-6)" }}>
            <div><DEyebrow>Bestsellers</DEyebrow><h2 style={{ fontFamily: "var(--font-display)", fontWeight: 400, fontSize: "clamp(2rem,3.2vw,2.8rem)", color: "var(--text-primary)", margin: "12px 0 0" }}>Loved by regulars</h2></div>
            <button onClick={() => onNav({ s: "category", cat: "Swiss Rolls" })} style={ghostBtn}>View full menu</button>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 24 }}>
            {best.map((p) => <DProductCard key={p.id} p={p} onNav={onNav} onAdd={onAdd} />)}
          </div>
        </div>
      </section>

      {/* story split */}
      <section style={{ maxWidth: 1140, margin: "0 auto", padding: "var(--section-y) 32px" }}>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "clamp(2rem,5vw,4.5rem)", alignItems: "center" }}>
          <div style={{ borderRadius: "var(--radius-lg)", overflow: "hidden", boxShadow: "var(--shadow-lg)", aspectRatio: "4/3" }}>
            <img src="photos/signature-set.jpg" alt="" style={{ width: "100%", height: "100%", objectFit: "cover" }} />
          </div>
          <div>
            <DEyebrow>Our story</DEyebrow>
            <h2 style={{ fontFamily: "var(--font-display)", fontWeight: 400, fontSize: "clamp(2rem,3.2vw,2.9rem)", color: "var(--text-primary)", margin: "14px 0 16px", lineHeight: 1.1 }}>A neighbourhood tradition since 1992</h2>
            <p style={{ fontFamily: "var(--font-body)", fontSize: "1.05rem", lineHeight: 1.75, color: "var(--text-body)", margin: 0 }}>From a single counter at Yan&rsquo;s Cakes, we&rsquo;ve been baking Swiss rolls, egg tarts and festive favourites the same careful way for over thirty years — small batches, quality ingredients, and recipes regulars have grown up with.</p>
            <p style={{ fontFamily: "var(--font-display)", fontStyle: "italic", fontSize: "1.45rem", color: "var(--gold-deep)", margin: "20px 0 26px" }}>Delicious to the very last bite.</p>
            <button onClick={() => onNav({ s: "info", topic: "Our Story" })} style={ghostBtn}>Read our story</button>
          </div>
        </div>
      </section>
    </main>
  );
}

function DCategory({ category, onNav, onAdd }) {
  const { products, categories } = window.YANS;
  const [active, setActive] = React.useState(category);
  React.useEffect(() => { setActive(category); }, [category]);
  const [sort, setSort] = React.useState("Popular");
  let items = products.filter((p) => p.cat === active);
  if (sort === "Price ↑") items = [...items].sort((a, b) => a.price - b.price);
  if (sort === "Price ↓") items = [...items].sort((a, b) => b.price - a.price);
  return (
    <div style={{ maxWidth: 1240, margin: "0 auto", padding: "40px 32px 80px" }}>
      <DEyebrow>Shop</DEyebrow>
      <h1 style={{ fontFamily: "var(--font-display)", fontWeight: 400, fontSize: "clamp(2.2rem,4vw,3.4rem)", color: "var(--text-primary)", margin: "10px 0 6px" }}>{active}</h1>
      <p style={{ fontFamily: "var(--font-body)", fontSize: 15, color: "var(--text-body)", margin: "0 0 28px" }}>{items.length} handcrafted {active.toLowerCase()} — baked fresh and kept chilled for you.</p>
      <div style={{ display: "grid", gridTemplateColumns: "210px 1fr", gap: 36, alignItems: "start" }}>
        <aside>
          <div style={{ fontFamily: "var(--font-body)", fontSize: 11, fontWeight: 700, letterSpacing: "0.14em", textTransform: "uppercase", color: "var(--text-secondary)", marginBottom: 14 }}>Categories</div>
          <div style={{ display: "flex", flexDirection: "column", gap: 2, marginBottom: 28 }}>
            {categories.map((c) => (
              <button key={c.name} onClick={() => setActive(c.name)} style={{ border: 0, background: c.name === active ? "var(--accent-soft)" : "transparent", cursor: "pointer", textAlign: "left", padding: "9px 12px", borderRadius: "var(--radius-md)", fontFamily: "var(--font-body)", fontSize: 14, fontWeight: c.name === active ? 700 : 500, color: c.name === active ? "var(--gold-deep)" : "var(--text-body)" }}>{c.name}</button>
            ))}
          </div>
          <div style={{ fontFamily: "var(--font-body)", fontSize: 11, fontWeight: 700, letterSpacing: "0.14em", textTransform: "uppercase", color: "var(--text-secondary)", marginBottom: 12 }}>Sort by</div>
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            {["Popular", "Price ↑", "Price ↓"].map((s) => (
              <button key={s} onClick={() => setSort(s)} style={{ border: 0, background: "transparent", cursor: "pointer", textAlign: "left", padding: 0, fontFamily: "var(--font-body)", fontSize: 13.5, color: sort === s ? "var(--gold-deep)" : "var(--text-secondary)", fontWeight: sort === s ? 700 : 500, display: "flex", alignItems: "center", gap: 7 }}>
                <span style={{ width: 14, height: 14, borderRadius: "50%", border: `2px solid ${sort === s ? "var(--gold)" : "var(--border-strong)"}`, background: sort === s ? "var(--gold)" : "transparent" }} />{s}
              </button>
            ))}
          </div>
        </aside>
        <div>
          {(() => {
            const subs = [...new Set(items.map((p) => p.sub).filter(Boolean))];
            if (subs.length === 0) {
              return <div style={{ display: "grid", gridTemplateColumns: "repeat(3,1fr)", gap: 24 }}>{items.map((p) => <DProductCard key={p.id} p={p} onNav={onNav} onAdd={onAdd} />)}</div>;
            }
            return subs.map((sg) => (
              <div key={sg} style={{ marginBottom: 36 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 16 }}>
                  <span style={{ fontFamily: "var(--font-display)", fontSize: 26, color: "var(--text-primary)" }}>{sg}</span>
                  <span style={{ flex: 1, height: 1, background: "var(--hairline-gold)" }} />
                </div>
                <div style={{ display: "grid", gridTemplateColumns: "repeat(3,1fr)", gap: 24 }}>
                  {items.filter((p) => p.sub === sg).map((p) => <DProductCard key={p.id} p={p} onNav={onNav} onAdd={onAdd} />)}
                </div>
              </div>
            ));
          })()}
        </div>
      </div>
    </div>
  );
}

function DProduct({ product, onNav, onAdd, onAddLine }) {
  const { products, fmt } = window.YANS;
  const [qty, setQty] = React.useState(1);
  const related = products.filter((p) => p.cat === product.cat && p.id !== product.id).slice(0, 4);
  const chilled = product.cat === "Seasonal" ? "Store in a cool, dry place. See expiry on the tub." : "Keep refrigerated. Best enjoyed within 2–3 days of delivery.";
  return (
    <div style={{ maxWidth: 1140, margin: "0 auto", padding: "32px 32px 80px" }}>
      <button onClick={() => onNav({ s: "category", cat: product.cat })} style={{ border: 0, background: "transparent", cursor: "pointer", fontFamily: "var(--font-body)", fontSize: 13, color: "var(--text-secondary)", display: "flex", alignItems: "center", gap: 6, marginBottom: 22, padding: 0 }}><i className="ph-light ph-caret-left" /> Back to {product.cat}</button>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 48, alignItems: "start" }}>
        <div>
          <div style={{ borderRadius: "var(--radius-lg)", overflow: "hidden", boxShadow: "var(--shadow-lg)", aspectRatio: "1" }}><img src={product.img} alt={product.name} style={{ width: "100%", height: "100%", objectFit: "cover" }} /></div>
          {Array.isArray(product.gallery) && product.gallery.length > 1 && (
            <div style={{ display: "flex", gap: 10, marginTop: 14 }}>{product.gallery.map((im, i) => <div key={i} style={{ flex: 1, aspectRatio: "1", borderRadius: "var(--radius-md)", overflow: "hidden", border: i === 0 ? "2px solid var(--gold)" : "1px solid var(--border-subtle)", opacity: i === 0 ? 1 : 0.7 }}><img src={im} alt="" style={{ width: "100%", height: "100%", objectFit: "cover" }} /></div>)}</div>
          )}
        </div>
        <div>
          <DEyebrow>{product.cat}</DEyebrow>
          <h1 style={{ fontFamily: "var(--font-display)", fontWeight: 500, fontSize: "clamp(2.2rem,3.5vw,3rem)", lineHeight: 1.08, color: "var(--text-primary)", margin: "12px 0 12px" }}>{product.name}</h1>
          <div style={{ fontFamily: "var(--font-display)", fontSize: 34, color: "var(--gold-deep)", marginBottom: 18 }}>{product.soldOut ? "" : (product.rollMode ? fmt(product.price) : ((product.tartMode || product.htMode || product.variants) ? "from " + fmt(product.price) : fmt(product.price)))}</div>
          <p style={{ fontFamily: "var(--font-body)", fontSize: 16, lineHeight: 1.75, color: "var(--text-body)", margin: "0 0 26px" }}>{product.desc}</p>
          {product.soldOut
            ? <div style={{ marginBottom: 22, display: "inline-flex", alignItems: "center", gap: 9, padding: "14px 20px", borderRadius: "var(--radius-md)", background: "var(--surface-sunken)", color: "var(--text-secondary)", fontFamily: "var(--font-body)", fontSize: 15, fontWeight: 700 }}><i className="ph-light ph-prohibit" style={{ fontSize: 20 }} /> Currently out of stock</div>
            : product.htMode
            ? <div style={{ marginBottom: 22 }}><HighTeaBuilder onAddLine={onAddLine} /></div>
            : product.tartMode
            ? <div style={{ marginBottom: 22 }}><TartPurchase product={product} onAddLine={onAddLine} /></div>
            : product.variants
            ? <div style={{ marginBottom: 22 }}><VariantPurchase product={product} onAddLine={onAddLine} /></div>
            : product.rollMode
            ? <div style={{ marginBottom: 22 }}><RollAssortment product={product} onAddLine={onAddLine} /></div>
            : <div style={{ display: "flex", gap: 14, marginBottom: 22 }}>
            <div style={{ display: "flex", alignItems: "center", border: "1px solid var(--border-default)", borderRadius: 999, overflow: "hidden", background: "var(--surface-raised)" }}>
              <button onClick={() => setQty((q) => Math.max(1, q - 1))} style={dStep} aria-label="Decrease"><i className="ph-light ph-minus" /></button>
              <span style={{ minWidth: 44, textAlign: "center", fontFamily: "var(--font-body)", fontSize: 17, fontWeight: 700 }}>{qty}</span>
              <button onClick={() => setQty((q) => q + 1)} style={dStep} aria-label="Increase"><i className="ph-light ph-plus" /></button>
            </div>
            <button onClick={() => onAdd(product, qty)} style={{ flex: 1, height: 54, border: 0, borderRadius: "var(--radius-md)", background: "var(--chocolate)", color: "var(--ivory)", fontFamily: "var(--font-body)", fontSize: 15.5, fontWeight: 700, cursor: "pointer" }}>Add to box · {fmt(product.price * qty)}</button>
          </div>}
          <div style={{ display: "flex", gap: 12, alignItems: "flex-start", padding: "15px 17px", background: "var(--surface-sunken)", borderRadius: "var(--radius-md)" }}>
            <i className="ph-light ph-snowflake" style={{ fontSize: 22, color: "var(--gold-deep)", flex: "none", marginTop: 1 }} />
            <div><div style={{ fontFamily: "var(--font-body)", fontSize: 13.5, fontWeight: 700, color: "var(--text-primary)" }}>Storage &amp; freshness</div><div style={{ fontFamily: "var(--font-body)", fontSize: 13.5, color: "var(--text-secondary)", lineHeight: 1.55 }}>{chilled}</div></div>
          </div>
        </div>
      </div>
      {related.length > 0 && (
        <div style={{ marginTop: 70 }}>
          <DEyebrow>You may also like</DEyebrow>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 24, marginTop: 18 }}>{related.map((p) => <DProductCard key={p.id} p={p} onNav={onNav} onAdd={onAdd} />)}</div>
        </div>
      )}
    </div>
  );
}

function DSearch({ onNav, onAdd }) {
  const { products, fmt } = window.YANS;
  const [q, setQ] = React.useState("");
  const results = q ? products.filter((p) => (p.name + " " + p.cat).toLowerCase().includes(q.toLowerCase())) : [];
  return (
    <div style={{ maxWidth: 900, margin: "0 auto", padding: "48px 32px 80px" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12, border: "1px solid var(--border-default)", borderRadius: 999, padding: "0 20px", background: "var(--surface-raised)", boxShadow: "var(--shadow-inset)", marginBottom: 30 }}>
        <i className="ph-light ph-magnifying-glass" style={{ fontSize: 22, color: "var(--text-muted)" }} />
        <input autoFocus value={q} onChange={(e) => setQ(e.target.value)} placeholder="Search cakes, tarts, rolls…" style={{ flex: 1, border: 0, outline: "none", background: "transparent", padding: "17px 0", fontFamily: "var(--font-body)", fontSize: 17, color: "var(--text-primary)" }} />
      </div>
      {!q && <div><DEyebrow>Popular</DEyebrow><div style={{ display: "flex", gap: 10, flexWrap: "wrap", marginTop: 14 }}>{["Egg tart", "Pandan", "Cheesecake", "Pineapple"].map((t) => <button key={t} onClick={() => setQ(t)} style={{ border: "1px solid var(--border-default)", background: "var(--surface-card)", borderRadius: 999, padding: "10px 18px", fontFamily: "var(--font-body)", fontSize: 14, color: "var(--text-secondary)", cursor: "pointer" }}>{t}</button>)}</div></div>}
      {q && <div style={{ display: "grid", gridTemplateColumns: "repeat(3,1fr)", gap: 24 }}>{results.map((p) => <DProductCard key={p.id} p={p} onNav={onNav} onAdd={onAdd} />)}</div>}
      {q && results.length === 0 && <p style={{ textAlign: "center", color: "var(--text-muted)", fontFamily: "var(--font-body)", padding: "50px 0" }}>No matches for &ldquo;{q}&rdquo;.</p>}
    </div>
  );
}

/* shared */
function DEyebrow({ children, center }) {
  return <div style={{ display: "flex", alignItems: "center", gap: 12, justifyContent: center ? "center" : "flex-start" }}><span style={{ height: 1, width: 30, background: "var(--hairline-gold)" }} /><span className="eyebrow">{children}</span>{center && <span style={{ height: 1, width: 30, background: "var(--hairline-gold)" }} />}</div>;
}
function DProductCard({ p, onNav, onAdd }) {
  const { fmt } = window.YANS;
  const [h, setH] = React.useState(false);
  return (
    <div onMouseEnter={() => setH(true)} onMouseLeave={() => setH(false)} style={{ background: "var(--surface-card)", border: "1px solid var(--border-subtle)", borderRadius: "var(--radius-lg)", overflow: "hidden", boxShadow: h ? "var(--shadow-lg)" : "var(--shadow-md)", transform: h ? "translateY(-3px)" : "none", transition: "box-shadow .24s, transform .24s", display: "flex", flexDirection: "column" }}>
      <div onClick={() => onNav({ s: "product", p })} style={{ aspectRatio: "4/3", overflow: "hidden", cursor: "pointer" }}><img src={p.img} alt={p.name} style={{ width: "100%", height: "100%", objectFit: "cover", transform: h ? "scale(1.05)" : "scale(1)", transition: "transform .4s" }} /></div>
      <div style={{ padding: "16px", display: "flex", flexDirection: "column", flex: 1 }}>
        <h3 onClick={() => onNav({ s: "product", p })} style={{ fontFamily: "var(--font-display)", fontWeight: 500, fontSize: 21, color: "var(--text-primary)", lineHeight: 1.12, margin: "0 0 6px", cursor: "pointer" }}>{p.name}</h3>
        <p style={{ fontFamily: "var(--font-body)", fontSize: 13, color: "var(--text-body)", lineHeight: 1.5, margin: "0 0 14px", display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical", overflow: "hidden" }}>{p.desc}</p>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginTop: "auto" }}>
          <span style={{ fontFamily: "var(--font-display)", fontSize: 24, color: p.soldOut ? "var(--text-muted)" : "var(--gold-deep)" }}>{p.soldOut ? "Sold out" : (p.rollMode ? fmt(p.price) : ((p.tartMode || p.htMode || p.variants) ? "from " + fmt(p.price) : fmt(p.price)))}</span>
          {p.soldOut
            ? <span style={{ height: 38, padding: "0 14px", borderRadius: 999, background: "var(--surface-sunken)", color: "var(--text-muted)", fontFamily: "var(--font-body)", fontSize: 12.5, fontWeight: 700, display: "flex", alignItems: "center" }}>Unavailable</span>
            : <button onClick={() => (p.tartMode || p.htMode || p.variants || p.rollMode) ? onNav({ s: "product", p }) : onAdd(p, 1)} style={{ height: 38, padding: "0 16px", borderRadius: 999, border: 0, background: "var(--chocolate)", color: "var(--ivory)", fontFamily: "var(--font-body)", fontSize: 13, fontWeight: 700, cursor: "pointer", display: "flex", alignItems: "center", gap: 6 }}><i className={"ph-light " + ((p.tartMode || p.htMode || p.variants || p.rollMode) ? "ph-sliders-horizontal" : "ph-plus")} /> {(p.tartMode || p.htMode || p.variants || p.rollMode) ? "Choose" : "Add"}</button>}
        </div>
      </div>
    </div>
  );
}
const dStep = { width: 48, height: 52, border: 0, background: "transparent", color: "var(--text-primary)", fontSize: 17, cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center" };
const dStepSm = { width: 38, height: 40, border: 0, background: "transparent", color: "var(--text-primary)", fontSize: 14, cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center" };
const stepperWrap = { display: "flex", alignItems: "center", border: "1px solid var(--border-default)", borderRadius: 999, overflow: "hidden", background: "var(--surface-raised)", flex: "none" };
const stepperNum = { minWidth: 44, textAlign: "center", fontFamily: "var(--font-body)", fontSize: 17, fontWeight: 700, color: "var(--text-primary)" };
const addBtn = { flex: 1, height: 54, border: 0, borderRadius: "var(--radius-md)", background: "var(--chocolate)", color: "var(--ivory)", fontFamily: "var(--font-body)", fontSize: 15.5, fontWeight: 700, cursor: "pointer" };

/* Tart purchase UI: egg tart (single or box of 10) + build-your-own assorted box of 10. */
function TartPurchase({ product, onAddLine }) {
  const { tartFlavours, fmt } = window.YANS;
  const t = product.tart || { unit: product.price, boxSize: 10, boxPrice: product.price * 10 };
  const [mode, setMode] = React.useState("single");
  const [qty, setQty] = React.useState(1);
  const [counts, setCounts] = React.useState({ egg: 0, fruit: 0, cheese: 0 });
  const BOX = 10;
  const total = (counts.egg || 0) + (counts.fruit || 0) + (counts.cheese || 0);
  const boxPrice = tartFlavours.reduce((s, f) => s + f.price * (counts[f.id] || 0), 0);
  const setC = (id, n) => setCounts((c) => ({ ...c, [id]: Math.max(0, n) }));

  if (product.tartMode === "single") {
    return (
      <div>
        <div style={{ display: "flex", gap: 10, marginBottom: 16 }}>
          {[["single", "Individual", fmt(t.unit) + " each"], ["box", "Box of " + t.boxSize, fmt(t.boxPrice)]].map(([m, tt, sub]) => (
            <button key={m} onClick={() => { setMode(m); setQty(1); }} style={{ flex: 1, textAlign: "left", border: `1.5px solid ${mode === m ? "var(--gold)" : "var(--border-default)"}`, background: mode === m ? "var(--accent-soft)" : "var(--surface-card)", borderRadius: "var(--radius-md)", padding: "13px 15px", cursor: "pointer" }}>
              <div style={{ fontFamily: "var(--font-body)", fontSize: 14, fontWeight: 700, color: "var(--text-primary)" }}>{tt}</div>
              <div style={{ fontFamily: "var(--font-body)", fontSize: 12.5, color: "var(--text-secondary)", marginTop: 2 }}>{sub}</div>
            </button>
          ))}
        </div>
        <div style={{ display: "flex", gap: 14 }}>
          <div style={stepperWrap}>
            <button onClick={() => setQty((q) => Math.max(1, q - 1))} style={dStep} aria-label="Decrease"><i className="ph-light ph-minus" /></button>
            <span style={stepperNum}>{qty}</span>
            <button onClick={() => setQty((q) => q + 1)} style={dStep} aria-label="Increase"><i className="ph-light ph-plus" /></button>
          </div>
          {mode === "single"
            ? <button onClick={() => onAddLine({ id: product.id, name: product.name, price: t.unit, img: product.img, qty })} style={addBtn}>Add to box · {fmt(t.unit * qty)}</button>
            : <button onClick={() => onAddLine({ id: product.id + "-box", name: product.name + " · Box of " + t.boxSize, price: t.boxPrice, img: product.img, qty, detail: t.boxSize + " " + product.name.toLowerCase() + "s" })} style={addBtn}>Add {qty > 1 ? qty + " boxes" : "box"} · {fmt(t.boxPrice * qty)}</button>}
        </div>
      </div>
    );
  }

  // assorted build-your-own box of 10
  return (
    <div>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 12 }}>
        <span style={{ fontFamily: "var(--font-body)", fontSize: 13, fontWeight: 700, letterSpacing: "0.02em", color: "var(--text-primary)" }}>Build your box · choose {BOX}</span>
        <span style={{ fontFamily: "var(--font-body)", fontSize: 13, fontWeight: 700, color: total === BOX ? "var(--sage)" : "var(--gold-deep)" }}>{total} / {BOX}</span>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: 10, marginBottom: 16 }}>
        {tartFlavours.map((f) => (
          <div key={f.id} style={{ display: "flex", alignItems: "center", gap: 12, border: "1px solid var(--border-subtle)", borderRadius: "var(--radius-md)", padding: "10px 12px" }}>
            <div style={{ width: 46, height: 46, borderRadius: 8, overflow: "hidden", flex: "none" }}><img src={f.img} alt="" style={{ width: "100%", height: "100%", objectFit: "cover" }} /></div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontFamily: "var(--font-body)", fontSize: 14, fontWeight: 600, color: "var(--text-primary)" }}>{f.name}</div>
              <div style={{ fontFamily: "var(--font-body)", fontSize: 12.5, color: "var(--gold-deep)" }}>{fmt(f.price)} each</div>
            </div>
            <div style={stepperWrap}>
              <button onClick={() => setC(f.id, (counts[f.id] || 0) - 1)} style={dStepSm} aria-label="Decrease"><i className="ph-light ph-minus" /></button>
              <span style={{ ...stepperNum, minWidth: 34, fontSize: 15 }}>{counts[f.id] || 0}</span>
              <button onClick={() => { if (total < BOX) setC(f.id, (counts[f.id] || 0) + 1); }} style={dStepSm} aria-label="Increase"><i className="ph-light ph-plus" /></button>
            </div>
          </div>
        ))}
      </div>
      <button disabled={total !== BOX} onClick={() => {
        const detail = tartFlavours.filter((f) => counts[f.id] > 0).map((f) => counts[f.id] + " " + f.name.replace(" Tart", "")).join(" · ");
        onAddLine({ id: "t-assorted-" + Date.now(), name: "Assorted Tart Box (" + BOX + ")", price: Math.round(boxPrice * 100) / 100, img: "photos/egg-tarts-set.jpg", qty: 1, detail });
        setCounts({ egg: 0, fruit: 0, cheese: 0 });
      }} style={{ ...addBtn, width: "100%", opacity: total === BOX ? 1 : 0.5, cursor: total === BOX ? "pointer" : "not-allowed" }}>
        {total === BOX ? "Add box · " + fmt(Math.round(boxPrice * 100) / 100) : "Choose " + (BOX - total) + " more"}
      </button>
    </div>
  );
}
/* Fixed-price assorted roll box: pick N flavours from a list. */
function RollAssortment({ product, onAddLine }) {
  const { fmt } = window.YANS;
  const need = product.pick || 3;
  const [counts, setCounts] = React.useState({});
  const total = Object.values(counts).reduce((s, n) => s + n, 0);
  const setC = (f, n) => setCounts((c) => ({ ...c, [f]: Math.max(0, n) }));
  return (
    <div>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 12 }}>
        <span style={{ fontFamily: "var(--font-body)", fontSize: 13, fontWeight: 700, color: "var(--text-primary)" }}>Pick {need} flavours</span>
        <span style={{ fontFamily: "var(--font-body)", fontSize: 13, fontWeight: 700, color: total === need ? "var(--sage)" : "var(--gold-deep)" }}>{total} / {need}</span>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: 8, marginBottom: 16 }}>
        {product.flavours.map((f) => (
          <div key={f} style={{ display: "flex", alignItems: "center", gap: 12, border: "1px solid var(--border-subtle)", borderRadius: "var(--radius-md)", padding: "10px 14px" }}>
            <span style={{ flex: 1, fontFamily: "var(--font-body)", fontSize: 14, fontWeight: 600, color: "var(--text-primary)" }}>{f}</span>
            <div style={stepperWrap}>
              <button onClick={() => setC(f, (counts[f] || 0) - 1)} style={dStepSm} aria-label="Less"><i className="ph-light ph-minus" /></button>
              <span style={{ ...stepperNum, minWidth: 34, fontSize: 15 }}>{counts[f] || 0}</span>
              <button onClick={() => { if (total < need) setC(f, (counts[f] || 0) + 1); }} style={dStepSm} aria-label="More"><i className="ph-light ph-plus" /></button>
            </div>
          </div>
        ))}
      </div>
      <button disabled={total !== need} onClick={() => {
        const detail = product.flavours.filter((f) => counts[f] > 0).map((f) => counts[f] + "× " + f).join(" · ");
        onAddLine({ id: product.id + "-" + Date.now(), name: product.name, price: product.price, img: product.img, qty: 1, detail });
        setCounts({});
      }} style={{ ...addBtn, width: "100%", opacity: total === need ? 1 : 0.5, cursor: total === need ? "pointer" : "not-allowed" }}>
        {total === need ? "Add to box · " + fmt(product.price) : "Pick " + (need - total) + " more"}
      </button>
    </div>
  );
}

/* Size/variant purchase UI (e.g. Small / Large). */
function VariantPurchase({ product, onAddLine }) {
  const { fmt } = window.YANS;
  const [vi, setVi] = React.useState(0);
  const [qty, setQty] = React.useState(1);
  const v = product.variants[vi];
  return (
    <div>
      <div style={{ display: "flex", gap: 10, marginBottom: 16 }}>
        {product.variants.map((o, i) => (
          <button key={o.label} onClick={() => setVi(i)} style={{ flex: 1, textAlign: "left", border: `1.5px solid ${vi === i ? "var(--gold)" : "var(--border-default)"}`, background: vi === i ? "var(--accent-soft)" : "var(--surface-card)", borderRadius: "var(--radius-md)", padding: "13px 15px", cursor: "pointer" }}>
            <div style={{ fontFamily: "var(--font-body)", fontSize: 14, fontWeight: 700, color: "var(--text-primary)" }}>{o.label}</div>
            <div style={{ fontFamily: "var(--font-body)", fontSize: 12.5, color: "var(--gold-deep)", marginTop: 2 }}>{fmt(o.price)}</div>
          </button>
        ))}
      </div>
      <div style={{ display: "flex", gap: 14 }}>
        <div style={stepperWrap}>
          <button onClick={() => setQty((q) => Math.max(1, q - 1))} style={dStep} aria-label="Decrease"><i className="ph-light ph-minus" /></button>
          <span style={stepperNum}>{qty}</span>
          <button onClick={() => setQty((q) => q + 1)} style={dStep} aria-label="Increase"><i className="ph-light ph-plus" /></button>
        </div>
        <button onClick={() => onAddLine({ id: product.id + "-" + v.label, name: product.name, price: v.price, img: product.img, qty, detail: v.label })} style={addBtn}>Add to box · {fmt(v.price * qty)}</button>
      </div>
    </div>
  );
}

const ghostBtn = { height: 44, padding: "0 22px", border: "1px solid var(--border-strong)", background: "transparent", borderRadius: "var(--radius-md)", fontFamily: "var(--font-body)", fontSize: 14, fontWeight: 600, color: "var(--text-primary)", cursor: "pointer" };

/* High Tea Set builder: pick 2 swiss rolls, a box of 10 tarts, and a cheesecake —
   each flavour-selectable. Total adjusts to the chosen components. */
function HighTeaBuilder({ onAddLine }) {
  const { htChoices, TART_BOX, byId, fmt } = window.YANS;
  const [roll1, setRoll1] = React.useState(htChoices.roll[0].id);
  const [roll2, setRoll2] = React.useState(htChoices.roll[1] ? htChoices.roll[1].id : htChoices.roll[0].id);
  const [cake, setCake] = React.useState(htChoices.cheesecake[0].id);
  const tf = (id) => ((htChoices.tart.find((t) => t.id === id) || {}).price || 0);
  const tartCombos = [
    { id: "egg-fruit",    label: "5 Egg · 5 Fruit",  price: tf("egg") * 5 + tf("fruit") * 5 },
    { id: "fruit-cheese", label: "5 Fruit · 5 Cheese", price: tf("fruit") * 5 + tf("cheese") * 5 },
    { id: "egg-cheese",   label: "5 Egg · 5 Cheese", price: tf("egg") * 5 + tf("cheese") * 5 },
    { id: "egg-10",       label: "10 Egg",    price: tf("egg") * 10 },
    { id: "fruit-10",     label: "10 Fruit",  price: tf("fruit") * 10 },
    { id: "cheese-10",    label: "10 Cheese", price: tf("cheese") * 10 },
  ];
  const [comboId, setComboId] = React.useState(tartCombos[0].id);
  const combo = tartCombos.find((c) => c.id === comboId) || tartCombos[0];

  const rollPrice = (id) => byId(id) ? byId(id).price : 0;
  const cakePrice = byId(cake) ? byId(cake).price : 0;
  const total = 32.8;

  const Picker = ({ label, hint, value, set, options, getName }) => (
    <div style={{ borderBottom: "1px solid var(--border-subtle)", padding: "13px 0" }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 9 }}>
        <span style={{ fontFamily: "var(--font-body)", fontSize: 14, fontWeight: 700, color: "var(--text-primary)" }}>{label}</span>
        <span style={{ fontFamily: "var(--font-body)", fontSize: 12, color: "var(--text-muted)" }}>{hint}</span>
      </div>
      <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
        {options.map((o) => {
          const on = value === o.id;
          return (
            <button key={o.id} onClick={() => set(o.id)} style={{ display: "flex", alignItems: "center", gap: 8, padding: "6px 12px 6px 6px", borderRadius: 999, cursor: "pointer", border: `1.5px solid ${on ? "var(--accent)" : "var(--border-default)"}`, background: on ? "var(--accent-soft)" : "var(--surface-card)" }}>
              <span style={{ width: 26, height: 26, borderRadius: "50%", overflow: "hidden", flex: "none" }}><img src={o.img} alt="" style={{ width: "100%", height: "100%", objectFit: "cover" }} /></span>
              <span style={{ fontFamily: "var(--font-body)", fontSize: 13, fontWeight: on ? 700 : 500, color: on ? "var(--accent)" : "var(--text-body)" }}>{getName(o)}</span>
            </button>
          );
        })}
      </div>
    </div>
  );

  return (
    <div>
      <Picker label="Swiss roll · 1st" hint="choose 1" value={roll1} set={setRoll1} options={htChoices.roll} getName={(o) => o.name} />
      <Picker label="Swiss roll · 2nd" hint="choose 1" value={roll2} set={setRoll2} options={htChoices.roll} getName={(o) => o.name} />
      <div style={{ borderBottom: "1px solid var(--border-subtle)", padding: "13px 0" }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 9 }}>
          <span style={{ fontFamily: "var(--font-body)", fontSize: 14, fontWeight: 700, color: "var(--text-primary)" }}>Tarts · box of {TART_BOX}</span>
          <span style={{ fontFamily: "var(--font-body)", fontSize: 12, color: "var(--text-muted)" }}>choose combination</span>
        </div>
        <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
          {tartCombos.map((c) => {
            const on = comboId === c.id;
            return (
              <button key={c.id} onClick={() => setComboId(c.id)} style={{ padding: "8px 14px", borderRadius: 999, cursor: "pointer", border: `1.5px solid ${on ? "var(--accent)" : "var(--border-default)"}`, background: on ? "var(--accent-soft)" : "var(--surface-card)", fontFamily: "var(--font-body)", fontSize: 13, fontWeight: on ? 700 : 500, color: on ? "var(--accent)" : "var(--text-body)" }}>{c.label}</button>
            );
          })}
        </div>
      </div>
      <Picker label="Cheesecake" hint="choose 1" value={cake} set={setCake} options={htChoices.cheesecake} getName={(o) => o.name.replace("Japanese ", "")} />
      <button onClick={() => {
        const rn = (id) => (byId(id) ? byId(id).name.replace(" Swiss Roll", "") : "");
        const detail = "2 rolls (" + rn(roll1) + ", " + rn(roll2) + ") · tarts (" + combo.label + ") · " + (byId(cake) ? byId(cake).name.replace("Japanese ", "") : "");
        onAddLine({ id: "ht-" + Date.now(), name: "Signature High Tea Set", price: Math.round(total * 100) / 100, img: "photos/signature-set.jpg", qty: 1, detail });
      }} style={{ ...addBtn, width: "100%", marginTop: 18 }}>Add set to box · {fmt(Math.round(total * 100) / 100)}</button>
    </div>
  );
}

Object.assign(window, { DHomeSections, DCategory, DProduct, DSearch, DEyebrow, DProductCard, TartPurchase, HighTeaBuilder, VariantPurchase, RollAssortment, ghostBtn });
