// inventory.jsx — 재고관리 (운영 전용, 깔끔 + 명확)
const { useState, useMemo, useEffect } = React;

const CAT = window.CATALOG;
const fmtKRW = (n) => "₩" + n.toLocaleString("ko-KR");
const CAT_LABEL = { road:"Road", gravel:"Gravel", mtb:"MTB", ebike:"E-bike", kids:"Kids" };
const LOC_LABEL = CAT.ops.locations;
const LOC_KEYS  = Object.keys(LOC_LABEL);

// 오늘 날짜
const todayStr = () => {
  const d = new Date();
  return `${d.getFullYear()}.${String(d.getMonth()+1).padStart(2,"0")}.${String(d.getDate()).padStart(2,"0")}`;
};
const nowTime = () => {
  const d = new Date();
  return `${String(d.getHours()).padStart(2,"0")}:${String(d.getMinutes()).padStart(2,"0")}`;
};

// ─── HEADER ──────────────────────────────────────────────
function InvHeader() {
  return (
    <header className="cat-header">
      <div className="cat-header-inner">
        <a className="cat-brand" href="#">
          <img className="cat-brand-mark" src="assets/symbol-brush.svg" alt="" />
          <div>
            <div className="cat-brand-name">김성호바이크 세종점</div>
            <div className="cat-brand-sub">Inventory ・ Operations</div>
          </div>
        </a>
        <div className="cat-header-sep" />
        <div className="cat-header-title">재고 <b>관리</b></div>
        <div className="cat-header-tools">
          <span>현재 시각 <b>{new Date().toLocaleString("ko-KR",{dateStyle:"short",timeStyle:"short"})}</b></span>
          <button className="cat-print-btn" onClick={() => window.print()}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><polyline points="6 9 6 2 18 2 18 9"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect width="12" height="8" x="6" y="14"/></svg>
            재고 출력
          </button>
        </div>
      </div>
    </header>
  );
}

// ─── PAGE NAV ────────────────────────────────────────────
function PageNav({ active }) {
  return (
    <nav className="cat-pagenav">
      <div className="cat-pagenav-inner">
        <a href="관리자.html">관리자 <small>Admin</small></a>
        <a href="스케줄러.html">스케줄러 <small>Scheduler</small></a>
        <a className={active === "catalog" ? "on" : ""} href="2026 카탈로그.html">카탈로그 <small>Catalog</small></a>
        <a className={active === "inventory" ? "on" : ""} href="재고관리.html">재고관리 <small>Inventory</small></a>
        <a className={active === "quote" ? "on" : ""} href="서비스 견적서.html">서비스 견적서 <small>Estimate</small></a>
        <div className="cat-pagenav-r">
          <span>마지막 갱신 · <b>{CAT.meta.asOf}</b> · {CAT.meta.updated}</span>
        </div>
      </div>
    </nav>
  );
}

// ─── v3.10 재고관리 보강 5건 (기획서 §4 Phase A) ─────────────────

// A1: 메인 KPI 큰 카드 — 총 평가액을 메인으로 강조
function InventoryHero({ bikes, moves }) {
  const role = window.AUTH?.role || "owner";
  const value     = bikes.reduce((a,b) => a + b.stock * b.priceShop, 0);
  const stock     = bikes.reduce((a,b) => a + b.stock, 0);
  const skus      = bikes.length;
  const lowCount  = bikes.filter(b => b.stock > 0 && b.stock <= (b.lowThreshold||1)).length;
  const outCount  = bikes.filter(b => b.stock === 0).length;

  // 자금 흐름 — 이번 달 입고/출고 가치 (mock — 운영시 stock_movements 집계)
  const monthIn  = (moves||[]).filter(m=>m.type==='IN').reduce((a,m)=> a + (m.qty||0)*(m.priceShop||1000000), 0);
  const monthOut = (moves||[]).filter(m=>m.type==='OUT').reduce((a,m)=> a + (m.qty||0)*(m.priceShop||1000000), 0);

  const fmtMoney = (n) => role === "owner" ? "₩" + (n||0).toLocaleString("ko-KR") : "₩***";

  return (
    <div style={{display:"grid",gridTemplateColumns:"1.7fr 1fr 1fr 1fr",gap:12,marginBottom:20}}>
      {/* 메인 — 총 평가액 */}
      <div style={{background:"linear-gradient(135deg, #1B1B1F, #2A2C30)",color:"#fff",
                   padding:"24px 28px",borderRadius:16,position:"relative",overflow:"hidden"}}>
        <div style={{fontSize:12,fontWeight:600,opacity:0.7,letterSpacing:"0.06em",textTransform:"uppercase"}}>
          총 재고 평가액
        </div>
        <div style={{fontSize:36,fontWeight:700,letterSpacing:"-0.02em",margin:"4px 0",
                     fontFeatureSettings:'"tnum" 1'}}>
          {fmtMoney(value)}
        </div>
        <div style={{display:"flex",gap:16,marginTop:14,fontSize:13,opacity:0.9}}>
          <span><b style={{fontSize:16}}>{skus}</b> SKU</span>
          <span style={{opacity:0.4}}>·</span>
          <span><b style={{fontSize:16}}>{stock}</b> 대</span>
          <span style={{opacity:0.4}}>·</span>
          <span>매장가 × 재고</span>
        </div>
        {(monthIn || monthOut) > 0 && (
          <div style={{marginTop:14,paddingTop:14,borderTop:"1px solid rgba(255,255,255,0.15)",
                       display:"flex",gap:14,fontSize:12}}>
            <span style={{color:"#4ADE80"}}>↗ 입고 {fmtMoney(monthIn)}</span>
            <span style={{color:"#F87171"}}>↘ 출고 {fmtMoney(monthOut)}</span>
          </div>
        )}
        <div style={{position:"absolute",top:0,right:0,bottom:0,width:120,
                     background:"radial-gradient(circle at 100% 0%, rgba(227,30,38,0.15), transparent 70%)"}}/>
      </div>

      {/* 작은 KPI 3개 */}
      <div className="inv-kpi-cell" style={{padding:18}}>
        <div className="inv-kpi-l">실 재고</div>
        <div className="inv-kpi-v">{stock}<small>대</small></div>
        <div className="inv-kpi-hint">{skus}개 SKU 합계</div>
      </div>
      <div className="inv-kpi-cell" style={{padding:18,borderLeft: lowCount ? "3px solid #DD7D02" : ""}}>
        <div className="inv-kpi-l">저재고</div>
        <div className={"inv-kpi-v " + (lowCount ? "warn":"")}>{lowCount}<small>건</small></div>
        <div className="inv-kpi-hint">{lowCount > 0 ? "발주 검토 필요" : "충분 ✓"}</div>
      </div>
      <div className="inv-kpi-cell" style={{padding:18,borderLeft: outCount ? "3px solid var(--red-500,#E31E26)" : ""}}>
        <div className="inv-kpi-l">품절</div>
        <div className={"inv-kpi-v " + (outCount ? "err":"")}>{outCount}<small>건</small></div>
        <div className="inv-kpi-hint">{outCount > 0 ? "즉시 입고 대기" : "전체 보유 ✓"}</div>
      </div>
    </div>
  );
}

// A2: 입고 예정 패널 — 본사 발주 후 입고 대기 (mock)
function PendingReceipts() {
  // 실 운영: pending_orders 테이블에서 fetch
  const items = [
    { sku:"TSL8-EXP", name:"Tarmac SL8 Expert", qty:2, ordered:"2026-05-22", expected:"2026-05-29", days: 3, status:"in_transit" },
    { sku:"PT-CHN-12", name:"Shimano CN-M8100 체인 (12s)", qty:10, ordered:"2026-05-24", expected:"2026-05-27", days: 1, status:"in_transit" },
    { sku:"ACC-HLM-EVD", name:"S-Works Evade III 헬멧", qty:3, ordered:"2026-05-25", expected:"2026-06-02", days: 7, status:"ordered" },
  ];
  if (items.length === 0) return null;
  return (
    <section style={{marginBottom:16,padding:"14px 18px",background:"#fff",
                     border:"1px solid var(--grey-200,#E5E8EB)",borderRadius:12}}>
      <div style={{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:10}}>
        <span style={{fontSize:14,fontWeight:600,color:"var(--grey-900)"}}>
          📦 입고 예정 <span style={{color:"var(--grey-500)",fontWeight:400,marginLeft:6}}>{items.length}건 · 본사 발주분</span>
        </span>
        <button style={{background:"none",border:"none",color:"var(--grey-500)",fontSize:12,cursor:"pointer"}}
          onClick={()=>alert("발주 관리 페이지 — Phase 5 직전 출시")}>전체 보기 →</button>
      </div>
      <div style={{display:"flex",gap:8,overflowX:"auto",paddingBottom:4}}>
        {items.map(it => {
          const urgent = it.days <= 2;
          return (
            <div key={it.sku} style={{
              flex:"0 0 240px",padding:"12px 14px",
              background: urgent ? "#FFF9E7" : "var(--grey-50)",
              border:`1px solid ${urgent ? "#F5C97A" : "var(--grey-100)"}`,
              borderRadius:8
            }}>
              <div style={{fontSize:11,color:"var(--grey-600)",fontFamily:"var(--font-mono)"}}>{it.sku}</div>
              <div style={{fontSize:13,fontWeight:600,color:"var(--grey-900)",margin:"2px 0",
                          whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}}>
                {it.name}
              </div>
              <div style={{display:"flex",alignItems:"center",justifyContent:"space-between",marginTop:6}}>
                <span style={{fontSize:12,color:"var(--grey-700)"}}>{it.qty}개 · {it.expected}</span>
                <span style={{fontSize:11,fontWeight:700,color: urgent ? "#DD7D02" : "var(--grey-600)"}}>
                  D-{it.days}
                </span>
              </div>
            </div>
          );
        })}
      </div>
    </section>
  );
}

// A4: 일일 마감 점검 카드 — 17시 이후 자동 노출 (또는 항상)
function DailyClosing({ moves }) {
  const [open, setOpen] = React.useState(false);
  const [checked, setChecked] = React.useState(() =>
    localStorage.getItem("sejong-closed-" + new Date().toISOString().slice(0,10)) === "1"
  );

  const inCount = (moves||[]).filter(m=>m.type==='IN').length;
  const outCount = (moves||[]).filter(m=>m.type==='OUT').length;
  const moveCount = (moves||[]).filter(m=>m.type==='MOVE_OUT' || m.type==='MOVE_IN').length / 2;
  const totalMoves = inCount + outCount + moveCount;

  const today = new Date().toISOString().slice(0,10);
  const doClose = () => {
    localStorage.setItem("sejong-closed-" + today, "1");
    setChecked(true);
    if (window.SECURITY) window.SECURITY.showToast("일일 마감 완료 ✓", "success");
  };

  return (
    <section style={{marginBottom:16,background:"#fff",
                     border:`1px solid ${checked ? "var(--green-500,#A7E0BF)" : "var(--grey-200,#E5E8EB)"}`,
                     borderRadius:12,overflow:"hidden"}}>
      <button onClick={()=>setOpen(!open)} style={{
        display:"flex",alignItems:"center",gap:12,width:"100%",
        padding:"14px 18px",border:0,background:"transparent",cursor:"pointer",
        borderBottom: open ? "1px solid var(--grey-100)" : "none"
      }}>
        <span style={{fontSize:20}}>{checked ? "✅" : "🌙"}</span>
        <span style={{flex:1,textAlign:"left"}}>
          <span style={{display:"block",fontSize:14,fontWeight:600,color:"var(--grey-900)"}}>
            일일 마감 점검 {checked && <span style={{color:"var(--green-500,#029359)",fontSize:12,marginLeft:6}}>· 완료</span>}
          </span>
          <span style={{display:"block",fontSize:12,color:"var(--grey-600)",marginTop:2}}>
            오늘 입고 {inCount}건 / 출고 {outCount}건 / 이동 {moveCount}건 · 총 {totalMoves}건 처리
          </span>
        </span>
        <span style={{fontSize:12,color:"var(--grey-500)"}}>{open ? "▲" : "▼"}</span>
      </button>
      {open && (
        <div style={{padding:"14px 18px",background:"var(--grey-50)"}}>
          <ul style={{margin:0,paddingLeft:20,fontSize:13,color:"var(--grey-700)",lineHeight:1.7}}>
            <li>오늘 모든 입출고 정확히 처리됨</li>
            <li>저재고 / 품절 모델 확인 완료</li>
            <li>다음 영업일 입고 예정 확인 완료</li>
            <li>지연된 정비 / 견적 응답 확인 완료</li>
          </ul>
          <div style={{display:"flex",gap:8,marginTop:12,justifyContent:"flex-end"}}>
            {!checked && (
              <button className="cat-btn cat-btn-primary" onClick={doClose}>
                ✓ 마감 확인
              </button>
            )}
            {checked && (
              <span style={{fontSize:12,color:"var(--grey-500)"}}>
                마감 완료 — 내일 자정에 자동 초기화
              </span>
            )}
          </div>
        </div>
      )}
    </section>
  );
}

// ─── KPI STRIP ───────────────────────────────────────────
function KpiStrip({ bikes }) {
  const stock     = bikes.reduce((a,b) => a + b.stock, 0);
  const value     = bikes.reduce((a,b) => a + b.stock * b.priceShop, 0);
  const skus      = bikes.length;
  const lowCount  = bikes.filter(b => b.stock > 0 && b.stock <= b.lowThreshold).length;
  const outCount  = bikes.filter(b => b.stock === 0).length;

  return (
    <div className="inv-kpi">
      <div className="inv-kpi-cell">
        <div className="inv-kpi-l">총 SKU</div>
        <div className="inv-kpi-v">{skus}<small>라인</small></div>
        <div className="inv-kpi-hint">전체 카테고리</div>
      </div>
      <div className="inv-kpi-cell">
        <div className="inv-kpi-l">실 재고</div>
        <div className="inv-kpi-v">{stock}<small>대</small></div>
        <div className="inv-kpi-hint">실재고 수량</div>
      </div>
      <div className="inv-kpi-cell">
        <div className="inv-kpi-l">총 평가액</div>
        <div className="inv-kpi-v">{fmtKRW(value)}</div>
        <div className="inv-kpi-hint">매장 판매가 × 재고</div>
      </div>
      <div className="inv-kpi-cell">
        <div className="inv-kpi-l">저재고</div>
        <div className={"inv-kpi-v " + (lowCount ? "warn":"")}>{lowCount}<small>건</small></div>
        <div className="inv-kpi-hint">발주 검토 필요</div>
      </div>
      <div className="inv-kpi-cell">
        <div className="inv-kpi-l">품절</div>
        <div className={"inv-kpi-v " + (outCount ? "err":"")}>{outCount}<small>건</small></div>
        <div className="inv-kpi-hint">즉시 입고 대기</div>
      </div>
    </div>
  );
}

// ─── TOOLBAR ─────────────────────────────────────────────
function Toolbar({ q, setQ, cat, setCat, loc, setLoc, status, setStatus }) {
  return (
    <div className="inv-toolbar">
      <div className="inv-toolbar-row">
        <div className="inv-toolbar-search">
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" style={{color:"var(--fg-tertiary)"}}><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
          <input
            placeholder="SKU · 모델 · 그룹세트 · 시리즈로 검색"
            value={q} onChange={(e) => setQ(e.target.value)}
          />
        </div>
        <button className="inv-tbl-action" onClick={() => { setQ(""); setCat("all"); setLoc("all"); setStatus("all"); }}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M3 12a9 9 0 1 0 9-9"/><polyline points="3 4 3 12 11 12"/></svg>
          필터 초기화
        </button>
      </div>
      <div className="inv-toolbar-row">
        <span className="inv-toolbar-label">카테고리</span>
        {CAT.categories.map(c => (
          <button key={c.id} className={"inv-chip " + (cat === c.id ? "on" : "")} onClick={() => setCat(c.id)}>
            {c.label}
          </button>
        ))}
        <div className="inv-divider" />
        <span className="inv-toolbar-label">위치</span>
        <button className={"inv-chip " + (loc === "all" ? "on" : "")} onClick={() => setLoc("all")}>전체</button>
        {Object.entries(LOC_LABEL).map(([k, v]) => (
          <button key={k} className={"inv-chip " + (loc === k ? "on" : "")} onClick={() => setLoc(k)}>
            {v} <small>{k}</small>
          </button>
        ))}
        <div className="inv-divider" />
        <span className="inv-toolbar-label">재고상태</span>
        <button className={"inv-chip " + (status === "all" ? "on" : "")} onClick={() => setStatus("all")}>전체</button>
        <button className={"inv-chip " + (status === "ok"  ? "on" : "")} onClick={() => setStatus("ok")}>재고있음</button>
        <button className={"inv-chip " + (status === "low" ? "on" : "")} onClick={() => setStatus("low")}>저재고</button>
        <button className={"inv-chip " + (status === "out" ? "on" : "")} onClick={() => setStatus("out")}>품절</button>
      </div>
    </div>
  );
}

// ─── LOW STOCK ALERT ─────────────────────────────────────
function LowAlert({ bikes }) {
  const lows = bikes.filter(b => b.stock <= b.lowThreshold);
  if (lows.length === 0) return null;
  const out = lows.filter(b => b.stock === 0).length;
  return (
    <div className="inv-alert">
      <div className="inv-alert-icon">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
          <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/>
          <line x1="12" y1="9" x2="12" y2="13"/>
          <line x1="12" y1="17" x2="12" y2="17"/>
        </svg>
      </div>
      <div className="inv-alert-msg">
        <b>{lows.length}개 SKU</b>가 저재고/품절 상태입니다.
        {out > 0 && <> 그 중 <b>{out}개는 품절</b> — 즉시 발주가 필요합니다.</>}
      </div>
      <button className="inv-alert-link">발주 라인 보기 →</button>
    </div>
  );
}

// ─── MOVEMENT MODAL (single OR batch, per-row settings) ──
function MovementModal({ bike, initialTab, allBikes, onClose, onApply }) {
  const [tab, setTab] = useState(initialTab || "in");
  // selected: [{ sku, qty, loc, size, checked }]
  const [selected, setSelected] = useState(() =>
    bike ? [{ sku: bike.sku, qty: 1, loc: bike.location, size: bike.sizes[0] || "", checked: false }] : []
  );
  const [note, setNote] = useState("");
  const [pickQ, setPickQ] = useState("");
  const [showPicker, setShowPicker] = useState(!bike);

  useEffect(() => {
    const k = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", k);
    return () => window.removeEventListener("keydown", k);
  }, []);

  // selected의 실제 bike 객체 매핑
  const selectedBikes = selected
    .map(s => {
      const b = allBikes.find(x => x.sku === s.sku);
      return b ? { ...b, qty: s.qty, loc: s.loc, size: s.size, checked: s.checked } : null;
    })
    .filter(Boolean);

  const checkedCount = selected.filter(s => s.checked).length;

  const addBike = (sku) => {
    if (selected.find(s => s.sku === sku)) return;
    const b = allBikes.find(x => x.sku === sku);
    setSelected([...selected, {
      sku, qty: 1,
      loc: b ? b.location : "A",
      size: b && b.sizes ? b.sizes[0] : "",
      checked: false
    }]);
    setPickQ("");
  };
  const removeBike = (sku) => setSelected(selected.filter(s => s.sku !== sku));
  const toggleCheck = (sku) => setSelected(selected.map(s => s.sku === sku ? { ...s, checked: !s.checked } : s));
  const toggleAll = () => {
    const allChecked = selected.length > 0 && selected.every(s => s.checked);
    setSelected(selected.map(s => ({ ...s, checked: !allChecked })));
  };
  const updateQty = (sku, delta) => {
    setSelected(selected.map(s => {
      if (s.sku !== sku) return s;
      if (tab === "out") {
        const b = allBikes.find(x => x.sku === sku);
        return { ...s, qty: Math.max(1, Math.min(b ? b.stock : 999, s.qty + delta)) };
      }
      return { ...s, qty: Math.max(1, s.qty + delta) };
    }));
  };
  const setQtyDirect = (sku, val) => {
    setSelected(selected.map(s => s.sku === sku ? { ...s, qty: Math.max(1, val) } : s));
  };
  const setRowLoc = (sku, loc) => {
    setSelected(selected.map(s => s.sku === sku ? { ...s, loc } : s));
  };
  const setRowSize = (sku, size) => {
    setSelected(selected.map(s => s.sku === sku ? { ...s, size } : s));
  };
  // 일괄: 체크된 모든 row의 loc/qty/size 일괄 변경
  const bulkSetLoc = (loc) => {
    setSelected(selected.map(s => s.checked ? { ...s, loc } : s));
  };
  const bulkSetQty = (qty) => {
    setSelected(selected.map(s => {
      if (!s.checked) return s;
      if (tab === "out") {
        const b = allBikes.find(x => x.sku === s.sku);
        return { ...s, qty: Math.max(1, Math.min(b ? b.stock : 999, qty)) };
      }
      return { ...s, qty: Math.max(1, qty) };
    }));
  };

  const candidates = useMemo(() => {
    const s = pickQ.trim().toLowerCase();
    return allBikes
      .filter(b => !selected.find(x => x.sku === b.sku))
      .filter(b => !s ||
        b.name.toLowerCase().includes(s) ||
        b.sku.toLowerCase().includes(s) ||
        b.series.toLowerCase().includes(s))
      .slice(0, 5);
  }, [pickQ, selected, allBikes]);

  const canApply = useMemo(() => {
    if (selectedBikes.length === 0) return false;
    if (tab === "out") return selectedBikes.every(b => b.qty <= b.stock);
    if (tab === "move") return selectedBikes.some(b => b.loc !== b.location); // 최소 1개는 위치 변경
    return true; // 입고는 항상 가능
  }, [selectedBikes, tab]);

  const apply = () => {
    if (!canApply) return;
    if (tab === "in") {
      selectedBikes.forEach(b => onApply(b, { kind: "입고", qty: b.qty, note, toLoc: b.loc, size: b.size }));
    } else if (tab === "out") {
      selectedBikes.forEach(b => onApply(b, { kind: "출고", qty: b.qty, note, size: b.size }));
    } else {
      selectedBikes.forEach(b => {
        if (b.loc !== b.location) onApply(b, { kind: "위치변경", from: b.location, to: b.loc, note, size: b.size });
      });
    }
  };

  const allChecked = selected.length > 0 && selected.every(s => s.checked);
  const someChecked = checkedCount > 0;

  return (
    <>
      <div className="inv-modal-scrim" onClick={onClose} />
      <div className="inv-modal inv-modal-batch" role="dialog">
        <button className="inv-modal-x" onClick={onClose}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M18 6 6 18M6 6l12 12"/></svg>
        </button>
        <div className="inv-modal-head">
          <div className="inv-modal-eyebrow">재고 이동 · Stock Movement {selectedBikes.length > 0 && <span style={{color:"var(--fg-tertiary)",marginLeft:8,fontSize:11}}>{selectedBikes.length}개 모델 선택</span>}</div>
          <div style={{fontSize:14, color:"var(--fg-secondary)"}}>
            모델별로 수량 · 위치를 개별 지정. 체크박스로 여러 행을 한 번에 수정할 수 있습니다.
          </div>
        </div>

        <div className="inv-modal-tabs">
          <button className={"inv-modal-tab " + (tab==="in" ? "on in" : "")}  onClick={()=>setTab("in")}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2"><line x1="12" y1="5" x2="12" y2="19"/><polyline points="19 12 12 19 5 12"/></svg>
            입고
          </button>
          <button className={"inv-modal-tab " + (tab==="out" ? "on out" : "")} onClick={()=>setTab("out")}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2"><line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/></svg>
            출고
          </button>
          <button className={"inv-modal-tab " + (tab==="move" ? "on move" : "")} onClick={()=>setTab("move")}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg>
            위치 변경
          </button>
        </div>

        <div className="inv-modal-body">
          {/* 검색으로 모델 추가 */}
          <div className="inv-mod-field">
            <label>모델 추가</label>
            <div className="inv-mod-search">
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" style={{color:"var(--fg-tertiary)"}}><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
              <input
                placeholder="모델명 · SKU · 시리즈로 검색해 추가"
                value={pickQ}
                onChange={(e)=>{setPickQ(e.target.value); setShowPicker(true);}}
                onFocus={()=>setShowPicker(true)}
              />
              {pickQ && <button className="inv-mod-clear" onClick={()=>setPickQ("")}>지우기</button>}
            </div>
            {showPicker && pickQ && candidates.length > 0 && (
              <div className="inv-mod-results">
                {candidates.map(b => (
                  <button key={b.sku} className="inv-mod-result" onClick={()=>addBike(b.sku)}>
                    <div className="inv-mod-result-sw" style={{background: b.gradient}} />
                    <div className="inv-mod-result-name">
                      <b>{b.name}</b>
                      <small>{b.sku} · {LOC_LABEL[b.location]} · 재고 {b.stock}대</small>
                    </div>
                    <span className="inv-mod-add-btn">＋ 추가</span>
                  </button>
                ))}
              </div>
            )}
            {showPicker && pickQ && candidates.length === 0 && (
              <div className="inv-mod-noresults">검색 결과 없음 · 이미 선택했거나 일치하는 모델이 없습니다.</div>
            )}
          </div>

          {/* BULK EDIT BAR — checked rows에 대해 일괄 수정 */}
          {selectedBikes.length > 0 && (
            <div className={"inv-mod-bulk " + (someChecked ? "on" : "")}>
              <label className="inv-mod-checkall">
                <input type="checkbox" checked={allChecked} onChange={toggleAll} />
                <span>{someChecked ? `${checkedCount}개 선택됨` : "전체 선택"}</span>
              </label>
              {someChecked && (
                <>
                  <div className="inv-mod-bulk-sep" />
                  {tab !== "out" && (
                    <>
                      <span className="inv-mod-bulk-l">일괄 위치 →</span>
                      <div className="inv-mod-bulk-locs">
                        {LOC_KEYS.map(k => (
                          <button key={k} onClick={()=>bulkSetLoc(k)}>
                            {LOC_LABEL[k]}<small>{k}</small>
                          </button>
                        ))}
                      </div>
                    </>
                  )}
                  {tab !== "move" && (
                    <>
                      <div className="inv-mod-bulk-sep" />
                      <span className="inv-mod-bulk-l">일괄 수량 →</span>
                      <div className="inv-mod-bulk-qtys">
                        {[1, 2, 3, 5, 10].map(n => (
                          <button key={n} onClick={()=>bulkSetQty(n)}>{n}대</button>
                        ))}
                      </div>
                    </>
                  )}
                </>
              )}
            </div>
          )}

          {/* 선택된 모델 목록 */}
          <div className="inv-mod-field">
            <label>선택된 모델 {selectedBikes.length > 0 && <em style={{color:"var(--fg-primary)",fontStyle:"normal",fontWeight:700,marginLeft:6}}>· {selectedBikes.length}개</em>}</label>
            {selectedBikes.length === 0 ? (
              <div className="inv-mod-empty">
                위 검색창에서 모델을 추가하세요.
              </div>
            ) : (
              <div className="inv-mod-selected">
                {selectedBikes.map(b => {
                  const outOver = tab === "out" && b.qty > b.stock;
                  const sameLoc = tab === "move" && b.loc === b.location;
                  const bodyCls = tab === "out" ? "out" : tab === "move" ? "move" : "";
                  return (
                    <div className={"inv-mod-selrow per-row " + (outOver ? "err" : "") + (sameLoc ? " skip" : "")} key={b.sku}>
                      <div className="inv-mod-row-head">
                        <label className="inv-mod-row-check">
                          <input type="checkbox" checked={b.checked} onChange={()=>toggleCheck(b.sku)} />
                        </label>
                        <div className="inv-mod-result-sw" style={{background: b.gradient}} />
                        <div className="inv-mod-result-name">
                          <b>{b.name}</b>
                          <small>
                            <span className={"inv-pill loc-" + b.location} style={{padding:"1px 6px",fontSize:9}}>
                              {LOC_LABEL[b.location]} · {b.location}
                            </span>
                            {" "}· {b.sku} · 재고 {b.stock}대
                          </small>
                        </div>
                        <button className="inv-mod-remove" onClick={()=>removeBike(b.sku)}>
                          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M18 6 6 18M6 6l12 12"/></svg>
                        </button>
                      </div>

                      <div className={"inv-mod-row-body " + bodyCls}>
                        {b.sizes && b.sizes.length > 0 && (
                          <div className="inv-mod-row-section">
                            <label>사이즈</label>
                            <div className="inv-mod-size-pills">
                              {b.sizes.map(sz => (
                                <button
                                  key={sz}
                                  className={b.size === sz ? "on" : ""}
                                  onClick={()=>setRowSize(b.sku, sz)}
                                >
                                  {sz}
                                </button>
                              ))}
                            </div>
                          </div>
                        )}

                        {tab !== "move" && (
                          <div className="inv-mod-row-section">
                            <label>{tab === "in" ? "입고 수량" : "출고 수량"}</label>
                            <div className="inv-mod-qty inv-mod-qty-sm">
                              <button onClick={()=>updateQty(b.sku,-1)}>−</button>
                              <input type="number" min="1" max={tab==="out" ? b.stock : 999} value={b.qty}
                                onChange={(e)=>setQtyDirect(b.sku, Number(e.target.value) || 1)}
                              />
                              <button onClick={()=>updateQty(b.sku, 1)}>＋</button>
                              <span>대</span>
                            </div>
                          </div>
                        )}

                        {tab !== "out" && (
                          <div className="inv-mod-row-section">
                            <label>{tab === "in" ? "입고 위치" : "이동 위치"}</label>
                            <div className="inv-mod-row-locs">
                              {LOC_KEYS.map(k => (
                                <button
                                  key={k}
                                  className={(b.loc === k ? "on " : "") + (tab === "move" && b.location === k ? "current" : "")}
                                  onClick={()=>setRowLoc(b.sku, k)}
                                >
                                  {LOC_LABEL[k]}
                                  <small>{k}</small>
                                </button>
                              ))}
                            </div>
                          </div>
                        )}
                      </div>
                    </div>
                  );
                })}
              </div>
            )}
          </div>

          {tab === "out" && selectedBikes.length > 0 && (
            <div className="inv-mod-info">
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
              <span>출고는 각 모델의 <b>현재 위치</b>에서 차감됩니다.</span>
            </div>
          )}

          {/* 공통 메모 */}
          <div className="inv-mod-field">
            <label>공통 메모 (선택)</label>
            <input
              type="text"
              value={note}
              onChange={(e)=>setNote(e.target.value)}
              placeholder={tab === "in" ? "예) 본사 발주 5/24 분 입고" : tab === "out" ? "예) 고객 매장 출고 일괄" : "예) 시승 라인으로 전환"}
            />
          </div>

          {/* 변경 후 프리뷰 */}
          {selectedBikes.length > 0 && (
            <div className="inv-mod-preview-list">
              <div className="inv-mod-preview-h">변경 후 미리보기</div>
              {selectedBikes.map(b => {
                const sameLoc = tab === "move" && b.loc === b.location;
                const locChange = tab === "in" && b.loc !== b.location;
                return (
                  <div className="inv-mod-preview-row" key={b.sku}>
                    <span>
                      {b.name}
                      {b.size && <span style={{marginLeft:6,padding:"1px 6px",background:"var(--neutral-100)",borderRadius:3,fontSize:11,fontWeight:600,color:"var(--fg-secondary)"}}>{b.size}</span>}
                    </span>
                    <b>
                      {tab === "in"   && <>
                        {b.stock} → {b.stock + b.qty}대 (+{b.qty})
                        {locChange && <span style={{color:"var(--info-500)",marginLeft:8,fontSize:11}}>· {LOC_LABEL[b.location]} → {LOC_LABEL[b.loc]}</span>}
                        {!locChange && <span style={{color:"var(--fg-tertiary)",marginLeft:8,fontSize:11}}>· {LOC_LABEL[b.loc]}</span>}
                      </>}
                      {tab === "out"  && (b.qty > b.stock
                        ? <em style={{color:"var(--red-500)",fontStyle:"normal"}}>재고 부족 ({b.stock}대)</em>
                        : <>{b.stock} → {Math.max(0, b.stock - b.qty)}대 (−{b.qty}) <span style={{color:"var(--fg-tertiary)",marginLeft:4,fontSize:11}}>· {LOC_LABEL[b.location]}에서</span></>)}
                      {tab === "move" && (sameLoc
                        ? <em style={{color:"var(--fg-quaternary)",fontStyle:"normal"}}>건너뜀</em>
                        : `${LOC_LABEL[b.location]} → ${LOC_LABEL[b.loc]}`)}
                    </b>
                  </div>
                );
              })}
            </div>
          )}
        </div>

        <div className="inv-modal-actions">
          <button className="inv-tbl-action" onClick={onClose}>취소</button>
          <button
            className={"inv-tbl-action primary " + (tab==="out" ? "warn" : tab==="move" ? "info" : "")}
            disabled={!canApply}
            onClick={apply}
          >
            {tab === "in"   && `＋ ${selectedBikes.length}개 모델 입고`}
            {tab === "out"  && `− ${selectedBikes.length}개 모델 출고`}
            {tab === "move" && `→ ${selectedBikes.filter(b => b.loc !== b.location).length}개 모델 위치 변경`}
          </button>
        </div>
      </div>
    </>
  );
}

// ─── QUICK ACTIONS (위쪽 섹션) ────────────────────────────
// v3.14: "신상 등록" 4번째 타일 신설 (사용자 요청)
function QuickActions({ onOpen, onCreate }) {
  return (
    <div className="inv-quick">
      <div className="inv-quick-head">
        <div className="inv-quick-h">빠른 재고 처리</div>
        <div className="inv-quick-sub">신상 등록 · 입고 · 출고 · 위치 이동을 한 번에 처리할 수 있습니다.</div>
      </div>
      <div className="inv-quick-grid" style={{display:"grid", gridTemplateColumns:"repeat(4, 1fr)", gap:10}}>
        {/* v3.14: 신상 등록 (사장님 운영 첫 시작점) */}
        <button className="inv-quick-tile new" onClick={onCreate} style={{
          display:"flex", alignItems:"center", gap:12, padding:"14px 16px",
          background:"#fff", border:"1px solid var(--grey-200,#E5E8EB)", borderRadius:12,
          cursor:"pointer", textAlign:"left", transition:"transform .15s, box-shadow .15s, border-color .15s",
        }}
        onMouseEnter={e=>{e.currentTarget.style.transform="translateY(-2px)";e.currentTarget.style.boxShadow="0 4px 12px rgba(227,30,38,0.12)";e.currentTarget.style.borderColor="var(--red-500,#E31E26)";}}
        onMouseLeave={e=>{e.currentTarget.style.transform="";e.currentTarget.style.boxShadow="";e.currentTarget.style.borderColor="var(--grey-200,#E5E8EB)";}}>
          <div style={{
            width:40, height:40, borderRadius:10, flexShrink:0,
            background:"var(--red-50,#FFE5E6)", color:"var(--red-500,#E31E26)",
            display:"flex", alignItems:"center", justifyContent:"center", fontSize:18,
          }}>🆕</div>
          <div style={{flex:1, minWidth:0}}>
            <b style={{display:"block", fontSize:14, color:"var(--grey-900,#191F28)"}}>신상 등록</b>
            <small style={{display:"block", fontSize:12, color:"var(--grey-600,#6B7684)", marginTop:2}}>새 모델 추가 · SKU + 사진</small>
          </div>
          <div style={{fontSize:18, color:"var(--red-500,#E31E26)", fontWeight:700}}>＋</div>
        </button>

        <button className="inv-quick-tile in" onClick={() => onOpen("in")}>
          <div className="inv-quick-icon">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4"><line x1="12" y1="5" x2="12" y2="19"/><polyline points="19 12 12 19 5 12"/></svg>
          </div>
          <div className="inv-quick-body">
            <b>입고 등록</b>
            <small>본사 발주 / 신규 입고 처리</small>
          </div>
          <div className="inv-quick-go">＋</div>
        </button>
        <button className="inv-quick-tile out" onClick={() => onOpen("out")}>
          <div className="inv-quick-icon">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4"><line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/></svg>
          </div>
          <div className="inv-quick-body">
            <b>출고 등록</b>
            <small>고객 인도 / 재고 차감</small>
          </div>
          <div className="inv-quick-go">−</div>
        </button>
        <button className="inv-quick-tile move" onClick={() => onOpen("move")}>
          <div className="inv-quick-icon">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg>
          </div>
          <div className="inv-quick-body">
            <b>위치 이동</b>
            <small>쇼룸 1F · 2F · 창고 간 전환</small>
          </div>
          <div className="inv-quick-go">⇄</div>
        </button>
      </div>
    </div>
  );
}

// ─── RECENT MOVEMENTS CARD ───────────────────────────────
function RecentMovements({ moves }) {
  if (moves.length === 0) return null;
  return (
    <div className="inv-moves">
      <div className="inv-moves-head">
        <div className="inv-moves-h">최근 재고 이동 <small>이번 세션 · {moves.length}건</small></div>
      </div>
      <div className="inv-moves-list">
        {moves.slice(0, 6).map((m, i) => (
          <div className="inv-move-row" key={i}>
            <div className={"inv-move-tag " + m.kindTone}>{m.kind}</div>
            <div className="inv-move-name">
              <b>{m.name}{m.size && <span style={{marginLeft:6,padding:"1px 6px",background:"var(--neutral-100)",borderRadius:3,fontSize:10,fontWeight:700,color:"var(--fg-secondary)",letterSpacing:0}}>{m.size}</span>}</b>
              <small>{m.sku}</small>
            </div>
            <div className="inv-move-detail">{m.detail}</div>
            {m.note && <div className="inv-move-note">"{m.note}"</div>}
            <div className="inv-move-time">{m.date} {m.time}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ─── STOCK MATRIX BOARD — 위치 × 사이즈 직관적 현황 ─────
function StockBoard({ bikes }) {
  const [collapsed, setCollapsed] = useState(false);
  const [view, setView] = useState("card"); // card | table
  // 전체 위치별 합계
  const locTotals = useMemo(() => {
    const t = { A: 0, B: 0, W: 0, T: 0 };
    bikes.forEach(b => {
      if (!b.stockMatrix) return;
      Object.values(b.stockMatrix).forEach(row => {
        LOC_KEYS.forEach(k => { t[k] += row[k] || 0; });
      });
    });
    return t;
  }, [bikes]);
  const grandTotal = LOC_KEYS.reduce((a, k) => a + locTotals[k], 0);

  return (
    <div className="inv-board">
      <div className="inv-board-head">
        <div className="inv-board-h">
          위치 × 사이즈 재고 현황판
          <small>전체 {grandTotal}대 ・ 16 모델</small>
        </div>
        <div className="inv-board-tot">
          {LOC_KEYS.map(k => (
            <div key={k} className={"inv-board-tot-cell loc-" + k}>
              <small>{LOC_LABEL[k]}</small>
              <b>{locTotals[k]}<em>대</em></b>
            </div>
          ))}
          <div className="inv-board-view-toggle">
            <button className={view === "card" ? "on" : ""} onClick={()=>setView("card")} title="카드 보기">
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>
              카드
            </button>
            <button className={view === "table" ? "on" : ""} onClick={()=>setView("table")} title="표 보기">
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
              표
            </button>
          </div>
          <button className="inv-board-toggle" onClick={()=>setCollapsed(!collapsed)}>
            {collapsed ? "펼치기 ↓" : "접기 ↑"}
          </button>
        </div>
      </div>

      {!collapsed && (
        view === "card"
          ? <div className="inv-board-grid">
              {bikes.map(b => <StockBoardCard key={b.sku} bike={b} />)}
            </div>
          : <StockBoardTable bikes={bikes} />
      )}
    </div>
  );
}

// ─── 표 보기: 모델 = 행, 위치 = 열, 셀 = 사이즈별 분포 ───
function StockBoardTable({ bikes }) {
  return (
    <div className="inv-boardt-wrap">
      <table className="inv-boardt">
        <thead>
          <tr>
            <th style={{width:300}}>모델</th>
            <th className="num" style={{width:64}}>총</th>
            {LOC_KEYS.map(k => (
              <th key={k} className={"loc-" + k}>
                <b>{LOC_LABEL[k]}</b>
                <small>{k}</small>
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {bikes.map(b => {
            const locStock = LOC_KEYS.map(k => {
              const sizes = b.sizes
                .map(sz => ({ sz, n: b.stockMatrix?.[sz]?.[k] || 0 }))
                .filter(x => x.n > 0);
              const total = sizes.reduce((a, x) => a + x.n, 0);
              return { loc: k, sizes, total };
            });
            return (
              <tr key={b.sku} className={b.stock === 0 ? "row-out" : b.stock <= b.lowThreshold ? "row-low" : ""}>
                <td>
                  <div className="inv-boardt-name">
                    <div className="inv-boardt-sw" style={{background: b.gradient}} />
                    <div>
                      <b>{b.name}</b>
                      <small>{b.sku} · {b.tier}</small>
                    </div>
                  </div>
                </td>
                <td className="num">
                  <span className={"inv-boardt-tot " + (b.stock === 0 ? "zero" : b.stock <= b.lowThreshold ? "low" : "")}>
                    {b.stock}<em>대</em>
                  </span>
                </td>
                {locStock.map(({ loc, sizes, total }) => (
                  <td key={loc} className={"inv-boardt-loc " + (total === 0 ? "empty" : "")}>
                    {total === 0 ? (
                      <span className="inv-boardt-dash">—</span>
                    ) : (
                      <div className="inv-boardt-chips">
                        {sizes.map(({ sz, n }) => (
                          <span key={sz} className={"inv-boardt-chip loc-" + loc}>
                            <em>{sz}</em>
                            <b>{n}</b>
                          </span>
                        ))}
                        <span className="inv-boardt-loc-tot">/ {total}</span>
                      </div>
                    )}
                  </td>
                ))}
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

function StockBoardCard({ bike }) {
  if (!bike.stockMatrix) return null;
  const sizes = bike.sizes;
  const sizeTotals = sizes.map(sz => LOC_KEYS.reduce((a, k) => a + (bike.stockMatrix[sz]?.[k] || 0), 0));
  const locTotals = LOC_KEYS.map(k => sizes.reduce((a, sz) => a + (bike.stockMatrix[sz]?.[k] || 0), 0));
  const max = Math.max(1, ...sizes.flatMap(sz => LOC_KEYS.map(k => bike.stockMatrix[sz]?.[k] || 0)));

  return (
    <div className={"inv-board-card " + (bike.stock === 0 ? "out" : bike.stock <= bike.lowThreshold ? "low" : "")}>
      <div className="inv-board-card-head">
        <div className="inv-board-card-sw" style={{background: bike.gradient}} />
        <div className="inv-board-card-name">
          <b>{bike.name}</b>
          <small>{bike.sku} · {bike.tier}</small>
        </div>
        <div className="inv-board-card-tot">
          <b>{bike.stock}</b>
          <small>대</small>
        </div>
      </div>
      <div className="inv-board-matrix" style={{gridTemplateColumns: `64px repeat(${sizes.length}, 1fr) 50px`}}>
        {/* 헤더 행 */}
        <div className="inv-board-cell head corner"></div>
        {sizes.map(sz => (
          <div key={sz} className="inv-board-cell head">{sz}</div>
        ))}
        <div className="inv-board-cell head tot">총</div>

        {/* 위치별 행 */}
        {LOC_KEYS.map(k => (
          <React.Fragment key={k}>
            <div className={"inv-board-cell rowhead loc-" + k}>
              <b>{LOC_LABEL[k]}</b>
              <small>{k}</small>
            </div>
            {sizes.map(sz => {
              const n = bike.stockMatrix[sz]?.[k] || 0;
              const intensity = n / max;
              return (
                <div
                  key={k+sz}
                  className={"inv-board-cell data " + (n === 0 ? "zero" : "") + " loc-" + k}
                  style={n > 0 ? { background: `rgba(227,30,38,${0.08 + intensity * 0.42})`, color: intensity > 0.6 ? "#fff" : "var(--fg-primary)" } : undefined}
                  title={`${LOC_LABEL[k]} · ${sz} · ${n}대`}
                >
                  {n > 0 ? n : "·"}
                </div>
              );
            })}
            <div className="inv-board-cell tot">{locTotals[LOC_KEYS.indexOf(k)]}</div>
          </React.Fragment>
        ))}

        {/* 사이즈별 합계 행 */}
        <div className="inv-board-cell rowhead foot">사이즈 합</div>
        {sizes.map((sz, i) => (
          <div key={"st"+sz} className="inv-board-cell foot">{sizeTotals[i] || "·"}</div>
        ))}
        <div className="inv-board-cell foot tot grand">{bike.stock}</div>
      </div>
    </div>
  );
}

// ─── NEW PRODUCT MODAL (size-specific SKU, optional sizes) ─
// v3.16: 카테고리별 사이즈+신장 프리셋 (회의록 v3.16 §2.3)
const HEIGHT_PRESETS = {
  road: [
    { size:"49", h1:152, h2:160 }, { size:"52", h1:159, h2:168 },
    { size:"54", h1:166, h2:175 }, { size:"56", h1:173, h2:182 },
    { size:"58", h1:180, h2:188 }, { size:"61", h1:186, h2:194 },
  ],
  gravel: [
    { size:"XS", h1:150, h2:162 }, { size:"S", h1:160, h2:172 },
    { size:"M",  h1:170, h2:182 }, { size:"L",  h1:180, h2:192 },
  ],
  mtb: [
    { size:"S", h1:155, h2:170 }, { size:"M", h1:168, h2:180 },
    { size:"L", h1:178, h2:190 },
  ],
  ebike: [
    { size:"S", h1:160, h2:172 }, { size:"M", h1:170, h2:182 },
    { size:"L", h1:180, h2:192 },
  ],
  kids: [
    { size:"20″", h1:110, h2:130 },
    { size:"24″", h1:125, h2:145 },
    { size:"26″", h1:140, h2:160 },
  ],
};

function NewProductModal({ onClose, onCreate }) {
  const [form, setForm] = useState({
    name: "", series: "", tier: "Comp", cat: "road", year: 2026,
    priceRetail: "", priceShop: "", weightKg: "",
    frame: "", wheels: "", brake: "디스크 · 유압", group: "",
    hasSizes: true,
    // v3.16: variants = [{ size, sku, h1, h2, A, B, C, W }]
    variants: [],
    singleSku: "", singleStock: { A:0, B:0, C:0, W:0 },
    colors: ["#1B1B1F"], image: "",
  });
  const [imgPreview, setImgPreview] = useState("");
  const [sizeInput, setSizeInput] = useState("");

  useEffect(() => {
    const k = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", k);
    return () => window.removeEventListener("keydown", k);
  }, []);

  const update = (patch) => setForm({...form, ...patch});

  const addSize = (sz) => {
    const s = (sz || sizeInput).trim();
    if (!s || form.variants.find(v => v.size === s)) return;
    setForm({...form, variants: [...form.variants, { size: s, sku: "", h1: "", h2: "", A:0, B:0, C:0, W:0 }]});
    setSizeInput("");
  };
  const removeVariant = (sz) => {
    setForm({...form, variants: form.variants.filter(v => v.size !== sz)});
  };
  const updateVariant = (sz, patch) => {
    setForm({...form, variants: form.variants.map(v => v.size === sz ? { ...v, ...patch } : v)});
  };

  const applyPreset = (sizes) => {
    const existing = new Set(form.variants.map(v => v.size));
    const merged = [...form.variants];
    sizes.forEach(s => {
      if (!existing.has(s)) merged.push({ size: s, sku: "", h1: "", h2: "", A:0, B:0, C:0, W:0 });
    });
    update({ variants: merged });
  };

  // v3.16: 카테고리별 신장 프리셋 한 번에 적용 (사이즈 + 신장 자동 채움)
  const applyHeightPreset = (catKey) => {
    const preset = HEIGHT_PRESETS[catKey];
    if (!preset) return;
    if (form.variants.length > 0 && !confirm("기존 사이즈 행을 모두 교체합니다. 진행하시겠습니까?")) return;
    update({
      variants: preset.map(p => ({ size: p.size, sku: "", h1: p.h1, h2: p.h2, A:0, B:0, C:0, W:0 })),
      cat: catKey,
    });
  };

  const setColor = (i, hex) => {
    const c = [...form.colors]; c[i] = hex; setForm({...form, colors: c});
  };

  const onImageFile = (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (ev) => { setImgPreview(ev.target.result); update({ image: ev.target.result }); };
    reader.readAsDataURL(file);
  };

  // ─ Validation
  const validSizesAll = form.hasSizes
    ? form.variants.length > 0 && form.variants.every(v => v.sku.trim())
    : form.singleSku.trim().length > 0;
  const canCreate = form.name.trim() && validSizesAll && Number(form.priceShop) > 0;

  const handleCreate = () => {
    if (!canCreate) return;
    let totalStock = 0;
    const matrix = {};
    const skuMap = {};
    let mainSku = "";

    if (form.hasSizes) {
      form.variants.forEach(v => {
        matrix[v.size] = { A: v.A||0, B: v.B||0, C: v.C||0, W: v.W||0 };
        totalStock += (v.A||0) + (v.B||0) + (v.C||0) + (v.W||0);
        skuMap[v.size] = v.sku.trim().toUpperCase();
      });
      mainSku = form.variants[0]?.sku.trim().toUpperCase() || "";
    } else {
      const sz = "ONE";
      matrix[sz] = { A: form.singleStock.A||0, B: form.singleStock.B||0, C: form.singleStock.C||0, W: form.singleStock.W||0 };
      totalStock = matrix[sz].A + matrix[sz].B + matrix[sz].C + matrix[sz].W;
      skuMap[sz] = form.singleSku.trim().toUpperCase();
      mainSku = form.singleSku.trim().toUpperCase();
    }

    const locSums = LOC_KEYS.map(k => ({ k, n: Object.values(matrix).reduce((a, m) => a + (m[k]||0), 0) }));
    locSums.sort((a,b) => b.n - a.n);
    const today = new Date();
    const todayDateStr = `${today.getFullYear()}.${String(today.getMonth()+1).padStart(2,"0")}.${String(today.getDate()).padStart(2,"0")}`;
    const fallbackGradient = `linear-gradient(155deg, ${form.colors[0] || "#1B1B1F"} 0%, ${form.colors[1] || form.colors[0] || "#37383C"} 100%)`;

    onCreate({
      sku: mainSku,
      name: form.name.trim(),
      series: form.series.trim() || form.name.split(" ")[0] || "",
      tier: form.tier, cat: form.cat,
      eyebrow: `${CAT_LABEL[form.cat]} · 신상`,
      group: form.group.trim() || "—",
      priceRetail: Number(form.priceRetail) || Number(form.priceShop),
      priceShop: Number(form.priceShop),
      weightKg: Number(form.weightKg) || 0,
      frame: form.frame.trim() || "—", wheels: form.wheels.trim() || "—",
      brake: form.brake.trim() || "—",
      status: "신상", statusTone: "blue", stock: totalStock,
      sizes: form.hasSizes ? form.variants.map(v => v.size) : ["ONE"],
      skuMap,
      colors: form.colors,
      gradient: fallbackGradient, image: form.image || null,
      sales: "신규 등록 모델.",
      location: locSums[0]?.n > 0 ? locSums[0].k : "A",
      lastReceived: todayDateStr,
      lowThreshold: 2, supplier: "Specialized KR",
      year: Number(form.year) || 2026, stockMatrix: matrix,
      // v3.16: 사이즈별 신장 매핑 ({size: {h1, h2}})
      sizeHeightMap: form.hasSizes
        ? form.variants.reduce((acc, v) => {
            if (v.h1 || v.h2) acc[v.size] = { h1: Number(v.h1) || null, h2: Number(v.h2) || null };
            return acc;
          }, {})
        : {},
    });
  };

  const grandTotal = form.hasSizes
    ? form.variants.reduce((a, v) => a + (v.A||0)+(v.B||0)+(v.C||0)+(v.W||0), 0)
    : (form.singleStock.A||0)+(form.singleStock.B||0)+(form.singleStock.C||0)+(form.singleStock.W||0);

  // ─ v3.14: 다른 모달 패턴(white BG + radius 16 + sticky CTA)에 맞춘 통일 디자인 ───
  const SX = {
    scrim: { position:"fixed", inset:0, background:"rgba(0,0,0,0.45)", zIndex:9999, display:"flex", alignItems:"center", justifyContent:"center", padding:16 },
    modal: { background:"#fff", borderRadius:16, maxWidth:720, width:"100%", maxHeight:"calc(100dvh - 32px)", display:"flex", flexDirection:"column", boxShadow:"0 20px 60px rgba(0,0,0,0.2)" },
    head: { padding:"20px 24px 12px", borderBottom:"1px solid var(--grey-100,#F2F4F6)" },
    headTopRow: { display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom:6 },
    h2: { margin:0, font:"700 20px/1.3 var(--font-sans)", letterSpacing:"-0.01em" },
    closeBtn: { background:"none", border:"none", fontSize:22, cursor:"pointer", color:"var(--grey-500,#8B95A1)", padding:0, lineHeight:1 },
    sub: { margin:0, fontSize:13, color:"var(--grey-600,#6B7684)", lineHeight:1.5 },
    statusChip: (ok) => ({
      display:"inline-flex", alignItems:"center", gap:4, padding:"3px 9px", borderRadius:14,
      fontSize:11, fontWeight:700, marginTop:8,
      background: ok ? "var(--green-50,#E5F8EE)" : "var(--red-50,#FFE5E6)",
      color: ok ? "#0E8F5F" : "var(--red-500,#E31E26)",
    }),
    body: { flex:1, overflowY:"auto", padding:"16px 24px" },
    section: { background:"var(--grey-50,#F9FAFB)", border:"1px solid var(--grey-100,#F2F4F6)", borderRadius:12, padding:"16px 18px", marginBottom:12 },
    sectionH: { display:"flex", alignItems:"center", gap:8, marginBottom:12, fontSize:13, fontWeight:700, color:"var(--grey-700,#4E5968)" },
    sectionStep: { width:20, height:20, borderRadius:10, background:"var(--grey-900,#191F28)", color:"#fff", fontSize:11, fontWeight:700, display:"flex", alignItems:"center", justifyContent:"center" },
    grid3: { display:"grid", gridTemplateColumns:"repeat(3, 1fr)", gap:10 },
    grid2: { display:"grid", gridTemplateColumns:"1fr 1fr", gap:10 },
    field: { display:"flex", flexDirection:"column", gap:4 },
    fieldLabel: { fontSize:12, fontWeight:500, color:"var(--grey-700,#4E5968)" },
    fieldRequired: { color:"var(--red-500,#E31E26)", marginLeft:3 },
    fieldInput: { padding:"9px 11px", border:"1px solid var(--grey-300,#D1D6DB)", borderRadius:8, fontSize:13, background:"#fff", width:"100%", boxSizing:"border-box" },
    fieldHint: { fontSize:11, color:"var(--grey-500,#8B95A1)" },
    catChips: { display:"flex", gap:6, flexWrap:"wrap" },
    catChip: (on) => ({
      padding:"7px 14px", border:"1px solid "+(on?"var(--red-500,#E31E26)":"var(--grey-300,#D1D6DB)"),
      borderRadius:8, background: on?"var(--red-50,#FFE5E6)":"#fff",
      color: on?"var(--red-700,#9A0E14)":"var(--grey-700,#4E5968)",
      fontSize:13, fontWeight: on?700:500, cursor:"pointer", transition:"all .15s",
    }),
    modeRow: { display:"grid", gridTemplateColumns:"1fr 1fr", gap:8, marginBottom:12 },
    modeTile: (on) => ({
      display:"flex", flexDirection:"column", alignItems:"flex-start", gap:2, padding:"12px 14px",
      border:"2px solid "+(on?"var(--blue-500,#0064FF)":"var(--grey-200,#E5E8EB)"),
      borderRadius:10, background: on?"var(--blue-50,#E8F3FF)":"#fff",
      cursor:"pointer", textAlign:"left",
    }),
    modeTileB: { fontSize:13, fontWeight:700, color:"var(--grey-900,#191F28)" },
    modeTileSmall: { fontSize:11, color:"var(--grey-600,#6B7684)" },
    presetRow: { display:"flex", gap:6, flexWrap:"wrap", alignItems:"center", marginBottom:10 },
    presetChip: { padding:"6px 10px", border:"1px solid var(--grey-300,#D1D6DB)", borderRadius:6, background:"#fff", cursor:"pointer", fontSize:12, color:"var(--grey-700,#4E5968)" },
    sizeInputRow: { display:"flex", gap:6, marginBottom:10, maxWidth:340 },
    actions: { display:"flex", alignItems:"center", gap:8, padding:"14px 24px", borderTop:"1px solid var(--grey-200,#E5E8EB)", background:"#fff", borderBottomLeftRadius:16, borderBottomRightRadius:16 },
    actionsLeft: { flex:1, fontSize:12, color:"var(--grey-600,#6B7684)" },
    btn: { padding:"10px 18px", border:"1px solid var(--grey-300,#D1D6DB)", borderRadius:8, background:"#fff", cursor:"pointer", fontSize:13, fontWeight:500 },
    btnPrimary: (disabled) => ({
      padding:"10px 20px", border:"none", borderRadius:8,
      background: disabled ? "var(--grey-300,#D1D6DB)" : "var(--red-500,#E31E26)",
      color:"#fff", cursor: disabled?"not-allowed":"pointer",
      fontSize:13, fontWeight:600, opacity: disabled?0.7:1,
    }),
  };

  return (
    <div style={SX.scrim} onClick={onClose}>
      <div style={SX.modal} role="dialog" aria-label="신상 등록" onClick={e=>e.stopPropagation()}>

        {/* ── 헤더 ── */}
        <div style={SX.head}>
          <div style={SX.headTopRow}>
            <h2 style={SX.h2}>🆕 신상 등록</h2>
            <button style={SX.closeBtn} onClick={onClose} aria-label="닫기">×</button>
          </div>
          <p style={SX.sub}>모델 정보 · 가격 · 사이즈별 SKU · 위치별 초기 재고 · 사진을 한 번에 등록합니다.</p>
          <div style={SX.statusChip(canCreate)}>
            {canCreate
              ? <>✓ 등록 준비됨 · {form.hasSizes ? form.variants.length + "개 사이즈" : "1개"} · 총 {grandTotal}대</>
              : <>⚠️ 모델명 · 매장가 · {form.hasSizes ? "사이즈별 SKU" : "SKU"} 입력 필요</>
            }
          </div>
        </div>

        {/* ── 본문 (스크롤) ── */}
        <div style={SX.body}>

          {/* ① 기본 정보 */}
          <section style={SX.section}>
            <div style={SX.sectionH}>
              <span style={SX.sectionStep}>1</span>
              <span>기본 정보</span>
            </div>
            <div style={{...SX.field, marginBottom:10}}>
              <label style={SX.fieldLabel}>모델명<span style={SX.fieldRequired}>*</span></label>
              <input style={SX.fieldInput} type="text" value={form.name}
                onChange={(e)=>update({name:e.target.value})}
                placeholder="예: Tarmac SL8 Expert" />
              {!form.name.trim() && <span style={{...SX.fieldHint, color:"var(--red-500,#E31E26)"}}>모델명을 입력해주세요</span>}
            </div>
            <div style={{...SX.grid3, marginBottom:10}}>
              <div style={SX.field}>
                <label style={SX.fieldLabel}>년식</label>
                <input style={SX.fieldInput} type="number" value={form.year} onChange={(e)=>update({year:e.target.value})} />
              </div>
              <div style={SX.field}>
                <label style={SX.fieldLabel}>시리즈</label>
                <input style={SX.fieldInput} type="text" value={form.series} onChange={(e)=>update({series:e.target.value})} placeholder="Tarmac" />
              </div>
              <div style={SX.field}>
                <label style={SX.fieldLabel}>등급</label>
                <select style={SX.fieldInput} value={form.tier} onChange={(e)=>update({tier:e.target.value})}>
                  <option>S-Works</option><option>Pro</option><option>Expert</option>
                  <option>Comp</option><option>Sport</option><option>Sprint</option><option>E5</option>
                </select>
              </div>
            </div>
            <div style={SX.field}>
              <label style={SX.fieldLabel}>카테고리</label>
              <div style={SX.catChips}>
                {Object.entries(CAT_LABEL).map(([k, v]) => (
                  <button key={k} type="button" style={SX.catChip(form.cat===k)} onClick={()=>update({cat:k})}>{v}</button>
                ))}
              </div>
            </div>
          </section>

          {/* ② 가격 · 스펙 */}
          <section style={SX.section}>
            <div style={SX.sectionH}>
              <span style={SX.sectionStep}>2</span>
              <span>가격 · 스펙</span>
            </div>
            <div style={{...SX.grid3, marginBottom:10}}>
              <div style={SX.field}>
                <label style={SX.fieldLabel}>매장가 (₩)<span style={SX.fieldRequired}>*</span></label>
                <input style={SX.fieldInput} type="number" value={form.priceShop}
                  onChange={(e)=>update({priceShop:e.target.value})} placeholder="11200000" />
                {!Number(form.priceShop) > 0 && form.priceShop && <span style={{...SX.fieldHint, color:"var(--red-500,#E31E26)"}}>0보다 큰 금액 입력</span>}
              </div>
              <div style={SX.field}>
                <label style={SX.fieldLabel}>권장 소비자가 (₩)</label>
                <input style={SX.fieldInput} type="number" value={form.priceRetail}
                  onChange={(e)=>update({priceRetail:e.target.value})} placeholder="11800000" />
              </div>
              <div style={SX.field}>
                <label style={SX.fieldLabel}>무게 (kg)</label>
                <input style={SX.fieldInput} type="number" step="0.1" value={form.weightKg}
                  onChange={(e)=>update({weightKg:e.target.value})} placeholder="7.4" />
              </div>
            </div>
            <div style={SX.grid3}>
              <div style={SX.field}>
                <label style={SX.fieldLabel}>프레임</label>
                <input style={SX.fieldInput} type="text" value={form.frame}
                  onChange={(e)=>update({frame:e.target.value})} placeholder="FACT 10r Carbon" />
              </div>
              <div style={SX.field}>
                <label style={SX.fieldLabel}>휠셋</label>
                <input style={SX.fieldInput} type="text" value={form.wheels}
                  onChange={(e)=>update({wheels:e.target.value})} placeholder="Roval Rapide CL II" />
              </div>
              <div style={SX.field}>
                <label style={SX.fieldLabel}>구동계</label>
                <input style={SX.fieldInput} type="text" value={form.group}
                  onChange={(e)=>update({group:e.target.value})} placeholder="Shimano Ultegra Di2" />
              </div>
            </div>
          </section>

          {/* ③ SKU · 사이즈 · 재고 */}
          <section style={SX.section}>
            <div style={SX.sectionH}>
              <span style={SX.sectionStep}>3</span>
              <span>SKU · 사이즈 · 초기 재고</span>
            </div>
            <div style={SX.modeRow}>
              <button type="button" style={SX.modeTile(form.hasSizes)} onClick={()=>update({hasSizes:true})}>
                <span style={SX.modeTileB}>📐 사이즈별 SKU</span>
                <span style={SX.modeTileSmall}>자전거 · 의류처럼 사이즈마다 다른 SKU</span>
              </button>
              <button type="button" style={SX.modeTile(!form.hasSizes)} onClick={()=>update({hasSizes:false})}>
                <span style={SX.modeTileB}>📦 단일 SKU</span>
                <span style={SX.modeTileSmall}>액세서리 · 부품처럼 사이즈 구분 없음</span>
              </button>
            </div>

            {form.hasSizes ? (
              <React.Fragment>
                <div style={SX.presetRow}>
                  <span style={{fontSize:12, color:"var(--grey-600,#6B7684)", marginRight:4}}>빠른 추가:</span>
                  {[
                    ["로드","49,52,54,56,58"],
                    ["MTB","S,M,L"],
                    ["트레일","S1,S2,S3,S4,S5"],
                    ["키즈","20″,24″"],
                  ].map(([label, sizes]) => (
                    <button key={label} type="button" style={SX.presetChip} onClick={()=>applyPreset(sizes.split(","))}>
                      <b>{label}</b> <small style={{color:"var(--grey-500,#8B95A1)",marginLeft:4}}>{sizes}</small>
                    </button>
                  ))}
                </div>

                {/* v3.16: 신장 프리셋 빠른 적용 */}
                <div style={{...SX.presetRow, padding:"8px 10px", background:"var(--blue-50,#E8F3FF)", borderRadius:8, marginTop:8}}>
                  <span style={{fontSize:12, fontWeight:600, color:"var(--blue-700,#0049BD)", marginRight:4}}>📏 신장 자동 추천</span>
                  {[
                    ["로드",  "road",  "49~61 / 152~194cm"],
                    ["그래블","gravel","XS~L / 150~192cm"],
                    ["MTB",   "mtb",   "S~L / 155~190cm"],
                    ["E-bike","ebike", "S~L / 160~192cm"],
                    ["키즈",  "kids",  "20-26″ / 110~160cm"],
                  ].map(([label, key, hint]) => (
                    <button key={key} type="button" style={{...SX.presetChip, borderColor:"var(--blue-300,#B0D2FF)", background:"#fff"}} onClick={()=>applyHeightPreset(key)} title="사이즈 + 신장 범위 한 번에 입력">
                      <b>{label}</b> <small style={{color:"var(--grey-500,#8B95A1)",marginLeft:4}}>{hint}</small>
                    </button>
                  ))}
                </div>

                <div style={SX.sizeInputRow}>
                  <input
                    style={SX.fieldInput}
                    type="text"
                    value={sizeInput}
                    onChange={(e)=>setSizeInput(e.target.value)}
                    onKeyDown={(e)=>{ if (e.key === "Enter") { e.preventDefault(); addSize(); }}}
                    placeholder="사이즈 직접 입력 후 Enter (예: 60)"
                  />
                  <button type="button" style={SX.btn} onClick={()=>addSize()}>＋</button>
                </div>

                {form.variants.length > 0 ? (
                  <div style={{marginTop:12, overflowX:"auto"}}>
                    <table style={{width:"100%", borderCollapse:"separate", borderSpacing:0, fontSize:12}}>
                      <thead>
                        <tr style={{background:"var(--grey-50,#F9FAFB)", color:"var(--grey-700,#4E5968)"}}>
                          <th style={{padding:"8px 6px", textAlign:"center", fontWeight:600, width:48}}>사이즈</th>
                          <th style={{padding:"8px 6px", textAlign:"left", fontWeight:600}}>SKU <span style={{color:"var(--red-500)"}}>*</span></th>
                          <th style={{padding:"8px 6px", textAlign:"center", fontWeight:600, width:130, background:"var(--blue-50,#E8F3FF)", color:"var(--blue-700,#0049BD)"}}>📏 추천 신장 (cm)</th>
                          {LOC_KEYS.map(k => <th key={k} style={{padding:"8px 4px", textAlign:"center", fontWeight:600, width:60}}>{LOC_LABEL[k]}<br/><small style={{fontWeight:400, color:"var(--grey-500)"}}>{k}</small></th>)}
                          <th style={{padding:"8px 6px", textAlign:"center", fontWeight:600, width:56}}>소계</th>
                          <th style={{width:30}}></th>
                        </tr>
                      </thead>
                      <tbody>
                        {form.variants.map(v => {
                          const subTot = (v.A||0)+(v.B||0)+(v.C||0)+(v.W||0);
                          return (
                            <tr key={v.size} style={{borderBottom:"1px solid var(--grey-100,#F2F4F6)"}}>
                              <td style={{padding:"6px", textAlign:"center", fontWeight:700, color:"var(--grey-900,#191F28)"}}>{v.size}</td>
                              <td style={{padding:"6px"}}>
                                <input type="text" value={v.sku} placeholder={`TSL8-EXP-${v.size}`}
                                  onChange={(e)=>updateVariant(v.size, { sku: e.target.value.toUpperCase() })}
                                  style={{width:"100%", padding:"5px 8px", border:"1px solid var(--grey-300,#D1D6DB)", borderRadius:6, fontSize:11, fontFamily:"monospace"}} />
                              </td>
                              <td style={{padding:"6px", background:"var(--blue-50,#E8F3FF)"}}>
                                <div style={{display:"flex", alignItems:"center", gap:3, justifyContent:"center"}}>
                                  <input type="number" min="80" max="230" value={v.h1 || ""} placeholder="-"
                                    onChange={(e)=>updateVariant(v.size, { h1: e.target.value ? Math.max(80, Math.min(230, Number(e.target.value))) : "" })}
                                    style={{width:48, padding:"4px 4px", border:"1px solid var(--blue-300,#B0D2FF)", borderRadius:4, fontSize:11, textAlign:"center"}} />
                                  <span style={{color:"var(--grey-500)", fontSize:10}}>~</span>
                                  <input type="number" min="80" max="230" value={v.h2 || ""} placeholder="-"
                                    onChange={(e)=>updateVariant(v.size, { h2: e.target.value ? Math.max(80, Math.min(230, Number(e.target.value))) : "" })}
                                    style={{width:48, padding:"4px 4px", border:"1px solid var(--blue-300,#B0D2FF)", borderRadius:4, fontSize:11, textAlign:"center"}} />
                                </div>
                                {v.h1 && v.h2 && Number(v.h2) < Number(v.h1) && (
                                  <div style={{fontSize:9, color:"var(--red-500)", textAlign:"center", marginTop:2}}>min&gt;max ⚠</div>
                                )}
                              </td>
                              {LOC_KEYS.map(k => (
                                <td key={k} style={{padding:"6px 4px"}}>
                                  <input type="number" min="0" value={v[k] || 0}
                                    onChange={(e)=>updateVariant(v.size, { [k]: Math.max(0, Number(e.target.value) || 0) })}
                                    style={{width:"100%", padding:"5px 6px", border:"1px solid var(--grey-300,#D1D6DB)", borderRadius:6, fontSize:11, textAlign:"center"}} />
                                </td>
                              ))}
                              <td style={{padding:"6px", textAlign:"center", fontWeight:700, color: subTot > 0 ? "var(--green-500,#0E8F5F)" : "var(--grey-400,#B0B8C1)"}}>{subTot}</td>
                              <td style={{padding:"6px", textAlign:"center"}}>
                                <button type="button" onClick={()=>removeVariant(v.size)}
                                  style={{background:"none", border:"none", color:"var(--grey-400)", cursor:"pointer", fontSize:16}}>×</button>
                              </td>
                            </tr>
                          );
                        })}
                      </tbody>
                      <tfoot>
                        <tr><td colSpan={3+LOC_KEYS.length+2} style={{padding:"8px 6px", fontSize:11, color:"var(--grey-600,#6B7684)", textAlign:"right"}}>
                          총 {form.variants.length}개 사이즈 · {grandTotal}대 · 📏 신장은 비워둬도 등록 가능 (카탈로그에서 "신장 정보 없음" 표시)
                        </td></tr>
                      </tfoot>
                    </table>
                  </div>
                ) : (
                  <div style={{padding:"20px",textAlign:"center",background:"#fff",border:"1px dashed var(--grey-300,#D1D6DB)",borderRadius:8,color:"var(--grey-500,#8B95A1)",fontSize:12}}>
                    위 빠른 추가 버튼을 클릭하거나 사이즈를 직접 입력해주세요
                  </div>
                )}
              </React.Fragment>
            ) : (
              <div>
                <div style={{...SX.field, maxWidth:340, marginBottom:14}}>
                  <label style={SX.fieldLabel}>SKU<span style={SX.fieldRequired}>*</span></label>
                  <input style={SX.fieldInput} type="text" value={form.singleSku}
                    onChange={(e)=>update({singleSku:e.target.value.toUpperCase()})}
                    placeholder="예: ACC-BOTTLE-24" />
                </div>
                <div style={SX.fieldLabel}>위치별 초기 재고</div>
                <div style={{display:"grid", gridTemplateColumns:"repeat(5, 1fr)", gap:8, marginTop:8}}>
                  {LOC_KEYS.map(k => (
                    <div key={k} style={{background:"#fff", padding:"8px 10px", borderRadius:8, border:"1px solid var(--grey-200,#E5E8EB)"}}>
                      <div style={{fontSize:11, color:"var(--grey-600,#6B7684)"}}>{LOC_LABEL[k]} <em>· {k}</em></div>
                      <input style={{...SX.fieldInput, padding:"4px 6px", marginTop:4}}
                        type="number" min="0" value={form.singleStock[k] || 0}
                        onChange={(e)=>update({singleStock: {...form.singleStock, [k]: Math.max(0, Number(e.target.value) || 0)}})} />
                    </div>
                  ))}
                  <div style={{background:"var(--grey-900,#191F28)", color:"#fff", padding:"8px 10px", borderRadius:8, textAlign:"center", display:"flex", flexDirection:"column", justifyContent:"center"}}>
                    <small style={{fontSize:10, opacity:0.7}}>총</small>
                    <b style={{fontSize:18}}>{grandTotal}<em style={{fontSize:11, opacity:0.7, marginLeft:2}}>대</em></b>
                  </div>
                </div>
              </div>
            )}
          </section>

          {/* ④ 사진 + 컬러 */}
          <section style={SX.section}>
            <div style={SX.sectionH}>
              <span style={SX.sectionStep}>4</span>
              <span>사진 · 컬러 (선택)</span>
            </div>
            <div style={SX.grid2}>
              <div style={SX.field}>
                <label style={SX.fieldLabel}>제품 사진</label>
                <label style={{
                  display:"flex", flexDirection:"column", alignItems:"center", justifyContent:"center",
                  height:140, border:"1px dashed var(--grey-300,#D1D6DB)", borderRadius:8,
                  background:"#fff", cursor:"pointer", color:"var(--grey-500,#8B95A1)", fontSize:12, gap:4,
                  overflow:"hidden",
                }}>
                  {imgPreview
                    ? <img src={imgPreview} alt="" style={{width:"100%",height:"100%",objectFit:"cover"}} />
                    : <React.Fragment>
                        <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
                        <span>클릭해서 사진 업로드</span>
                        <small style={{fontSize:11, color:"var(--grey-400,#B0B8C1)"}}>JPG · PNG · 최대 5MB</small>
                      </React.Fragment>}
                  <input type="file" accept="image/*" onChange={onImageFile} style={{display:"none"}} />
                </label>
                {imgPreview && (
                  <button type="button" style={{...SX.btn, marginTop:6, fontSize:12}}
                    onClick={()=>{setImgPreview(""); update({image:""});}}>사진 제거</button>
                )}
              </div>
              <div style={SX.field}>
                <label style={SX.fieldLabel}>컬러</label>
                <div style={{display:"flex", flexDirection:"column", gap:6}}>
                  {form.colors.map((c, i) => (
                    <div key={i} style={{display:"flex", alignItems:"center", gap:6, background:"#fff", padding:6, borderRadius:8, border:"1px solid var(--grey-200,#E5E8EB)"}}>
                      <div style={{width:32, height:32, borderRadius:6, background:c, flexShrink:0}} />
                      <input type="color" value={c} onChange={(e)=>setColor(i, e.target.value)}
                        style={{width:32, height:32, padding:0, border:"none", background:"transparent", cursor:"pointer"}} />
                      <input type="text" value={c} onChange={(e)=>setColor(i, e.target.value)}
                        style={{...SX.fieldInput, flex:1, fontFamily:"monospace", fontSize:12}} />
                      {form.colors.length > 1 && (
                        <button type="button" style={{...SX.btn, padding:"4px 8px", fontSize:14}}
                          onClick={()=>setForm({...form, colors: form.colors.filter((_,j)=>j!==i)})}>×</button>
                      )}
                    </div>
                  ))}
                  <button type="button" style={{...SX.btn, fontSize:12}}
                    onClick={()=>setForm({...form, colors:[...form.colors,"#888888"]})}>＋ 컬러 추가</button>
                </div>
              </div>
            </div>
          </section>

        </div>

        {/* ── 하단 sticky CTA ── */}
        <div style={SX.actions}>
          <div style={SX.actionsLeft}>
            {canCreate
              ? <span>* 저장 시 카탈로그 · 재고 라인에 즉시 반영</span>
              : <span style={{color:"var(--red-500,#E31E26)"}}>* 필수 항목을 먼저 입력해주세요</span>
            }
          </div>
          <button style={SX.btn} onClick={onClose}>취소</button>
          <button style={SX.btnPrimary(!canCreate)} disabled={!canCreate} onClick={handleCreate}>
            🆕 등록
          </button>
        </div>

      </div>
    </div>
  );
}

const COLS = [
  { id: "sku",          label: "년식",                 w: 76, mono: true },
  { id: "name",         label: "모델",                 w: 240 },
  { id: "cat",          label: "카테고리",             w: 76 },
  { id: "stock",        label: "총재고",               w: 96,  num: true },
  { id: "locA",         label: "쇼룸 1F (A)",          w: 160, sortable: false },
  { id: "locB",         label: "쇼룸 2F (B)",          w: 160, sortable: false },
  { id: "locC",         label: "쇼룸 3F (C)",          w: 160, sortable: false },
  { id: "locW",         label: "창고 (W)",             w: 160, sortable: false },
  { id: "priceShop",    label: "매장가",               w: 124, num: true },
  { id: "lastReceived", label: "최근 입고",            w: 100, center: true },
  { id: "actions",      label: "재고 이동",            w: 196, center: true, sortable: false },
];

function InventoryView({ bikes, sortBy, setSortBy, onAction, view, setView, onCreate }) {
  return (
    <div className="inv-tbl-wrap">
      <div className="inv-tbl-head">
        <div className="inv-tbl-h">
          재고 라인 · {bikes.length}건
          <small style={{marginLeft:8, fontWeight:500, color:"var(--fg-tertiary)", fontSize:11, letterSpacing:0, textTransform:"none"}}>
            위치별 분포 · 사이즈 · 매장가 · 액션을 한 화면에
          </small>
        </div>
        <div className="inv-tbl-actions">
          <div className="inv-board-view-toggle">
            <button className={view === "table" ? "on" : ""} onClick={()=>setView("table")} title="표 보기">
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
              표
            </button>
            <button className={view === "card" ? "on" : ""} onClick={()=>setView("card")} title="카드 보기">
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>
              카드
            </button>
          </div>
          <button className="inv-tbl-action primary" onClick={onCreate}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
            신상 등록
          </button>
          <button className="inv-tbl-action">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
            CSV 내보내기
          </button>
        </div>
      </div>

      {/* A5: 빈 검색 결과 EmptyState */}
      {bikes.length === 0
        ? (
          <div style={{padding:"64px 24px",textAlign:"center",background:"var(--grey-50,#F9FAFB)",borderRadius:12}}>
            <div style={{fontSize:48,marginBottom:12}}>📦</div>
            <h3 style={{margin:"0 0 8px",font:"700 22px/1.3 var(--font-sans)"}}>
              {(window.CATALOG?.bikes||[]).length === 0 ? "등록된 모델이 없어요" : "검색 결과가 없어요"}
            </h3>
            <p style={{margin:"0 0 20px",color:"var(--grey-600)",fontSize:14}}>
              {(window.CATALOG?.bikes||[]).length === 0
                ? "첫 신상을 등록하면 이곳에 표시됩니다."
                : "다른 검색어나 필터를 시도해보세요."}
            </p>
            <button className="cat-btn cat-btn-primary" onClick={onCreate}
              style={{padding:"10px 20px"}}>
              + 신상 등록
            </button>
          </div>
        )
        : (view === "table"
            ? <InventoryTable bikes={bikes} sortBy={sortBy} setSortBy={setSortBy} onAction={onAction} />
            : <InventoryCardGrid bikes={bikes} onAction={onAction} />)
      }
    </div>
  );
}

function InventoryTable({ bikes, sortBy, setSortBy, onAction }) {
  const toggleSort = (key) => {
    setSortBy(s => s.key === key ? { key, dir: s.dir === "asc" ? "desc" : "asc" } : { key, dir: "asc" });
  };

  return (
    <table className="inv-tbl inv-tbl-merged">
      <thead>
        <tr>
          {COLS.map(c => (
            <th
              key={c.id}
              style={{ width: c.w }}
              className={(c.num ? "num " : c.center ? "center " : "") + (sortBy.key === c.id ? "sorted" : "")
                + (c.id === "locA" ? " loc-A" : c.id === "locB" ? " loc-B" : c.id === "locW" ? " loc-W" : "")}
              onClick={c.sortable === false ? undefined : () => toggleSort(c.id)}
            >
              {c.label}
              {c.sortable !== false && <span style={{marginLeft:4, opacity:.5}}>
                {sortBy.key === c.id ? (sortBy.dir === "asc" ? "▲" : "▼") : "↕"}
              </span>}
            </th>
          ))}
        </tr>
      </thead>
      <tbody>
        {bikes.map(b => {
          const isOut = b.stock === 0;
          const isLow = b.stock > 0 && b.stock <= b.lowThreshold;
          return (
            <tr key={b.sku} className={isOut ? "row-out" : isLow ? "row-low" : ""}>
              <td className="mono">
                <span className="inv-tbl-year">{b.year}</span>
              </td>
              <td>
                <div className="inv-tbl-name">
                  {b.image ? (
                    <img className="inv-tbl-sw inv-tbl-photo" src={b.image} alt="" />
                  ) : (
                    <div className="inv-tbl-sw" style={{ background: b.gradient }} />
                  )}
                  <div>
                    <b>{b.name}</b>
                    <small>{b.tier} · {b.group}</small>
                  </div>
                </div>
              </td>
              <td><span className={"cat-tbl-cat " + b.cat}>{CAT_LABEL[b.cat]}</span></td>
              <td className="num">
                <span className={"inv-tbl-tot " + (isOut ? "zero" : isLow ? "low" : "")}>
                  {b.stock}<em>대</em>
                </span>
              </td>
              {LOC_KEYS.map(loc => {
                const sizes = b.sizes
                  .map(sz => ({ sz, n: b.stockMatrix?.[sz]?.[loc] || 0 }))
                  .filter(x => x.n > 0);
                const total = sizes.reduce((a, x) => a + x.n, 0);
                return (
                  <td key={loc} className={"inv-tbl-loccol loc-" + loc + (total === 0 ? " empty" : "")}>
                    {total === 0 ? <span className="inv-boardt-dash">—</span> : (
                      <div className="inv-boardt-chips">
                        {sizes.map(({ sz, n }) => (
                          <span key={sz} className={"inv-boardt-chip loc-" + loc}>
                            <span className="inv-boardt-chip-line">
                              <em>{sz}</em>
                              <b>{n}</b>
                            </span>
                            <i className="inv-boardt-sku">{b.sku}-{sz}</i>
                          </span>
                        ))}
                      </div>
                    )}
                  </td>
                );
              })}
              <td className="num cat-tbl-price">
                <b>{fmtKRW(b.priceShop)}</b>
                <small style={{display:"block",fontSize:10,color:"var(--fg-tertiary)",marginTop:4}}>평가 {fmtKRW(b.priceShop * b.stock)}</small>
              </td>
              <td className="center" style={{color:"var(--fg-tertiary)", fontSize:12}}>{b.lastReceived}</td>
              <td className="center">
                <div className="inv-row-actions">
                  <button className="inv-row-btn in"   onClick={() => onAction(b, "in")}   title="입고">
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4"><line x1="12" y1="5" x2="12" y2="19"/><polyline points="19 12 12 19 5 12"/></svg>
                    입고
                  </button>
                  <button className="inv-row-btn out"  onClick={() => onAction(b, "out")}  title="출고" disabled={b.stock === 0}>
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4"><line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/></svg>
                    출고
                  </button>
                  <button className="inv-row-btn move" onClick={() => onAction(b, "move")} title="위치 변경">
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg>
                  </button>
                </div>
              </td>
            </tr>
          );
        })}
        {bikes.length === 0 && (
          <tr><td colSpan={COLS.length}><div className="inv-empty">조건에 맞는 재고가 없습니다.</div></td></tr>
        )}
      </tbody>
    </table>
  );
}

// ─── CARD GRID 보기 ───────────────────────────────────────
function InventoryCardGrid({ bikes, onAction }) {
  return (
    <div className="inv-cardgrid">
      {bikes.map(b => {
        const isOut = b.stock === 0;
        const isLow = b.stock > 0 && b.stock <= b.lowThreshold;
        return (
          <div key={b.sku} className={"inv-card " + (isOut ? "out " : isLow ? "low " : "")}>
            <div className="inv-card-head">
              {b.image ? (
                <img className="inv-card-sw inv-card-photo" src={b.image} alt="" />
              ) : (
                <div className="inv-card-sw" style={{background: b.gradient}} />
              )}
              <div className="inv-card-meta">
                <div className="inv-card-year">
                  <span className="inv-tbl-year">{b.year}</span>
                  <span className={"cat-tbl-cat " + b.cat} style={{padding:"3px 8px",fontSize:9,marginLeft:6}}>{CAT_LABEL[b.cat]}</span>
                </div>
                <b>{b.name}</b>
                <small>{b.sku} · {b.tier}</small>
              </div>
              <div className="inv-card-tot">
                <b>{b.stock}</b><small>대</small>
              </div>
            </div>
            <div className="inv-card-locs">
              {LOC_KEYS.map(loc => {
                const sizes = b.sizes
                  .map(sz => ({ sz, n: b.stockMatrix?.[sz]?.[loc] || 0 }))
                  .filter(x => x.n > 0);
                const total = sizes.reduce((a, x) => a + x.n, 0);
                return (
                  <div key={loc} className={"inv-card-loc loc-" + loc + (total === 0 ? " empty" : "")}>
                    <div className="inv-card-loc-h">
                      <b>{LOC_LABEL[loc]}</b>
                      <em>{total}대</em>
                    </div>
                    <div className="inv-card-loc-chips">
                      {total === 0 ? <span className="inv-boardt-dash">—</span> : sizes.map(({sz, n}) => {
                        // v3.16: 신장 정보 (있으면 툴팁)
                        const h = b.sizeHeightMap?.[sz] || (Array.isArray(b.size_height_map) ? b.size_height_map.find(x => x.size === sz) : null);
                        const hTitle = h && (h.h1 || h.height_min_cm) ? ` · 신장 ${h.h1 || h.height_min_cm}~${h.h2 || h.height_max_cm}cm` : "";
                        return (
                          <span key={sz} className={"inv-boardt-chip loc-" + loc} title={`${b.sku}-${sz}${hTitle}`}>
                            <span className="inv-boardt-chip-line">
                              <em>{sz}</em>
                              <b>{n}</b>
                            </span>
                            <i className="inv-boardt-sku">{b.sku}-{sz}</i>
                          </span>
                        );
                      })}
                    </div>
                  </div>
                );
              })}
            </div>
            <div className="inv-card-foot">
              <div className="inv-card-price">
                <small>매장가</small>
                <b>{fmtKRW(b.priceShop)}</b>
              </div>
              <div className="inv-card-recv">
                <small>최근 입고</small>
                <b>{b.lastReceived}</b>
              </div>
              <div className="inv-row-actions">
                <button className="inv-row-btn in"   onClick={() => onAction(b, "in")}>
                  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4"><line x1="12" y1="5" x2="12" y2="19"/><polyline points="19 12 12 19 5 12"/></svg>
                  입고
                </button>
                <button className="inv-row-btn out"  onClick={() => onAction(b, "out")} disabled={b.stock === 0}>
                  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4"><line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/></svg>
                  출고
                </button>
                <button className="inv-row-btn move" onClick={() => onAction(b, "move")} title="위치 이동">
                  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg>
                </button>
              </div>
            </div>
          </div>
        );
      })}
      {bikes.length === 0 && <div className="inv-empty" style={{gridColumn:"1 / -1"}}>조건에 맞는 재고가 없습니다.</div>}
    </div>
  );
}

// ─── FOOTER ──────────────────────────────────────────────
function Footer() {
  return (
    <footer className="cat-footer">
      <div className="cat-inner cat-footer-inner">
        <div className="cat-footer-brand">
          <img className="cat-footer-mark" src="assets/symbol-brush-sejong.svg" alt="" />
          <div>
            <div className="cat-footer-name">재고관리 · 김성호바이크 세종점</div>
            <div className="cat-footer-sub">매주 월요일 본사 시스템 동기화 · 수동 입출고는 즉시 반영</div>
          </div>
        </div>
        <div className="cat-footer-meta">
          <span>위치코드: A 쇼룸 1F · B 쇼룸 2F · W 창고</span>
          <img className="cat-footer-wordmark" src="assets/wordmark-specialized.svg" alt="Specialized" />
        </div>
      </div>
    </footer>
  );
}

// ─── APP ─────────────────────────────────────────────────
function App() {
  // 재고 데이터 → React state (변경 가능)
  const [bikes, setBikes] = useState(() => CAT.bikes.map(b => ({...b})));
  const [moves, setMoves] = useState([]); // 이번 세션 이동 로그

  // Modal 상태 — bike==null이면 SKU 검색부터
  const [modal, setModal] = useState(null);
  const [newProductOpen, setNewProductOpen] = useState(false); // { bike?, action }

  const [q, setQ] = useState("");
  const [cat, setCat] = useState("all");
  const [loc, setLoc] = useState("all");
  const [status, setStatus] = useState("all");
  const [sortBy, setSortBy] = useState({ key: "cat", dir: "asc" });
  const [view, setView] = useState("table"); // table | card

  const filtered = useMemo(() => {
    const qs = q.trim().toLowerCase();
    let r = bikes.filter(b =>
      (cat === "all" || b.cat === cat) &&
      (loc === "all" || b.location === loc) &&
      (status === "all" ||
        (status === "ok"  && b.stock > b.lowThreshold) ||
        (status === "low" && b.stock > 0 && b.stock <= b.lowThreshold) ||
        (status === "out" && b.stock === 0)) &&
      (!qs ||
        b.name.toLowerCase().includes(qs) ||
        b.sku.toLowerCase().includes(qs) ||
        b.group.toLowerCase().includes(qs) ||
        b.series.toLowerCase().includes(qs))
    );
    const dir = sortBy.dir === "asc" ? 1 : -1;
    r = [...r].sort((a, b) => {
      let av, bv;
      if (sortBy.key === "value") { av = a.priceShop * a.stock; bv = b.priceShop * b.stock; }
      else if (sortBy.key === "sizes") { av = a.sizes.length; bv = b.sizes.length; }
      else { av = a[sortBy.key]; bv = b[sortBy.key]; }
      if (typeof av === "number") return (av - bv) * dir;
      return String(av).localeCompare(String(bv), "ko") * dir;
    });
    return r;
  }, [bikes, q, cat, loc, status, sortBy]);

  const handleAction = (bike, action) => {
    setModal({ bike, action });
  };
  const handleQuick = (action) => {
    setModal({ bike: null, action });
  };

  const handleApply = async (bike, op) => {
    // v3.12 Phase B1: Supabase RPC 실호출 (운영 모드에서만)
    if (window.DAL?.isOnline && bike.id) {
      try {
        const type = op.kind === "입고" ? "IN" : op.kind === "출고" ? "OUT" : "MOVE";
        await window.DAL.moveStock({
          bike_id: bike.id,
          size: op.size || (bike.sizes && bike.sizes[0]) || "M",
          qty: op.qty,
          type,
          from_location: op.from || (type === "OUT" || type === "MOVE" ? bike.location : null),
          to_location: op.toLoc || op.to || null,
          note: op.note || null,
        });
        if (window.SECURITY) window.SECURITY.showToast(`${op.kind} 완료 — Supabase 반영됨`, "success", 2000);
      } catch (e) {
        console.error("[B1] moveStock 실패:", e);
        if (window.SECURITY) window.SECURITY.showToast(`${op.kind} 실패: ${e.message}`, "error", 4000);
        else alert(`${op.kind} 실패: ${e.message}`);
        return;
      }
    }

    // 함수형 업데이트 — 배치 호출 시 누적되도록
    const sizeNote = op.size ? `${op.size} · ` : "";
    const base = { sku: bike.sku, name: bike.name, size: op.size, date: todayStr(), time: nowTime(), note: op.note };

    setBikes(prev => {
      const current = prev.find(b => b.sku === bike.sku) || bike;
      if (op.kind === "입고") {
        return prev.map(b => b.sku === bike.sku
          ? { ...b,
              stock: b.stock + op.qty,
              lastReceived: todayStr(),
              location: op.toLoc || b.location,
            }
          : b);
      } else if (op.kind === "출고") {
        return prev.map(b => b.sku === bike.sku
          ? { ...b, stock: Math.max(0, b.stock - op.qty) }
          : b);
      } else {
        return prev.map(b => b.sku === bike.sku ? { ...b, location: op.to } : b);
      }
    });

    setMoves(prev => {
      const current = bikes.find(b => b.sku === bike.sku) || bike;
      let move;
    if (op.kind === "입고") {
      const newLoc = op.toLoc || current.location;
      const locChanged = newLoc !== current.location;
      move = { ...base, kind: "입고", kindTone: "in",
        detail: `${sizeNote}+${op.qty}대 · ${current.stock} → ${current.stock + op.qty}대 · ${locChanged ? `${LOC_LABEL[current.location]} → ${LOC_LABEL[newLoc]} (${newLoc})` : `${LOC_LABEL[newLoc]} (${newLoc})`}` };
      } else if (op.kind === "출고") {
        move = { ...base, kind: "출고", kindTone: "out",
          detail: `${sizeNote}−${op.qty}대 · ${current.stock} → ${Math.max(0, current.stock - op.qty)}대 · ${LOC_LABEL[current.location]} (${current.location})에서` };
      } else {
        move = { ...base, kind: "이동", kindTone: "move",
          detail: `${sizeNote}${LOC_LABEL[op.from]} (${op.from}) → ${LOC_LABEL[op.to]} (${op.to}) · ${current.stock}대` };
      }
      return [move, ...prev];
    });

    setModal(null);
  };

  // Modal에 표시할 bike (선택된 경우)
  const modalBike = modal && modal.bike ? bikes.find(b => b.sku === modal.bike.sku) : null;

  return (
    <div className="inv-app">
      <InvHeader />
      <PageNav active="inventory" />
      <main className="inv-main">
        <div className="cat-inner">
          <div className="inv-page-head">
            <div>
              <h1 className="inv-page-h">재고 현황</h1>
              <p className="inv-page-sub" style={{marginTop:8}}>
                {CAT.meta.asOf} 기준 · 입고 / 출고 / 위치 이동을 각 라인에서 바로 처리.
              </p>
            </div>
          </div>
          {/* A1: 메인 KPI 큰 카드 + 자금 흐름 통합 (KpiStrip 대체) */}
          <InventoryHero bikes={bikes} moves={moves} />

          {/* A2: 입고 예정 패널 */}
          <PendingReceipts />

          {/* A4: 일일 마감 점검 */}
          <DailyClosing moves={moves} />

          <LowAlert bikes={bikes} />
          <QuickActions onOpen={handleQuick} onCreate={()=>setNewProductOpen(true)} />
          <RecentMovements moves={moves} />
          <Toolbar
            q={q} setQ={setQ}
            cat={cat} setCat={setCat}
            loc={loc} setLoc={setLoc}
            status={status} setStatus={setStatus}
          />
          <InventoryView bikes={filtered} sortBy={sortBy} setSortBy={setSortBy} onAction={handleAction} view={view} setView={setView} onCreate={()=>setNewProductOpen(true)} />
        </div>
      </main>
      <Footer />

      {modal && (
        <MovementModal
          bike={modalBike}
          allBikes={bikes}
          initialTab={modal.action}
          onClose={() => setModal(null)}
          onApply={handleApply}
        />
      )}

      {newProductOpen && (
        <NewProductModal
          onClose={() => setNewProductOpen(false)}
          onCreate={async (newBike) => {
            // v3.12 Phase B3: Supabase RPC 신상 등록 (운영 모드)
            if (window.DAL?.isOnline) {
              try {
                const baseSku = newBike.sku?.split("-").slice(0, -1).join("-") || newBike.sku;
                const initialStock = newBike.sizes.map(sz => ({
                  size: sz, location: newBike.location || "A", qty: 1
                }));
                const { data, error } = await window.DAL.client.rpc("create_bike_with_variants", {
                  p_base_sku:     baseSku,
                  p_name:         newBike.name,
                  p_series:       newBike.series || "기타",
                  p_tier:         newBike.tier   || "기본",
                  p_category:     newBike.cat    || "road",
                  p_price_retail: newBike.priceRetail || newBike.priceShop,
                  p_price_shop:   newBike.priceShop,
                  p_year:         newBike.year || 2026,
                  p_sizes:        newBike.sizes,
                  p_colors:       newBike.colors || [],
                  p_initial_stock: initialStock,
                });
                if (error) throw error;
                if (data?.bike_id) newBike.id = data.bike_id;

                // B3b: 이미지가 있으면 Storage 업로드
                if (newBike.imageFile && window.STORAGE?.registerBikeMedia) {
                  await window.STORAGE.registerBikeMedia(data.bike_id, baseSku, newBike.imageFile);
                }

                if (window.SECURITY) window.SECURITY.showToast("신상 등록 완료 — Supabase 반영됨", "success", 2500);
              } catch (e) {
                console.error("[B3] 신상 등록 실패:", e);
                if (window.SECURITY) window.SECURITY.showToast(`신상 등록 실패: ${e.message}`, "error", 4000);
                else alert(`신상 등록 실패: ${e.message}`);
                return;
              }
            }

            setBikes([newBike, ...bikes]);
            setMoves(m => [{
              sku: newBike.sku, name: newBike.name,
              date: todayStr(), time: nowTime(), note: "신규 등록",
              kind: "등록", kindTone: "in",
              detail: `신상 등록 · ${newBike.sizes.length}개 사이즈 · 초기 ${newBike.stock}대`,
            }, ...m]);
            setNewProductOpen(false);
          }}
        />
      )}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
