// KASHA — 굿즈 상품 (코스어 굿즈 판매 대행 마켓)
// 굿즈 폼(프로젝트) 카드 목록 + 검색(코스어명/폼명) + 예약 폼 실시간 카운트다운.
function GoodsScreen({ onNavigate }) {
  const { Icon, Button } = window.DS;
  window.KASHADB.useStore();
  const [q, setQ] = React.useState("");
  // 서버 publicView 는 draft 제외 + status(effective)/openAt 제공
  const all = (window.KASHADB.getGoodsForms() || []).filter((f) => f && (f.status ? f.status !== "draft" : f.active));
  const query = q.trim().toLowerCase();
  const forms = !query ? all : all.filter((f) =>
    (f.name || "").toLowerCase().indexOf(query) >= 0 || (f.cosplayerName || "").toLowerCase().indexOf(query) >= 0);
  // 더보기(append) — 검색어 바뀌면 처음으로 리셋
  const PAGE = 12;
  const [shown, setShown] = React.useState(PAGE);
  React.useEffect(() => { setShown(PAGE); }, [query]);
  const visible = forms.slice(0, shown);

  return (
    <div style={{ minHeight: "60vh" }}>
      <style>{`
        .kg-wrap{max-width:1120px;margin:0 auto;padding:0 24px}
        .kg-hero{padding:64px 0 20px;text-align:center;position:relative;z-index:3}   /* 검색 제안 드롭다운이 아래 굿즈 그리드 위에 오도록 히어로 스택 승격 */
        .kg-searchwrap{max-width:520px;margin:22px auto 0;position:relative;text-align:left}
        .kg-search{width:100%;height:52px;padding:0 46px 0 48px;border-radius:14px;border:1.5px solid var(--border-default);background:var(--gray-0);font-family:var(--font-sans);font-size:15.5px;color:var(--text-strong);box-sizing:border-box;transition:border-color var(--dur-fast) var(--ease-out),box-shadow var(--dur-fast) var(--ease-out)}
        .kg-search:focus{outline:none;border-color:var(--blue-500);box-shadow:0 0 0 4px var(--blue-50)}
        .kg-search::placeholder{color:var(--text-tertiary)}
        .kg-searchicon{position:absolute;left:17px;top:26px;transform:translateY(-50%);color:var(--gray-400);pointer-events:none}
        .kg-clear{position:absolute;right:12px;top:26px;transform:translateY(-50%);width:26px;height:26px;border-radius:50%;border:none;background:var(--gray-100);color:var(--text-tertiary);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:background var(--dur-fast) var(--ease-ios)}
        .kg-clear:hover{background:var(--gray-200)}
        .kg-clear:focus-visible{outline:2px solid var(--blue-500);outline-offset:2px}
        .kg-suggest{position:absolute;left:0;right:0;top:calc(100% + 8px);z-index:60;background:var(--surface-card);border:1px solid var(--border-subtle);border-radius:14px;box-shadow:var(--shadow-lg);padding:6px;opacity:0;transform:translateY(-6px);transition:opacity var(--dur-fast) var(--ease-out),transform var(--dur-fast) var(--ease-out);pointer-events:none}
        .kg-suggest.open{opacity:1;transform:none;pointer-events:auto}
        .kg-suggest-sec{font-size:11.5px;font-weight:700;color:var(--text-tertiary);padding:9px 12px 5px;letter-spacing:0.02em}
        .kg-suggest-item{display:flex;align-items:center;gap:10px;width:100%;text-align:left;padding:10px 12px;border:none;background:transparent;border-radius:10px;cursor:pointer;font-family:var(--font-sans);font-size:14.5px;font-weight:600;color:var(--text-strong);transition:background var(--dur-fast) var(--ease-ios)}
        .kg-suggest-item.active{background:var(--gray-50)}
        .kg-suggest-item:focus-visible{outline:2px solid var(--blue-500);outline-offset:-2px}
        .kg-sug-ic{color:var(--gray-400);flex-shrink:0;display:flex}
        .kg-sug-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
        .kg-sug-badge{margin-left:auto;flex-shrink:0;font-size:11px;font-weight:700;padding:2px 8px;border-radius:999px;background:var(--blue-50);color:var(--blue-600)}
        .kg-sug-badge.cos{background:var(--gray-100);color:var(--text-secondary)}
        .kg-sug-x{margin-left:auto;flex-shrink:0;color:var(--gray-400);display:flex;padding:2px;border-radius:6px}
        .kg-sug-x:hover{color:var(--text-secondary);background:var(--gray-100)}
        .kg-reco-chip{display:inline-flex;align-items:center;gap:6px;padding:9px 15px;border-radius:999px;border:1px solid var(--border-default);background:var(--gray-0);color:var(--text-secondary);font-family:var(--font-sans);font-size:13.5px;font-weight:700;cursor:pointer;transition:transform var(--dur-fast) var(--ease-ios),border-color var(--dur-fast) var(--ease-ios)}
        .kg-reco-chip:hover{border-color:var(--ink);color:var(--text-strong)}
        .kg-reco-chip:active{transform:scale(0.97)}
        @media(prefers-reduced-motion:reduce){.kg-suggest{transition:none}.kg-clear,.kg-suggest-item,.kg-reco-chip{transition:none}.kg-reco-chip:active{transform:none}}
        .kg-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:20px;padding:26px 0 80px}
        @media(max-width:1080px){.kg-grid{grid-template-columns:repeat(3,1fr)}}
        @media(max-width:820px){.kg-grid{grid-template-columns:repeat(2,1fr);gap:18px}}
        @media(max-width:520px){.kg-grid{grid-template-columns:1fr}}
        .kg-card{cursor:pointer;border-radius:18px;overflow:hidden;background:var(--gray-0);border:1px solid var(--border-subtle);transition:transform var(--dur-base) var(--ease-ios),box-shadow var(--dur-base) var(--ease-out),border-color var(--dur-base) var(--ease-out)}
        .kg-card:hover{transform:translateY(-4px);box-shadow:0 18px 40px -20px rgba(15,17,21,0.28);border-color:var(--blue-200)}
        @media(prefers-reduced-motion:reduce){.kg-card{transition:none}.kg-card:hover{transform:none}.kg-search{transition:none}.kg-thumb img{transition:none}.kg-card:hover .kg-thumb img{transform:none}}
        .kg-thumb{position:relative;width:100%;aspect-ratio:1/1;background:var(--gray-100);overflow:hidden}
        .kg-thumb img{width:100%;height:100%;object-fit:cover;display:block;transition:transform var(--dur-slow) var(--ease-out);will-change:transform}
        .kg-card:hover .kg-thumb img{transform:scale(1.05)}
        .kg-badge{position:absolute;left:12px;top:12px;color:#fff;font-size:11.5px;font-weight:700;padding:5px 10px;border-radius:999px;backdrop-filter:blur(6px)}
        .kg-body{padding:15px 16px 17px}
        /* 마감 폼: 흑백 커버 + 스크림 + 중앙 마감 라벨. 색만이 아니라 형태·텍스트로도 마감을 전달해요 */
        .kg-card.closed .kg-thumb img{filter:grayscale(1) brightness(0.92)}
        .kg-card.closed:hover .kg-thumb img{transform:none}
        .kg-card.closed:hover{transform:none;box-shadow:none;border-color:var(--border-subtle)}
        .kg-closed-scrim{position:absolute;inset:0;background:rgba(15,17,21,0.42);display:flex;align-items:center;justify-content:center}
        .kg-closed-label{display:inline-flex;align-items:center;gap:6px;padding:9px 16px;border-radius:999px;background:rgba(15,17,21,0.82);color:#fff;font-size:13.5px;font-weight:800;letter-spacing:0.02em;backdrop-filter:blur(4px)}
        .kg-card.closed .kg-body{color:var(--text-tertiary)}
        .kg-card.closed .kg-name,.kg-card.closed .kg-price{color:var(--text-tertiary)}
      `}</style>
      <div className="kg-wrap">
        <section className="kg-hero">
          <div style={{ fontSize: 12.5, fontWeight: 700, color: "var(--blue-500)", letterSpacing: "0.08em" }}>COSPLAYER GOODS</div>
          <h1 style={{ fontSize: "var(--text-title-1)", letterSpacing: "var(--tracking-title)", marginTop: 12 }}>굿즈 상품</h1>
          <p style={{ marginTop: 12, color: "var(--text-secondary)", fontSize: "var(--text-body-lg)", lineHeight: 1.6 }}>
            KASHA 대행 코스어들의 공식 굿즈를 만나보세요. 판매 마감 전에 주문해 주세요.
          </p>
          {/* 검색 경험 — 최근 검색·자동완성·키보드·결과 없음 복구(designmotionhq search-experience) */}
          <GoodsSearch q={q} onQuery={setQ} all={all} onOpenForm={(id) => onNavigate("goodsDetail", { formId: id })} />
        </section>

        {all.length === 0 ? (
          <div style={{ textAlign: "center", padding: "40px 20px 90px" }}>
            <div style={{ width: 64, height: 64, borderRadius: 18, background: "var(--blue-50)", display: "inline-flex", alignItems: "center", justifyContent: "center", color: "var(--blue-500)" }}>
              <Icon name="sparkles" size={28} />
            </div>
            <h3 style={{ marginTop: 18, fontSize: "var(--text-title-3)" }}>아직 등록된 굿즈가 없어요</h3>
            <p style={{ marginTop: 10, color: "var(--text-secondary)", fontSize: 15, lineHeight: 1.6 }}>코스어라면 굿즈 판매 대행으로 상품을 등록해 보세요.</p>
            <div style={{ marginTop: 20 }}><Button variant="primary" size="lg" onClick={() => onNavigate("agency")}>코스어 굿즈 판매 대행 알아보기</Button></div>
          </div>
        ) : forms.length === 0 ? (
          <GoodsNoResults q={q} all={all} onPick={setQ} onClear={() => setQ("")} />
        ) : (
          <React.Fragment>
            <div className="kg-grid">
              {visible.map((f) => <GoodsFormCard key={f.id} f={f} onOpen={() => onNavigate("goodsDetail", { formId: f.id })} />)}
            </div>
            <window.KASHALoadMore shown={visible.length} total={forms.length} onMore={() => setShown(shown + PAGE)} label="굿즈 더보기" />
          </React.Fragment>
        )}
      </div>
    </div>
  );
}

function GoodsFormCard({ f, onOpen }) {
  const status = window.KASHAGoods.effStatus(f);   // draft/scheduled/open/closed (openAt 도달 시 로컬에서도 open 처리)
  const cover = (f.images || [])[0] || "";
  const products = f.products || [];
  const prices = products.map((p) => parseInt(p.price, 10) || 0);
  const minPrice = prices.length ? Math.min.apply(null, prices) : 0;
  const priceLabel = prices.length ? ((window.won ? window.won(minPrice) : minPrice) + "원" + (prices.length > 1 ? "~" : "")) : "준비 중";
  const badge = status === "scheduled" ? { t: "오픈 예정", bg: "rgba(49,130,246,0.86)" }
    : status === "closed" ? { t: "판매 마감", bg: "rgba(220,38,38,0.85)" }
    : { t: window.KASHAGoods.closeLabel(f), bg: "rgba(15,17,21,0.72)" };
  const closed = status === "closed";
  return (
    <div className={"kg-card" + (closed ? " closed" : "")} onClick={onOpen}>
      <div className="kg-thumb">
        {cover ? <img src={cover} alt={f.name} loading="lazy" className="kasha-img-fade" ref={(el) => { if (el && el.complete) el.classList.add("loaded"); }} onLoad={(e) => e.currentTarget.classList.add("loaded")} /> : <div style={{ width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--gray-400)", fontSize: 13 }}>이미지 준비 중</div>}
        {/* 마감은 좌상단 배지 대신 커버 전체 스크림 + 중앙 라벨로 확실하게 표시 */}
        {closed
          ? <span className="kg-closed-scrim"><span className="kg-closed-label">판매 마감</span></span>
          : <span className="kg-badge" style={{ background: badge.bg }}>{badge.t}</span>}
      </div>
      <div className="kg-body">
        <div className="kg-name" style={{ fontSize: 16, fontWeight: 700, color: closed ? "var(--text-tertiary)" : "var(--text-strong)", letterSpacing: "-0.01em", lineHeight: 1.35, display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical", overflow: "hidden", minHeight: 43 }}>{f.name}</div>
        {/* 예약 폼: 오픈까지 실시간 카운트다운 */}
        {status === "scheduled" && f.openAt && <OpenCountdown openAt={f.openAt} />}
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 11 }}>
          <span style={{ width: 24, height: 24, borderRadius: "50%", overflow: "hidden", background: "var(--gray-200)", flexShrink: 0 }}>
            {f.cosplayerAvatar ? <img src={f.cosplayerAvatar} alt="" style={{ width: "100%", height: "100%", objectFit: "cover" }} /> : null}
          </span>
          <span style={{ fontSize: 13, color: "var(--text-secondary)", fontWeight: 600 }}>{f.cosplayerName || "코스어"}</span>
        </div>
        <div style={{ marginTop: 12, display: "flex", alignItems: "baseline", justifyContent: "space-between" }}>
          <span className="kg-price" style={{ fontSize: 18, fontWeight: 800, color: closed ? "var(--text-tertiary)" : "var(--text-strong)" }}>{closed ? "판매 마감" : priceLabel}</span>
          <span style={{ fontSize: 12.5, fontWeight: 600, color: "var(--text-tertiary)" }}>상품 {products.length}개</span>
        </div>
      </div>
    </div>
  );
}

// 오픈까지 남은 시간 실시간 카운트다운 (초 단위). 0 도달 시 '지금 오픈'.
function OpenCountdown({ openAt }) {
  const target = React.useMemo(() => Date.parse(openAt), [openAt]);
  const [now, setNow] = React.useState(Date.now());
  React.useEffect(() => {
    const id = setInterval(() => setNow(Date.now()), 1000);
    return () => clearInterval(id);
  }, []);
  const diff = target - now;
  if (isNaN(target)) return null;
  if (diff <= 0) return <div style={{ marginTop: 6, fontSize: 12.5, fontWeight: 700, color: "var(--green-500)" }}>지금 오픈됐어요 · 새로고침하면 구매할 수 있어요</div>;
  const s = Math.floor(diff / 1000);
  const d = Math.floor(s / 86400), h = Math.floor((s % 86400) / 3600), m = Math.floor((s % 3600) / 60), sec = s % 60;
  const pad = (n) => String(n).padStart(2, "0");
  const clock = pad(h) + ":" + pad(m) + ":" + pad(sec);
  return (
    <div style={{ marginTop: 6, display: "inline-flex", alignItems: "center", gap: 6, fontSize: 12.5, fontWeight: 700, color: "var(--blue-600)", fontVariantNumeric: "tabular-nums" }}>
      <span style={{ display: "inline-block", width: 6, height: 6, borderRadius: "50%", background: "var(--blue-500)" }} />
      {d > 0 ? "D-" + d + " " : ""}{clock} 후 오픈
    </div>
  );
}

// 굿즈 폼 상태·마감 유틸 (window 공용 — 카드/상세 공유)
window.KASHAGoods = {
  // 유효 상태 — 서버 status 우선, openAt 도달 시 로컬에서도 open 으로 승격, closeAt 경과 시 closed
  effStatus: function (f) {
    if (!f) return "draft";
    var s = f.status || (f.active === false ? "draft" : "open");
    if (s === "draft" || s === "closed") return s;
    var now = Date.now();
    if (f.closeAt) { var t = Date.parse(f.closeAt); if (!isNaN(t) && t < now) return "closed"; }
    if (s === "scheduled") { if (f.openAt) { var o = Date.parse(f.openAt); if (!isNaN(o) && o <= now) return "open"; } return "scheduled"; }
    return "open";
  },
  isClosed: function (f) { return this.effStatus(f) === "closed"; },
  isOpen: function (f) { return this.effStatus(f) === "open"; },
  closeLabel: function (f) {
    if (!f || !f.closeAt) return "상시 판매";
    var t; try { t = new Date(f.closeAt).getTime(); } catch (e) { return ""; }
    if (isNaN(t)) return "";
    if (t < Date.now()) return "판매 마감";
    var d = Math.ceil((t - Date.now()) / 86400000);
    return d <= 1 ? "오늘 마감" : "D-" + d + " 마감";
  },
  // 특전 실시간 평가 — 장바구니(cart:{productId:qty})와 총액으로 각 특전 달성 여부·남은 조건 계산
  //  반환: [{ perk, met, remaining, remainLabel }] (tier 오름차순)
  evalPerks: function (form, cart, amount) {
    var perks = (form && form.perks) || [];
    if (!perks.length) return [];
    cart = cart || {}; amount = amount || 0;
    return perks.slice().sort(function (a, z) { return (a.tier || 0) - (z.tier || 0); }).map(function (k) {
      var th = k.threshold || 0;
      var met, remaining, remainLabel;
      if (k.condType === "productQty") {
        var have = cart[k.productId] || 0;
        met = th > 0 && have >= th;
        remaining = Math.max(0, th - have);
        remainLabel = met ? "달성" : (remaining + "개 더 담으면 받아요");
      } else {
        met = th > 0 && amount >= th;
        remaining = Math.max(0, th - amount);
        remainLabel = met ? "달성" : (Number(remaining).toLocaleString("ko-KR") + "원 더 담으면 받아요");
      }
      return { perk: k, met: met, remaining: remaining, remainLabel: remainLabel };
    });
  },
};
// 검색 경험 — 최근 검색(포커스 시 즉시)·자동완성(접두 우선·카테고리 배지·적고 날카롭게)·
// 키보드(↑↓·Enter·Esc, 포커스 링 유지). 순수 표현/클라이언트, 굿즈 데이터·라우팅 계약 불변.
function GoodsSearch({ q, onQuery, all, onOpenForm }) {
  const { Icon } = window.DS;
  const [focused, setFocused] = React.useState(false);
  const [activeIdx, setActiveIdx] = React.useState(-1);
  const [recent, setRecent] = React.useState(function () { try { return JSON.parse(localStorage.getItem("kasha_goods_recent") || "[]"); } catch (e) { return []; } });
  const wrapRef = React.useRef(null);
  const inputRef = React.useRef(null);
  const query = (q || "").trim().toLowerCase();

  // 검색 대상 풀 — 코스어(고유명) + 굿즈 폼(이름)
  const pool = React.useMemo(function () {
    const seen = {}, arr = [];
    (all || []).forEach(function (f) {
      if (f.cosplayerName && !seen["c:" + f.cosplayerName]) { seen["c:" + f.cosplayerName] = 1; arr.push({ type: "코스어", label: f.cosplayerName }); }
      if (f.name) arr.push({ type: "굿즈", label: f.name, formId: f.id });
    });
    return arr;
  }, [all]);

  // 자동완성 — 접두 일치 우선 → 부분 일치, 최대 6 (세 개의 날카로운 결과가 열 개의 잡음보다 나아요)
  const matches = React.useMemo(function () {
    if (!query) return [];
    const pre = [], inc = [];
    pool.forEach(function (it) { const i = it.label.toLowerCase().indexOf(query); if (i === 0) pre.push(it); else if (i > 0) inc.push(it); });
    return pre.concat(inc).slice(0, 6);
  }, [pool, query]);

  // 빈 검색어 + 포커스: 최근 검색 + 추천
  const suggestions = React.useMemo(function () {
    if (query) return matches;
    const rec = recent.slice(0, 5).map(function (s) { return { type: "recent", label: s }; });
    const recSet = {}; rec.forEach(function (r) { recSet[r.label] = 1; });
    const reco = pool.filter(function (it) { return !recSet[it.label]; }).slice(0, 5);
    return rec.concat(reco);
  }, [query, matches, recent, pool]);

  const firstReco = (!query) ? suggestions.findIndex(function (s) { return s.type !== "recent"; }) : -1;

  const persist = function (arr) { setRecent(arr); try { localStorage.setItem("kasha_goods_recent", JSON.stringify(arr)); } catch (e) {} };
  const saveRecent = function (str) { const s = String(str || "").trim(); if (!s) return; persist([s].concat(recent.filter(function (r) { return r !== s; })).slice(0, 6)); };
  const removeRecent = function (str, e) { e.stopPropagation(); persist(recent.filter(function (r) { return r !== str; })); };

  const close = function () { setFocused(false); setActiveIdx(-1); };
  const choose = function (it) {
    if (!it) return;
    if (it.type === "굿즈" && it.formId) { saveRecent(it.label); close(); onOpenForm(it.formId); return; }
    onQuery(it.label); saveRecent(it.label); close();   // 코스어·최근 → 검색어로 필터
  };
  const onKey = function (e) {
    if (e.key === "ArrowDown") { e.preventDefault(); setFocused(true); setActiveIdx(function (i) { return Math.min(suggestions.length - 1, i + 1); }); }
    else if (e.key === "ArrowUp") { e.preventDefault(); setActiveIdx(function (i) { return Math.max(-1, i - 1); }); }
    else if (e.key === "Enter") { if (activeIdx >= 0 && suggestions[activeIdx]) { e.preventDefault(); choose(suggestions[activeIdx]); } else { saveRecent(q); close(); } }
    else if (e.key === "Escape") { close(); if (inputRef.current) inputRef.current.blur(); }
  };

  React.useEffect(function () {
    if (!focused) return;
    const onDown = function (ev) { if (wrapRef.current && !wrapRef.current.contains(ev.target)) close(); };
    document.addEventListener("mousedown", onDown);
    return function () { document.removeEventListener("mousedown", onDown); };
  }, [focused]);

  const showDrop = focused && suggestions.length > 0;

  return (
    <div className="kg-searchwrap" ref={wrapRef}>
      <span className="kg-searchicon"><Icon name="search" size={20} /></span>
      <input ref={inputRef} className="kg-search" value={q}
        onChange={(e) => { onQuery(e.target.value); setActiveIdx(-1); setFocused(true); }}
        onFocus={() => setFocused(true)} onKeyDown={onKey}
        placeholder="코스어 이름·굿즈 이름으로 검색"
        role="combobox" aria-expanded={showDrop} aria-controls="kg-suggest-list" aria-autocomplete="list"
        aria-activedescendant={activeIdx >= 0 ? ("kg-sug-" + activeIdx) : undefined} aria-label="굿즈 검색" />
      {q && <button type="button" className="kg-clear" aria-label="검색어 지우기" onClick={() => { onQuery(""); setActiveIdx(-1); if (inputRef.current) inputRef.current.focus(); }}><Icon name="x" size={15} /></button>}
      <div id="kg-suggest-list" role="listbox" aria-label="검색 제안" className={"kg-suggest" + (showDrop ? " open" : "")}>
        {suggestions.map(function (it, i) {
          const isRecent = it.type === "recent";
          return (
            <React.Fragment key={it.type + "-" + it.label + "-" + i}>
              {(!query && i === 0 && isRecent) && <div className="kg-suggest-sec">최근 검색</div>}
              {(!query && i === firstReco && firstReco >= 0) && <div className="kg-suggest-sec">추천 검색</div>}
              <button type="button" id={"kg-sug-" + i} role="option" aria-selected={i === activeIdx}
                className={"kg-suggest-item" + (i === activeIdx ? " active" : "")}
                onMouseEnter={() => setActiveIdx(i)} onClick={() => choose(it)}>
                <span className="kg-sug-ic"><Icon name={isRecent ? "clock" : it.type === "코스어" ? "user" : "search"} size={16} /></span>
                <span className="kg-sug-label">{it.label}</span>
                {isRecent
                  ? <span className="kg-sug-x" role="button" aria-label="최근 검색에서 삭제" onClick={(e) => removeRecent(it.label, e)}><Icon name="x" size={13} /></span>
                  : <span className={"kg-sug-badge" + (it.type === "코스어" ? " cos" : "")}>{it.type}</span>}
              </button>
            </React.Fragment>
          );
        })}
      </div>
    </div>
  );
}

// 결과 없음 복구 — 막다른 화면 대신 추천 코스어·검색 지우기로 복귀 경로 제공
function GoodsNoResults({ q, all, onPick, onClear }) {
  const { Icon, Button } = window.DS;
  const cos = [];
  (all || []).forEach(function (f) { if (f.cosplayerName && cos.indexOf(f.cosplayerName) < 0) cos.push(f.cosplayerName); });
  const picks = cos.slice(0, 6);
  return (
    <div style={{ textAlign: "center", padding: "52px 20px 90px" }}>
      <div style={{ width: 60, height: 60, borderRadius: 18, background: "var(--gray-100)", display: "inline-flex", alignItems: "center", justifyContent: "center", color: "var(--gray-500)" }}>
        <Icon name="search" size={26} />
      </div>
      <h3 style={{ marginTop: 18, fontSize: "var(--text-title-3)" }}>‘{q}’에 대한 결과가 없어요</h3>
      <p style={{ marginTop: 10, color: "var(--text-secondary)", fontSize: 15, lineHeight: 1.6 }}>이름의 일부만 입력하거나, 아래 코스어를 눌러보세요.</p>
      {picks.length > 0 && (
        <div style={{ marginTop: 18, display: "flex", flexWrap: "wrap", gap: 8, justifyContent: "center", maxWidth: 520, marginLeft: "auto", marginRight: "auto" }}>
          {picks.map((c) => <button key={c} type="button" className="kg-reco-chip" onClick={() => onPick(c)}><Icon name="user" size={14} color="var(--gray-400)" />{c}</button>)}
        </div>
      )}
      <div style={{ marginTop: 22 }}><Button variant="secondary" onClick={onClear}>검색 지우기</Button></div>
    </div>
  );
}
window.GoodsScreen = GoodsScreen;

// ── 특전 보드 — 상세·결제 공용. 실시간 달성 여부를 색으로 강조 + 특전 정보 모달 ──
function GoodsPerkBoard({ form, cart, amount }) {
  const { Icon, Dialog, Button } = window.DS;
  const [open, setOpen] = React.useState(false);
  const perks = (form && form.perks) || [];
  if (!perks.length) return null;
  const rows = window.KASHAGoods.evalPerks(form, cart || {}, amount || 0);
  // 피크 모먼트(peak-end): 특전이 방금 달성됐을 때만 체크 아이콘을 스프링으로 pop.
  // 최초 마운트에선 pop 안 함(이미 달성된 특전을 축하하지 않음). 표현만 — 특전 로직/데이터 무변경.
  const prevMetRef = React.useRef(null);
  const nowMetIds = rows.filter((r) => r.met).map((r) => r.perk.id);
  const nowMetSet = new Set(nowMetIds);
  const justMet = prevMetRef.current === null ? new Set() : new Set(nowMetIds.filter((id) => !prevMetRef.current.has(id)));
  React.useEffect(() => { prevMetRef.current = nowMetSet; });
  const wonf = (n) => (window.won ? window.won(n) : Number(n || 0).toLocaleString("ko-KR"));
  const prodName = (pid) => { const p = (form.products || []).filter((x) => x.id === pid)[0]; return p ? p.name : "특정 상품"; };
  const condLabel = (k) => k.condType === "productQty" ? (prodName(k.productId) + " " + (k.threshold || 0) + "개 이상") : ("총 " + wonf(k.threshold) + "원 이상");
  const gotCount = rows.filter((r) => r.met).length;

  return (
    <div style={{ marginTop: 16, border: "1px solid var(--border-subtle)", borderRadius: 16, overflow: "hidden" }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "12px 14px", background: "var(--gray-50)" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <span style={{ fontSize: 14, fontWeight: 800, color: "var(--text-strong)" }}>특전</span>
          <window.KASHAPopover title="특전이란?" ariaLabel="특전 안내">조건(구매 금액·수량 등)을 달성하면 상품과 함께 무료로 받는 혜택이에요. 담는 즉시 달성 여부가 실시간으로 표시돼요.</window.KASHAPopover>
          {gotCount > 0 && <span style={{ fontSize: 12, fontWeight: 700, color: "var(--green-600)", background: "var(--green-50)", padding: "2px 8px", borderRadius: 999 }}>{gotCount}개 달성</span>}
        </div>
        <button onClick={() => setOpen(true)} style={{ display: "inline-flex", alignItems: "center", gap: 4, border: "none", background: "transparent", color: "var(--blue-600)", fontFamily: "var(--font-sans)", fontSize: 13, fontWeight: 700, cursor: "pointer" }}>특전 정보 <Icon name="chevronRight" size={15} /></button>
      </div>
      <div style={{ display: "flex", flexDirection: "column" }}>
        {rows.map((r, i) => (
          <div key={r.perk.id} className={"kasha-perk-row" + (r.met ? " met" : "")} style={{ display: "flex", alignItems: "center", gap: 11, padding: "11px 14px", borderTop: i ? "1px solid var(--border-subtle)" : "none", background: r.met ? "var(--green-50)" : "var(--gray-0)" }}>
            <span className={justMet.has(r.perk.id) ? "kasha-perk-pop" : undefined} style={{ flexShrink: 0, width: 26, height: 26, borderRadius: "50%", display: "flex", alignItems: "center", justifyContent: "center", background: r.met ? "var(--green-500)" : "var(--gray-200)", color: r.met ? "#fff" : "var(--gray-500)" }}>
              {r.met ? <Icon name="check" size={15} color="#fff" /> : <span style={{ fontSize: 12, fontWeight: 800 }}>{i + 1}</span>}
            </span>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13.5, fontWeight: 700, color: r.met ? "var(--green-600)" : "var(--text-strong)" }}>{r.perk.name}</div>
              <div style={{ fontSize: 11.5, color: "var(--text-tertiary)" }}>{condLabel(r.perk)}</div>
            </div>
            <span style={{ flexShrink: 0, fontSize: 12, fontWeight: 700, color: r.met ? "var(--green-600)" : "var(--text-tertiary)" }}>{r.remainLabel}</span>
          </div>
        ))}
      </div>

      {/* 항상 마운트 + open 토글 → 닫힘 애니메이션 재생(다른 모달과 통일) */}
      <Dialog open={open} onClose={() => setOpen(false)} style={{ maxWidth: 460, textAlign: "left" }}>
          <div style={{ textAlign: "left" }}>
            <h3 style={{ fontSize: 19, letterSpacing: "var(--tracking-title)" }}>특전 안내</h3>
            <p style={{ marginTop: 4, fontSize: 13, color: "var(--text-tertiary)" }}>구매 조건을 만족하면 아래 특전을 함께 드려요.</p>
            <div style={{ marginTop: 14, display: "flex", flexDirection: "column", gap: 12, maxHeight: "56vh", overflowY: "auto" }}>
              {rows.map((r, i) => (
                <div key={r.perk.id} style={{ display: "flex", gap: 12, padding: "12px", borderRadius: 14, border: "1.5px solid " + (r.met ? "var(--green-500)" : "var(--border-subtle)"), background: r.met ? "var(--green-50)" : "var(--gray-0)" }}>
                  <div style={{ width: 60, height: 60, borderRadius: 10, background: "var(--gray-100)", overflow: "hidden", flexShrink: 0 }}>{(r.perk.images || [])[0] ? <img src={r.perk.images[0]} alt="" style={{ width: "100%", height: "100%", objectFit: "cover" }} /> : null}</div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
                      <span style={{ fontSize: 12, fontWeight: 700, color: "var(--blue-600)" }}>{i + 1}단계</span>
                      {r.met && <span style={{ fontSize: 11.5, fontWeight: 700, color: "var(--green-600)" }}>· 달성</span>}
                    </div>
                    <div style={{ fontSize: 14.5, fontWeight: 800, color: "var(--text-strong)", marginTop: 2 }}>{r.perk.name}</div>
                    <div style={{ fontSize: 12, color: "var(--text-secondary)", marginTop: 2 }}>{condLabel(r.perk)}</div>
                    {r.perk.desc && <p style={{ fontSize: 12.5, color: "var(--text-body)", marginTop: 6, lineHeight: 1.6, whiteSpace: "pre-wrap" }}>{r.perk.desc}</p>}
                  </div>
                </div>
              ))}
            </div>
            <div style={{ marginTop: 16 }}><Button variant="secondary" fullWidth onClick={() => setOpen(false)}>닫기</Button></div>
          </div>
      </Dialog>
    </div>
  );
}
window.GoodsPerkBoard = GoodsPerkBoard;
