/* Runtime override — the shipped _ds_bundle.js predates the clickable-slot
   (onSlot) feature, so its WalletPass/GoogleWalletPass ignore preview clicks.
   This redefines both from the current source and swaps them into the
   design-system namespace BEFORE the studios read it. Auto-generated. */
(function () {
  const NS = window.SpoonityWalletPassDesignSystem_de20b8;
  if (!NS) return;
  const Barcode = NS.Barcode;

  // ===== Apple WalletPass =====

/* Pick a readable foreground if the caller didn't specify one. */
function autoForeground(bg) {
  if (!bg || typeof bg !== "string" || bg[0] !== "#") return "#ffffff";
  const hex = bg.length === 4
    ? bg.slice(1).split("").map((c) => c + c).join("")
    : bg.slice(1, 7);
  const r = parseInt(hex.slice(0, 2), 16);
  const g = parseInt(hex.slice(2, 4), 16);
  const b = parseInt(hex.slice(4, 6), 16);
  const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
  return lum > 0.62 ? "#1d1d1f" : "#ffffff";
}

function Guide({ label, color, style }) {
  return (
    <div style={{
      border: `1px dashed ${color}`, borderRadius: 7, boxSizing: "border-box",
      display: "flex", alignItems: "center", justifyContent: "center", textAlign: "center",
      color, fontSize: 10, fontWeight: 700, letterSpacing: ".08em", textTransform: "uppercase",
      padding: "8px 10px", ...style,
    }}>{label}</div>
  );
}

/* Wraps an editable region: in editor mode (onSlot set) it becomes a clickable
   target that highlights on hover and opens that slot's editor. */
function EditableSlot({ slot, onSlot, hl, children, style, round = 8 }) {
  const [h, setH] = React.useState(false);
  if (!onSlot || !slot) return children;
  return (
    <div
      onClick={(e) => { e.stopPropagation(); onSlot(slot); }}
      onMouseEnter={() => setH(true)}
      onMouseLeave={() => setH(false)}
      style={{ cursor: "pointer", borderRadius: round, transition: "box-shadow .12s ease", boxShadow: h ? `0 0 0 2px ${hl}` : "none", ...style }}
    >
      {children}
    </div>
  );
}

/* A single label/value cell. Each part renders real text when present, or a
   dotted guide box when empty (guides on) — independently, so removing just the
   label shows a "Label" guide while the value stays, and vice-versa. */
function FieldCell({ field, fg, label, align = "left", emphasized = false, valueSize = 14, guides, guideColor, style }) {
  const f = field || {};
  const hasLabel = f.label != null && f.label !== "";
  const hasValue = f.value != null && f.value !== "";
  if (!hasLabel && !hasValue && !guides) return null;
  const alignItems = align === "right" ? "flex-end" : align === "center" ? "center" : "flex-start";
  const vSize = emphasized ? Math.round(valueSize * 1.6) : valueSize;
  const guideBox = (text, h, minW, fs) => (
    <div style={{
      border: `1px dashed ${guideColor}`, borderRadius: 5, color: guideColor, height: h, minWidth: minW,
      display: "flex", alignItems: "center", justifyContent: "center", fontSize: fs, fontWeight: 700,
      letterSpacing: ".06em", textTransform: "uppercase", padding: "0 6px", boxSizing: "border-box",
    }}>{text}</div>
  );
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 3, minWidth: 0, textAlign: align, alignItems, ...style }}>
      {hasLabel
        ? <span style={{ fontFamily: "var(--font-pass)", fontSize: 10, fontWeight: 600, letterSpacing: "0.06em", textTransform: "uppercase", color: label, lineHeight: 1.2, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", maxWidth: "100%" }}>{f.label}</span>
        : guides ? guideBox("Label", 13, 44, 8) : null}
      {hasValue
        ? <span style={{ fontFamily: "var(--font-pass)", fontSize: vSize, fontWeight: emphasized ? 500 : 400, color: fg, lineHeight: 1.1, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", maxWidth: "100%" }}>{f.value}</span>
        : guides ? guideBox("Value", emphasized ? 28 : 21, 58, 9) : null}
    </div>
  );
}

function FieldRow({ fields, fg, label, count, emphasized, guides, guideColor, onSlot, slotGroup, slotTitle, hl, lockField }) {
  let shown = (fields || []).slice(0, count);
  const hasAny = shown.some((f) => f && (f.label || f.value));
  if (!hasAny && !guides) return null;
  if (!hasAny && guides && shown.length === 0) shown = [null, null];
  return (
    <div style={{ display: "flex", gap: 12, justifyContent: "space-between" }}>
      {shown.map((f, i) => {
        const align = shown.length > 1 && i === shown.length - 1 ? "right" : "left";
        const flex = shown.length > 1 ? "1 1 0" : "1 1 auto";
        const clickable = onSlot && slotGroup && !(lockField && lockField(slotGroup, i));
        const cell = (
          <FieldCell
            key={i}
            field={f}
            fg={fg}
            label={label}
            align={align}
            emphasized={emphasized}
            valueSize={emphasized ? 17 : 14}
            guides={guides}
            guideColor={guideColor}
            style={clickable ? { width: "100%" } : { flex }}
          />
        );
        if (clickable) {
          return (
            <EditableSlot key={i} onSlot={onSlot} hl={hl || guideColor} slot={{ kind: "field", group: slotGroup, index: i, title: slotTitle || "Field" }} style={{ flex }}>
              {cell}
            </EditableSlot>
          );
        }
        return cell;
      })}
    </div>
  );
}

/**
 * Spoonity Wallet Pass — a faithful Apple-Wallet pass recreation built on the
 * Macondo token system. Supports all five PassKit styles.
 */
function WalletPass({
  passStyle = "storeCard",
  backgroundColor = "#ff7e3d",
  foregroundColor,
  labelColor,
  logoText = "Spoonity",
  logoSrc = null,
  headerFields = [],
  primaryFields = [],
  secondaryFields = [],
  auxiliaryFields = [],
  stripSrc = null,
  thumbnailSrc = null,
  transitType = "✈",
  barcode = null,
  width = 320,
  height = null,
  guides = false,
  onSlot = null,
  lockField = null,
  style = {}
}) {
  const fg = foregroundColor || autoForeground(backgroundColor);
  const label = labelColor || (fg === "#ffffff" ? "rgba(255,255,255,0.72)" : "rgba(29,29,31,0.55)");
  const guideColor = fg === "#ffffff" ? "rgba(255,255,255,0.5)" : "rgba(29,29,31,0.4)";
  const stripCapable = passStyle === "storeCard" || passStyle === "coupon" || passStyle === "eventTicket";
  const hasStrip = stripCapable && stripSrc;
  const hasThumb = (passStyle === "generic" || passStyle === "eventTicket") && thumbnailSrc;
  const notched = passStyle === "eventTicket" || passStyle === "boardingPass";
  const pad = 14;
  // Apple strip artwork is 1125×432 px (storeCard/coupon/ticket); keep the band
  // at that exact aspect so a correctly-cropped banner sits undistorted.
  const stripH = Math.round(width * 432 / 1125);

  const LOGO_SLOT = { kind: "image", target: "logoSrc", title: "Logo", crops: [{ label: "Square 660×660", w: 660, h: 660 }, { label: "Wide 480×150", w: 480, h: 150 }] };
  const Header = (
    <div style={{ display: "flex", alignItems: "center", gap: 10, padding: `${pad}px ${pad}px 8px`, minHeight: 24 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 7, flex: 1, minWidth: 0 }}>
        {(logoSrc || guides) ? (
          <EditableSlot onSlot={onSlot} hl={guideColor} slot={LOGO_SLOT} style={{ display: "inline-flex" }}>
            {logoSrc
              ? <img src={logoSrc} alt="" style={{ height: 26, maxWidth: 140, objectFit: "contain", display: "block" }} />
              : <Guide label="Logo" color={guideColor} style={{ height: 28, minWidth: 92 }} />}
          </EditableSlot>
        ) : null}
      </div>
      {(headerFields.some((f) => f && (f.label || f.value)) ? headerFields.slice(0, 2) : (guides ? [null] : [])).map((f, i) => (
        <EditableSlot key={i} onSlot={lockField && lockField("headerFields", i) ? null : onSlot} hl={guideColor} slot={{ kind: "field", group: "headerFields", index: i, title: "Header field" }}>
          <FieldCell field={f} fg={fg} label={label} align="right" valueSize={15} guides={guides} guideColor={guideColor} />
        </EditableSlot>
      ))}
    </div>
  );

  const PrimaryRegion = () => {
    if (passStyle === "boardingPass" && primaryFields.length >= 2) {
      return (
        <div style={{ display: "flex", alignItems: "center", gap: 8, padding: `4px ${pad}px 10px` }}>
          <EditableSlot onSlot={onSlot} hl={guideColor} slot={{ kind: "field", group: "primaryFields", index: 0, title: "Primary field" }} style={{ flex: 1 }}>
            <FieldCell field={primaryFields[0]} fg={fg} label={label} emphasized valueSize={20} guides={guides} guideColor={guideColor} style={{ width: "100%" }} />
          </EditableSlot>
          <span style={{ color: fg, fontSize: 18, opacity: 0.9, padding: "0 2px" }}>{transitType}</span>
          <EditableSlot onSlot={onSlot} hl={guideColor} slot={{ kind: "field", group: "primaryFields", index: 1, title: "Primary field" }} style={{ flex: 1 }}>
            <FieldCell field={primaryFields[1]} fg={fg} label={label} emphasized valueSize={20} align="right" guides={guides} guideColor={guideColor} style={{ width: "100%" }} />
          </EditableSlot>
        </div>
      );
    }
    if (!primaryFields.some((f) => f && (f.label || f.value)) && !hasThumb) {
      return null;
    }
    return (
      <div style={{ display: "flex", alignItems: "flex-start", gap: 12, padding: `4px ${pad}px 12px` }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <FieldRow fields={primaryFields} fg={fg} label={label} count={1} emphasized onSlot={onSlot} slotGroup="primaryFields" slotTitle="Primary field" guideColor={guideColor} />
        </div>
        {hasThumb && (
          <img src={thumbnailSrc} alt="" style={{ width: 48, height: 48, borderRadius: 6, objectFit: "cover", flexShrink: 0 }} />
        )}
      </div>
    );
  };

  return (
    <div
      style={{
        position: "relative",
        width,
        ...(height ? { height, display: "flex", flexDirection: "column" } : {}),
        background: backgroundColor,
        borderRadius: "var(--radius-pass)",
        boxShadow: "var(--shadow-pass)",
        overflow: "hidden",
        fontFamily: "var(--font-pass)",
        WebkitFontSmoothing: "antialiased",
        ...style,
      }}
    >
      {Header}

      {/* Coupon perforation */}
      {passStyle === "coupon" && (
        <div style={{ borderTop: `1px dashed ${fg}`, opacity: 0.35, margin: `2px ${pad}px 8px` }} />
      )}

      {/* Strip image band (with primary fields overlaid) */}
      {hasStrip ? (
        <EditableSlot onSlot={onSlot} hl={guideColor} round={0} slot={{ kind: "image", target: "stripSrc", title: "Banner image", crops: { w: 1125, h: 432 } }}>
          <div style={{ position: "relative", width: "100%", height: stripH, overflow: "hidden" }}>
            <img src={stripSrc} alt="" style={{ width: "100%", height: "100%", objectFit: "cover" }} />
            {primaryFields.length > 0 && (
              <div style={{ position: "absolute", inset: 0, display: "flex", alignItems: "flex-start", justifyContent: "space-between", padding: pad, background: "linear-gradient(180deg, rgba(0,0,0,.28), rgba(0,0,0,.04))" }}>
                <FieldRow fields={primaryFields} fg="#ffffff" label="rgba(255,255,255,.8)" count={2} emphasized />
              </div>
            )}
          </div>
        </EditableSlot>
      ) : (
        <>
          {guides && stripCapable && (
            <div style={{ padding: `2px ${pad}px 10px` }}>
              <EditableSlot onSlot={onSlot} hl={guideColor} slot={{ kind: "image", target: "stripSrc", title: "Banner image", crops: { w: 1125, h: 432 } }}>
                <Guide label="Banner · 1125 × 432" color={guideColor} style={{ width: "100%", height: Math.max(48, stripH - 24) }} />
              </EditableSlot>
            </div>
          )}
          <PrimaryRegion />
        </>
      )}

      {/* Secondary + auxiliary fields */}
      <div style={{ display: "flex", flexDirection: "column", gap: 10, padding: `10px ${pad}px ${notched ? 4 : 14}px` }}>
        <FieldRow fields={secondaryFields} fg={fg} label={label} count={4} guides={guides} guideColor={guideColor} onSlot={onSlot} slotGroup="secondaryFields" slotTitle="Secondary field" lockField={lockField} />
        <FieldRow fields={auxiliaryFields} fg={fg} label={label} count={4} />
      </div>

      {/* Spacer keeps the card a fixed height; pushes the barcode to the bottom. */}
      {height && <div style={{ flex: 1, minHeight: 0 }} />}

      {/* Notch divider for tickets / boarding passes */}
      {notched && barcode && (
        <div style={{ position: "relative", height: 12 }}>
          <span style={{ position: "absolute", left: -7, top: -1, width: 14, height: 14, borderRadius: "50%", background: "var(--surface-base, #fff)", boxShadow: "inset 0 0 0 1px rgba(0,0,0,.04)" }} />
          <span style={{ position: "absolute", right: -7, top: -1, width: 14, height: 14, borderRadius: "50%", background: "var(--surface-base, #fff)", boxShadow: "inset 0 0 0 1px rgba(0,0,0,.04)" }} />
          <span style={{ position: "absolute", left: 10, right: 10, top: 5, borderTop: `1px dashed ${fg}`, opacity: 0.3 }} />
        </div>
      )}

      {/* Barcode footer */}
      {barcode && (
        <div style={{ display: "flex", justifyContent: "center", padding: `8px ${pad}px 16px` }}>
          <Barcode
            format={barcode.format || "qr"}
            message={barcode.message || "SPOONITY"}
            altText={barcode.altText}
            size={barcode.size || (barcode.format === "pdf417" || barcode.format === "code128" ? 112 : 132)}
            style={{ width: barcode.format === "pdf417" || barcode.format === "code128" ? "100%" : "auto" }}
          />
        </div>
      )}
    </div>
  );
}


  // ===== Google GoogleWalletPass =====

/* Readable foreground for a given hex background. */
function autoFg(bg) {
  if (!bg || typeof bg !== "string" || bg[0] !== "#") return "#ffffff";
  const hex = bg.length === 4 ? bg.slice(1).split("").map((c) => c + c).join("") : bg.slice(1, 7);
  const r = parseInt(hex.slice(0, 2), 16), g = parseInt(hex.slice(2, 4), 16), b = parseInt(hex.slice(4, 6), 16);
  return (0.299 * r + 0.587 * g + 0.114 * b) / 255 > 0.62 ? "#202124" : "#ffffff";
}

function GGuide({ label, color, style }) {
  return (
    <div style={{
      border: `1px dashed ${color}`, borderRadius: 6, boxSizing: "border-box",
      display: "flex", alignItems: "center", justifyContent: "center", textAlign: "center",
      color, fontSize: 10, fontWeight: 600, letterSpacing: ".06em", textTransform: "uppercase",
      padding: "6px 8px", ...style,
    }}>{label}</div>
  );
}

function GEditableSlot({ slot, onSlot, hl, children, style, round = 8 }) {
  const [h, setH] = React.useState(false);
  if (!onSlot || !slot) return children;
  return (
    <div
      onClick={(e) => { e.stopPropagation(); onSlot(slot); }}
      onMouseEnter={() => setH(true)}
      onMouseLeave={() => setH(false)}
      style={{ cursor: "pointer", borderRadius: round, transition: "box-shadow .12s ease", boxShadow: h ? `0 0 0 2px ${hl}` : "none", ...style }}
    >
      {children}
    </div>
  );
}

/**
 * Spoonity Google Wallet Pass — a Google Wallet (loyalty / generic) card:
 * large logo, hairline divider, card title, a 3-column field grid (with "+"
 * add-slots while editing), a wide barcode panel + code text, and a full-bleed
 * hero image anchored to the bottom.
 */
function GoogleWalletPass({
  backgroundColor = "#640c6f",
  foregroundColor,
  labelColor,
  logoSrc = null,
  programName = "",
  cardTitle = "",
  subtitle = "",
  fields = [],
  heroSrc = null,
  barcode = null,
  width = 360,
  height = null,
  guides = false,
  onSlot = null,
  lockField = null,
  style = {}
}) {
  const fg = foregroundColor || autoFg(backgroundColor);
  const labelMuted = labelColor || (fg === "#ffffff" ? "rgba(255,255,255,0.82)" : "rgba(32,33,36,0.68)");
  const muted = fg === "#ffffff" ? "rgba(255,255,255,0.85)" : "rgba(32,33,36,0.72)";
  const guideColor = fg === "#ffffff" ? "rgba(255,255,255,0.5)" : "rgba(32,33,36,0.4)";
  const divider = fg === "#ffffff" ? "rgba(255,255,255,0.22)" : "rgba(32,33,36,0.16)";
  const title = cardTitle || programName;

  const N = 6;
  const cells = fields.slice(0, N);
  let lastFilled = -1;
  cells.forEach((f, i) => { if (f && (f.label || f.value)) lastFilled = i; });
  const colAlignItems = (i) => (i % 3 === 0 ? "flex-start" : i % 3 === 1 ? "center" : "flex-end");
  const colText = (i) => (i % 3 === 0 ? "left" : i % 3 === 1 ? "center" : "right");
  const isWide = barcode && barcode.format !== "qr" && barcode.format !== "aztec";

  const gbox = (t, h, w) => (
    <div style={{ border: `1px dashed ${guideColor}`, borderRadius: 4, color: guideColor, height: h, minWidth: w,
      display: "flex", alignItems: "center", justifyContent: "center", fontSize: 8, fontWeight: 700,
      letterSpacing: ".06em", textTransform: "uppercase", padding: "0 6px", boxSizing: "border-box" }}>{t}</div>
  );

  return (
    <div style={{
      position: "relative", width,
      ...(height ? { height, display: "flex", flexDirection: "column" } : {}),
      background: backgroundColor, color: fg,
      borderRadius: "var(--radius-pass)", boxShadow: "var(--shadow-pass)", overflow: "hidden",
      fontFamily: "var(--font-sans)", WebkitFontSmoothing: "antialiased", ...style,
    }}>
      {/* Logo */}
      <div style={{ padding: "18px 22px 12px" }}>
        {(logoSrc || guides) ? (
          <GEditableSlot onSlot={onSlot} hl={guideColor} slot={{ kind: "image", target: "logoSrc", title: "Logo", crops: { w: 660, h: 660 } }} style={{ display: "inline-flex" }}>
            {logoSrc
              ? <img src={logoSrc} alt="" style={{ height: 40, maxWidth: "78%", objectFit: "contain", display: "block" }} />
              : <GGuide label="Logo" color={guideColor} style={{ height: 40, maxWidth: 170 }} />}
          </GEditableSlot>
        ) : null}
      </div>

      {/* Divider */}
      <div style={{ height: 1, background: divider }} />

      {/* Title */}
      <div style={{ padding: "14px 22px 12px" }}>
        {(title || guides) ? (
          <GEditableSlot onSlot={onSlot} hl={guideColor} slot={{ kind: "text", target: "cardTitle", title: "Card title", placeholder: "Account or program name" }} style={{ display: "inline-flex", maxWidth: "100%" }}>
            {title
              ? <div style={{ fontSize: 28, fontWeight: 500, lineHeight: 1.15, letterSpacing: "-0.01em" }}>{title}</div>
              : <GGuide label="Card title" color={guideColor} style={{ height: 32, maxWidth: 200 }} />}
          </GEditableSlot>
        ) : null}
      </div>

      {/* Field grid (3 columns) */}
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", rowGap: 18, columnGap: 12, padding: "0 22px 14px" }}>
        {Array.from({ length: N }).map((_, i) => {
          const f = cells[i] || {};
          const hasL = f.label != null && f.label !== "";
          const hasV = f.value != null && f.value !== "";
          const slot = { kind: "field", group: "fields", index: i, title: "Field" };
          const fieldOnSlot = lockField && lockField("fields", i) ? null : onSlot;
          if (hasL || hasV) {
            return (
              <GEditableSlot key={i} onSlot={fieldOnSlot} hl={guideColor} slot={slot}>
                <div style={{ display: "flex", flexDirection: "column", gap: 4, minWidth: 0, alignItems: colAlignItems(i), textAlign: colText(i) }}>
                  {hasL ? <span style={{ fontSize: 13, fontWeight: 400, color: labelMuted, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", maxWidth: "100%" }}>{f.label}</span>
                    : guides ? gbox("Label", 13, 40) : null}
                  {hasV ? <span style={{ fontSize: 17, fontWeight: 700, color: fg, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", maxWidth: "100%" }}>{f.value}</span>
                    : guides ? gbox("Value", 18, 50) : null}
                </div>
              </GEditableSlot>
            );
          }
          if (guides && i <= lastFilled + 1) {
            return (
              <GEditableSlot key={i} onSlot={onSlot} hl={guideColor} slot={slot} style={{ display: "flex", justifyContent: colAlignItems(i), alignItems: "center" }}>
                <span style={{ fontSize: 26, fontWeight: 300, lineHeight: 1, color: muted, padding: "4px 10px" }}>+</span>
              </GEditableSlot>
            );
          }
          return <div key={i} />;
        })}
      </div>

      {/* Barcode panel */}
      {barcode && (
        <div style={{ padding: "2px 18px 10px" }}>
          <div style={{ background: "#fff", borderRadius: 20, padding: isWide ? "16px 20px" : "16px", display: "flex", justifyContent: "center" }}>
            <Barcode
              format={barcode.format || "code128"}
              message={barcode.message || "SPOONITY"}
              size={barcode.size || (isWide ? 116 : 120)}
              fg="#111111"
              bg="#ffffff"
              style={{ padding: 0, width: isWide ? "100%" : "auto" }}
            />
          </div>
        </div>
      )}

      {/* Code text */}
      {barcode && barcode.altText && (
        <div style={{ textAlign: "center", padding: "0 18px 12px", fontSize: 14, color: muted }}>{barcode.altText}</div>
      )}

      {/* Hero — full-bleed, edge touching the bottom of the pass */}
      {heroSrc ? (
        <GEditableSlot onSlot={onSlot} hl={guideColor} round={0} slot={{ kind: "image", target: "heroSrc", title: "Hero image", crops: { w: 1032, h: 336 } }} style={{ flex: height ? "1 1 0" : "0 0 auto", minHeight: height ? 90 : undefined, height: height ? undefined : 110, overflow: "hidden", display: "block" }}>
          <img src={heroSrc} alt="" style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
        </GEditableSlot>
      ) : guides ? (
        <GEditableSlot onSlot={onSlot} hl={guideColor} round={0} slot={{ kind: "image", target: "heroSrc", title: "Hero image", crops: { w: 1032, h: 336 } }} style={{ flex: height ? "1 1 0" : "0 0 auto", minHeight: height ? 90 : undefined, height: height ? undefined : 110, display: "block" }}>
          <GGuide label="Hero image" color={guideColor} style={{ width: "100%", height: "100%", borderRadius: 0, border: "none", borderTop: `1px dashed ${guideColor}` }} />
        </GEditableSlot>
      ) : (height ? <div style={{ flex: 1, minHeight: 0 }} /> : null)}
    </div>
  );
}


  NS.WalletPass = WalletPass;
  NS.GoogleWalletPass = GoogleWalletPass;
})();
