// Left filter rail + Rule Builder drawer (shared criteria for alerts & saved searches)

const filterStyles = `
  .rail {
    width: var(--rail-w);
    border-right: 1px solid var(--line);
    background: var(--surface-2);
    height: calc(100vh - var(--header-h));
    position: sticky; top: var(--header-h);
    display: flex; flex-direction: column;
    flex: none;
  }
  .rail-head { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px 10px; }
  .rail-head h2 {
    margin: 0; font-size: 11.5px; font-weight: 600;
    letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-2);
    display: flex; align-items: center; gap: 6px;
  }
  .rail-head h2 .dot { width: 5px; height: 5px; border-radius: 50%; background: var(--accent); }
  .rail-body { flex: 1; overflow-y: auto; padding: 4px 16px 80px; }
  .rail-foot {
    border-top: 1px solid var(--line); padding: 10px 14px;
    display: flex; gap: 8px; background: var(--surface-2);
    position: sticky; bottom: 0;
  }

  .filt-group { padding: 10px 0; border-bottom: 1px dashed var(--line); }
  .filt-group:last-child { border-bottom: 0; }
  .filt-group > .ghead {
    display: flex; align-items: center; justify-content: space-between;
    margin-bottom: 10px; cursor: pointer; user-select: none;
  }
  .filt-group > .ghead h4 {
    margin: 0; font-size: 10.5px; font-weight: 600;
    letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
  }
  .filt-group > .ghead .count { font-family: var(--font-mono); font-size: 10.5px; color: var(--faint); }
  .filt-group > .gbody { display: flex; flex-direction: column; gap: 12px; }

  .checks { display: flex; flex-wrap: wrap; gap: 4px; }
  .chkpill {
    display: inline-flex; align-items: center; height: 26px; padding: 0 10px;
    border-radius: var(--r-sm); border: 1px solid var(--line); background: var(--surface);
    font-size: 12px; color: var(--ink-2); cursor: pointer;
    font-family: var(--font-mono); font-weight: 500; transition: all .12s; text-transform: capitalize;
  }
  .chkpill:hover { border-color: var(--line-strong); }
  .chkpill.on { background: var(--ink); color: var(--bg); border-color: var(--ink); }
  .chkpill.gold.on { background: var(--accent); border-color: var(--accent-deep); color: #fff; }

  .row-flex { display: flex; align-items: center; justify-content: space-between; padding: 4px 0; gap: 10px; }
  .row-flex .rlabel { font-size: 13px; color: var(--ink-2); }
  .row-flex .rsub { font-size: 11px; color: var(--muted); display:block; margin-top: 1px; }

  .slider-wrap { position: relative; height: 26px; display: flex; align-items: center; }
  .slider-track { position: relative; height: 4px; background: var(--bg-sunken); border-radius: 2px; width: 100%; }
  .slider-fill { position: absolute; top: 0; bottom: 0; background: var(--accent); border-radius: 2px; }
  .slider-thumb {
    position: absolute; top: 50%; transform: translate(-50%, -50%);
    width: 14px; height: 14px; background: var(--surface);
    border: 2px solid var(--accent); border-radius: 50%; box-shadow: 0 1px 3px rgba(0,0,0,0.12);
  }
  .scale-lbls { display: flex; justify-content: space-between; font-size: 10px; color: var(--faint); font-family: var(--font-mono); }

  /* live stream control card */
  .live-card {
    margin: 12px 16px 0; padding: 12px 13px;
    border: 1px solid var(--line); border-radius: var(--r);
    background: var(--surface); position: relative; overflow: hidden;
  }
  .live-card.on { background: linear-gradient(180deg, var(--live-soft), var(--surface) 140%); border-color: rgba(47,143,94,0.3); }
  [data-theme="dim"] .live-card.on { background: linear-gradient(180deg, rgba(47,143,94,0.12), var(--surface) 140%); }
  .live-card.locked { background: var(--surface); }
  .live-card .lc-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
  .live-card .lc-title {
    display: flex; align-items: center; gap: 8px; font-size: 11.5px; font-weight: 600;
    letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-2);
  }
  .live-card .lc-title .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--faint); }
  .live-card.on .lc-title .dot { background: var(--live); animation: live-pulse 1.8s ease-out infinite; }
  .live-card .lc-meta { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line); }
  .live-card .m-num { font-family: var(--font-mono); font-size: 14px; font-weight: 500; color: var(--ink); font-feature-settings: "tnum"; }
  .live-card .m-lbl { font-size: 9.5px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); margin-top: 1px; }
  .live-card .scope-sel { margin-top: 10px; }

  /* ===== Rule builder drawer ===== */
  .rb-drawer {
    position: fixed; top: 0; right: 0; width: 580px; max-width: 96vw; height: 100vh;
    background: var(--bg); border-left: 1px solid var(--line); z-index: 90;
    display: flex; flex-direction: column;
    box-shadow: var(--shadow-pop); transform: none;
  }
  .rb-head { display: flex; align-items: flex-start; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid var(--line); }
  .rb-head h2 { margin: 0; font-family: var(--font-serif); font-size: 23px; letter-spacing: -0.01em; }
  .rb-head .sub { font-size: 12px; color: var(--muted); margin-top: 3px; max-width: 360px; line-height: 1.4; }
  .rb-name-row { padding: 14px 20px; border-bottom: 1px solid var(--line); background: var(--surface-2); display: flex; gap: 12px; align-items: center; }
  .rb-body { flex: 1; overflow-y: auto; padding: 4px 20px 40px; }
  .rb-foot { padding: 12px 20px; border-top: 1px solid var(--line); display: flex; justify-content: space-between; align-items: center; background: var(--surface-2); gap: 10px; }

  .rb-sect { padding: 16px 0; border-bottom: 1px solid var(--line); }
  .rb-sect:last-child { border-bottom: 0; }
  .rb-sect > h3 {
    margin: 0 0 14px; font-size: 11px; font-weight: 600;
    letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
    display: flex; align-items: center; gap: 8px;
  }
  .rb-sect > h3 .n { width: 18px; height: 18px; border-radius: 50%; background: var(--bg-sunken); color: var(--ink-2); font-family: var(--font-mono); font-size: 10px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--line); }
  .rb-sect > h3::after { content: ""; flex: 1; height: 1px; background: var(--line); }
  .rb-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px 16px; }
  .rb-field .lbl-row { display: flex; align-items: baseline; justify-content: space-between; }
  .rb-field .lbl-row .val { font-family: var(--font-mono); font-size: 12px; color: var(--accent-deep); font-weight: 600; }
  .rb-toggle-row { display: grid; grid-template-columns: 1fr auto; align-items: center; padding: 9px 0; gap: 12px; }
  .rb-toggle-row + .rb-toggle-row { border-top: 1px dashed var(--line); }
  .rb-toggle-row .rlabel { font-size: 13px; color: var(--ink-2); }
  .rb-toggle-row .rsub { font-size: 11px; color: var(--muted); margin-top: 1px; }

  /* test preview */
  .rb-test { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
  .rb-test-head { padding: 11px 13px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--line); background: var(--surface-2); }
  .rb-test-head .t { font-size: 12px; color: var(--ink-2); }
  .rb-test-head b { font-family: var(--font-mono); color: var(--accent-deep); }
  .rb-test-item { display: grid; grid-template-columns: 1fr auto auto; gap: 10px; align-items: center; padding: 9px 13px; border-bottom: 1px dashed var(--line); }
  .rb-test-item:last-child { border-bottom: 0; }
  .rb-test-item .ti-t { font-size: 12px; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .rb-test-item .ti-m { font-family: var(--font-mono); font-size: 10.5px; color: var(--muted); margin-top: 2px; }
  .rb-test-item .ti-r { font-family: var(--font-mono); font-size: 13px; font-weight: 600; }
  .rb-test-item .ti-x { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: 50%; }
  .rb-test-item .ti-x.yes { background: var(--live-soft); color: var(--live); }
  .rb-test-item .ti-x.no { background: var(--bg-sunken); color: var(--muted-2); }

  .chip-strip { display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 16px; border-bottom: 1px solid var(--line); background: var(--surface-2); align-items: center; }
`;

if (!document.getElementById("filt-styles")) {
  const s = document.createElement("style");
  s.id = "filt-styles"; s.textContent = filterStyles;
  document.head.appendChild(s);
}

const FilterGroup = ({ title, count, children, defaultOpen = true }) => {
  const [open, setOpen] = React.useState(defaultOpen);
  return (
    <div className="filt-group">
      <div className="ghead" onClick={() => setOpen(!open)}>
        <h4>{title}</h4>
        <span className="count">{count != null ? `${count}` : ""} {open ? "−" : "+"}</span>
      </div>
      {open ? <div className="gbody">{children}</div> : null}
    </div>
  );
};

const ChkPill = ({ children, on, onClick, tone }) => (
  <button className={`chkpill ${on ? "on" : ""} ${tone || ""}`} onClick={onClick}>{children}</button>
);

const Slider = ({ fillStart = 0, fillEnd = 0.8, single }) => (
  <div className="slider-wrap">
    <div className="slider-track">
      <div className="slider-fill" style={{left: `${fillStart*100}%`, width: `${(fillEnd - fillStart)*100}%`}}/>
      {!single && <div className="slider-thumb" style={{left: `${fillStart*100}%`}}/>}
      <div className="slider-thumb" style={{left: `${fillEnd*100}%`}}/>
    </div>
  </div>
);

// ===== Live stream control (signed-in only) =====
const LiveCard = ({ on, setOn, lastMatch, signedIn, onSignIn }) => {
  if (!signedIn) {
    return (
      <div className="live-card locked">
        <div className="lc-top">
          <div className="lc-title"><span className="dot"/> Live stream</div>
          <I.bell size={14} style={{color:"var(--muted)"}}/>
        </div>
        <div style={{fontSize:12, color:"var(--muted)", lineHeight:1.45}}>
          New deals refresh automatically for everyone. <b style={{color:"var(--ink-2)"}}>Live push</b> returns after account features ship.
        </div>
        <button className="btn accent sm" style={{width:"100%", justifyContent:"center", marginTop:10}} onClick={onSignIn}>
          <I.bolt size={12}/> Coming soon
        </button>
      </div>
    );
  }
  return (
    <div className={`live-card ${on ? "on" : ""}`}>
      <div className="lc-top">
        <div className="lc-title"><span className="dot"/>{on ? "Live · streaming" : "Live · paused"}</div>
        <Toggle on={on} onChange={setOn} variant="live"/>
      </div>
      <div style={{fontSize:12, color: on ? "var(--ink-2)" : "var(--muted)", lineHeight:1.4}}>
        {on ? "Newly analyzed items matching your scope appear at the top in real time." : "Paused. Resume to receive new items via SSE as they complete analysis."}
      </div>
      <div className="scope-sel">
        <label className="lbl" style={{marginBottom:4}}>Scope stream to</label>
        <select className="select" style={{height:30}}>
          <option>Active filters</option>
          {SAVED_SEARCHES.map(s => <option key={s.id}>{s.name}</option>)}
          <option>Everything (unfiltered)</option>
        </select>
      </div>
      <div className="lc-meta">
        <div><div className="m-num">{on ? "23" : "—"}</div><div className="m-lbl">offers 1h</div></div>
        <div><div className="m-num">{on ? "1.2s" : "—"}</div><div className="m-lbl">lag</div></div>
        <div><div className="m-num">{on ? lastMatch : "—"}</div><div className="m-lbl">last</div></div>
      </div>
    </div>
  );
};

const FilterRail = ({
  liveOn,
  setLiveOn,
  onOpenRuleBuilder,
  lastMatch,
  filters,
  setFilters,
  signedIn,
  onComingSoon,
  searchTerm,
  onSearch,
  onClearSearch,
  onApplyFilters,
  onResetFilters,
}) => {
  const [keyword, setKeyword] = React.useState(searchTerm || "");
  React.useEffect(() => { setKeyword(searchTerm || ""); }, [searchTerm]);
  const toggleArr = (k, v) => {
    const cur = filters[k] || [];
    setFilters({...filters, [k]: cur.includes(v) ? cur.filter(x => x !== v) : [...cur, v]});
  };
  const submitKeyword = (event) => {
    event.preventDefault();
    onSearch?.(keyword);
  };
  const clearKeyword = () => {
    setKeyword("");
    onClearSearch?.();
  };
  return (
    <aside className="rail">
      <LiveCard on={false} setOn={setLiveOn} lastMatch={lastMatch} signedIn={false} onSignIn={() => onComingSoon?.("Live push", "Anonymous visitors stay fresh through periodic re-fetching. The auth-only SSE stream returns after the account rollout.")}/>
      <div className="rail-head">
        <h2><span className="dot"/> Filters</h2>
        <div style={{display:"flex", gap:"6px"}}>
          <button className="btn xs ghost" title="Reset" onClick={onResetFilters}><I.refresh size={11}/></button>
          <button className="btn xs ghost" title="Save as search" onClick={onOpenRuleBuilder}><I.save size={11}/></button>
        </div>
      </div>
      <div className="rail-body">
        <FilterGroup title="Keyword">
          <form style={{position:"relative"}} onSubmit={submitKeyword}>
            <I.search size={13} style={{position:"absolute", left:"9px", top:"50%", transform:"translateY(-50%)", color:"var(--muted)"}}/>
            <input
              className="input"
              placeholder="Title contains..."
              style={{paddingLeft:"28px", paddingRight: keyword ? "32px" : undefined}}
              value={keyword}
              onChange={event => setKeyword(event.target.value)}
              aria-label="Filter keyword"
            />
            {keyword ? (
              <button className="btn icon sm ghost search-clear" type="button" onClick={clearKeyword} title="Clear keyword" style={{position:"absolute", right:"5px", top:"50%", transform:"translateY(-50%)"}}>
                <I.x size={12}/>
              </button>
            ) : null}
          </form>
        </FilterGroup>

        <FilterGroup title="Gold purity (karat)">
          <div className="checks">
            {KARATS.map(k => (
              <ChkPill key={k} tone="gold" on={(filters.karat||[]).includes(k)} onClick={() => toggleArr("karat", k)}>{k}K</ChkPill>
            ))}
          </div>
        </FilterGroup>

        <FilterGroup title="Item type">
          <div className="checks">
            {ITEM_TYPES.map(t => (
              <ChkPill key={t} on={(filters.type||[]).includes(t)} onClick={() => toggleArr("type", t)}>{t}</ChkPill>
            ))}
          </div>
        </FilterGroup>

        <ComingSoon
          variant="inline"
          feature="Advanced filters"
          detail="Price, weight, confidence, and saved rule criteria are staged for the account-feature rollout. The public MVP keeps keyword, karat, and item-type filters live."
          onAction={() => onComingSoon?.("Advanced filters", "Price, weight, confidence, and saved rule criteria are staged for the account-feature rollout. The public MVP keeps keyword, karat, and item-type filters live.")}
          actionLabel="Coming soon"
        />

        <button className="btn" style={{width:"100%", justifyContent:"center", marginTop:"10px"}} onClick={onOpenRuleBuilder}>
          <I.sliders size={14}/> Open rule builder
        </button>
      </div>
      <div className="rail-foot">
        <button className="btn primary" style={{flex:1, justifyContent:"center"}} onClick={onApplyFilters}><I.check size={13}/> Apply</button>
        <button className="btn" onClick={onResetFilters}>Reset</button>
      </div>
    </aside>
  );
};

// Active filter summary strip
const FilterStrip = ({
  onOpenRuleBuilder,
  signedIn,
  filters = {},
  setFilters,
  searchTerm,
  onClearSearch,
  onClearFilters,
}) => {
  const karats = filters.karat || [];
  const types = filters.type || [];
  const hasActive = !!searchTerm || karats.length > 0 || types.length > 0;
  return (
  <div className="chip-strip">
    <span className="micro" style={{alignSelf:"center", marginRight:"4px"}}>Active</span>
    {searchTerm ? <Chip onRemove={onClearSearch}>&quot;{searchTerm}&quot;</Chip> : null}
    {karats.length ? (
      <Chip onRemove={() => setFilters?.(prev => ({...prev, karat: []}))} tone="var(--accent-tint)">
        {karats.map(k => `${k}K`).join(", ")}
      </Chip>
    ) : null}
    {types.length ? (
      <Chip onRemove={() => setFilters?.(prev => ({...prev, type: []}))}>
        {types.join(", ")}
      </Chip>
    ) : null}
    {!hasActive ? <span className="mono" style={{fontSize:11, color:"var(--muted)"}}>No active public filters</span> : null}
    <button className="btn xs ghost" style={{marginLeft:"auto"}} onClick={onOpenRuleBuilder}>
      <I.sliders size={11}/> Rule builder
    </button>
    {hasActive ? <button className="btn xs ghost" onClick={onClearFilters}>Clear all</button> : null}
    <button className="btn xs" onClick={onOpenRuleBuilder}><I.save size={11}/> Save</button>
    </div>
  );
};

// ===== Rule Builder (criteria shared by alerts + saved searches) =====
const RBSlider = ({ label, value, fill }) => (
  <div className="rb-field">
    <div className="lbl-row"><label className="lbl">{label}</label><span className="val">{value}</span></div>
    <Slider single fillEnd={fill}/>
  </div>
);

const RuleBuilder = ({ onClose, mode = "rule", signedIn }) => {
  const [tab, setTab] = React.useState("criteria"); // criteria | test
  const isRule = mode === "rule";
  const testItems = React.useMemo(() => SEED_ITEMS.slice(0, 6).map(it => ({
    ...it, matched: it.ratio != null && it.ratio <= 0.90 && (it.karat >= 14),
  })), []);
  const matchedCount = testItems.filter(t => t.matched).length;

  return (
    <>
      <div className="scrim" onClick={onClose}/>
      <div className="rb-drawer" role="dialog">
        <div className="rb-head">
          <div>
            <h2>{isRule ? "Alert rule" : "Advanced filter"}</h2>
            <div className="sub">{isRule
              ? "Get pinged the moment a new listing matches. Same criteria power saved searches."
              : "Precision criteria across every analyzed attribute. Save it as a one-tap search."}</div>
          </div>
          <div style={{display:"flex", gap:"8px"}}>
            {isRule && <div className="seg">
              <button className={tab==="criteria"?"active":""} onClick={()=>setTab("criteria")}>Criteria</button>
              <button className={tab==="test"?"active":""} onClick={()=>setTab("test")}>Test</button>
            </div>}
            <button className="btn icon sm ghost" onClick={onClose}><I.x size={14}/></button>
          </div>
        </div>

        {isRule && (
          <div className="rb-name-row">
            <div style={{flex:1}}>
              <input className="input" placeholder="Rule name — e.g. Underpriced 18K (≥10g)" defaultValue="Underpriced 18K (≥10g)"/>
            </div>
            <div className="row-flex" style={{gap:8, padding:0}}>
              <span style={{fontSize:12, color:"var(--muted)"}}>Enabled</span>
              <Toggle on={true} onChange={()=>{}} variant="accent"/>
            </div>
          </div>
        )}

        {(!isRule || tab === "criteria") && (
          <div className="rb-body">
            <div className="rb-sect">
              <h3><span className="n">1</span> Price &amp; value</h3>
              <div className="rb-grid">
                <RBSlider label="Max price-to-melt ratio" value="≤ 0.80×" fill={0.5}/>
                <RBSlider label="Min deal score" value="≥ 70" fill={0.7}/>
                <div className="rb-field"><label className="lbl">Max all-in price</label><input className="input mono" defaultValue="$3,000"/></div>
                <div className="rb-field"><label className="lbl">Min gold weight</label><input className="input mono" defaultValue="10.0 g"/></div>
              </div>
            </div>

            <div className="rb-sect">
              <h3><span className="n">2</span> Item</h3>
              <div style={{display:"flex", flexDirection:"column", gap:12}}>
                <div className="rb-field">
                  <label className="lbl">Item types</label>
                  <div className="checks">{ITEM_TYPES.slice(0,10).map((t,i) => <ChkPill key={t} on={i===2||i===5}>{t}</ChkPill>)}</div>
                </div>
                <div className="rb-field">
                  <label className="lbl">Karats</label>
                  <div className="checks">{KARATS.map((k,i) => <ChkPill key={k} tone="gold" on={k===18}>{k}K</ChkPill>)}</div>
                </div>
                <div className="rb-grid">
                  <div className="rb-field"><label className="lbl">Brand include <span style={{color:"var(--faint)", fontWeight:400}}>(filter only)</span></label><input className="input" placeholder="Tiffany, Cartier…"/></div>
                  <div className="rb-field"><label className="lbl">Brand exclude</label><input className="input" placeholder="costume, replica…"/></div>
                </div>
              </div>
            </div>

            <div className="rb-sect">
              <h3><span className="n">3</span> Material &amp; purity</h3>
              <div className="rb-field" style={{marginBottom:12}}>
                <label className="lbl">Primary metal</label>
                <div className="checks">{["gold","silver","platinum"].map(m => <ChkPill key={m} on={m==="gold"}>{m}</ChkPill>)}</div>
              </div>
              <div className="rb-toggle-row"><div><div className="rlabel">Require solid gold</div><div className="rsub">Exclude plated / filled / vermeil entirely</div></div><Toggle on={true} onChange={()=>{}} variant="accent"/></div>
              <div className="rb-toggle-row"><div><div className="rlabel">Allow gold-filled</div></div><Toggle on={false} onChange={()=>{}}/></div>
              <div className="rb-toggle-row"><div><div className="rlabel">Allow gold-plated</div></div><Toggle on={false} onChange={()=>{}}/></div>
              <div className="rb-toggle-row"><div><div className="rlabel">Allow vermeil</div></div><Toggle on={false} onChange={()=>{}}/></div>
            </div>

            <div className="rb-sect">
              <h3><span className="n">4</span> Quality &amp; risk</h3>
              <div className="rb-grid" style={{marginBottom:6}}>
                <div className="rb-field"><label className="lbl">Min confidence</label><div className="checks">{CONFIDENCE_LEVELS.map((c,i)=><ChkPill key={c} on={i<2}>{c}</ChkPill>)}</div></div>
                <div className="rb-field"><label className="lbl">Max listing age</label><input className="input mono" defaultValue="60 min"/></div>
              </div>
              <div className="rb-toggle-row"><div><div className="rlabel">Allow review-required pricing</div><div className="rsub">Include items flagged for human review</div></div><Toggle on={false} onChange={()=>{}}/></div>
              <div className="rb-toggle-row"><div><div className="rlabel">Require authenticity guarantee</div></div><Toggle on={false} onChange={()=>{}}/></div>
              <div className="rb-toggle-row"><div><div className="rlabel">Require jewelry</div><div className="rsub">Exclude scrap / coins / bullion</div></div><Toggle on={true} onChange={()=>{}}/></div>
              <div className="rb-toggle-row"><div><div className="rlabel">Best offer required</div></div><Toggle on={false} onChange={()=>{}}/></div>
            </div>

            <div className="rb-sect">
              <h3><span className="n">5</span> Seller</h3>
              <div className="rb-field"><label className="lbl">Exclude sellers</label><input className="input mono" placeholder="seller_a, seller_b…"/></div>
            </div>

            {isRule && (
              <div className="rb-sect">
                <h3><span className="n">6</span> Notify via</h3>
                <div style={{display:"flex", flexDirection:"column", gap:8}}>
                  {CHANNELS.map(c => (
                    <label key={c.id} className="rb-toggle-row" style={{padding:"7px 0", cursor: c.status==="coming_soon" ? "default":"pointer"}}>
                      <div style={{display:"flex", alignItems:"center", gap:10}}>
                        <span className="a-ch"><span className="ico">{c.type==="discord_webhook"?<I.hash size={11}/>:c.type==="email"?<I.mail size={11}/>:<I.bell size={11}/>}</span></span>
                        <div><div className="rlabel">{c.label}</div><div className="rsub">{c.type.replace("_"," ")}{c.status==="coming_soon"?" · coming soon":""}</div></div>
                      </div>
                      {c.status==="coming_soon" ? <Badge tone="outline">soon</Badge> : <Toggle on={c.enabled} onChange={()=>{}} variant="accent"/>}
                    </label>
                  ))}
                </div>
              </div>
            )}
          </div>
        )}

        {isRule && tab === "test" && (
          <div className="rb-body">
            <div className="rb-sect" style={{paddingTop:8}}>
              <div className="rb-test">
                <div className="rb-test-head">
                  <div className="t">Dry-run against recent listings — <b>{matchedCount}</b> of {testItems.length} would match</div>
                  <button className="btn xs"><I.refresh size={11}/> Re-run</button>
                </div>
                {testItems.map(t => (
                  <div key={t.id} className="rb-test-item">
                    <div style={{minWidth:0}}>
                      <div className="ti-t">{t.title}</div>
                      <div className="ti-m">{t.karat}K · {t.goldWeight ?? "?"}g · score {t.dealScore ?? "—"} · {t.matched ? "matched: ratio, karat, weight" : "missed: below thresholds"}</div>
                    </div>
                    <div className="ti-r" style={{color: t.ratio != null && t.ratio < 1 ? "var(--accent-deep)":"var(--ink-2)"}}>{t.ratio != null ? `${t.ratio.toFixed(2)}×` : "—"}</div>
                    <div className={`ti-x ${t.matched ? "yes":"no"}`}>{t.matched ? <I.check size={11}/> : <I.x size={11}/>}</div>
                  </div>
                ))}
              </div>
              <div style={{display:"flex", gap:8, marginTop:14, alignItems:"center"}}>
                <button className="btn"><I.refresh size={13}/> Rescan recent window</button>
                <span style={{fontSize:11, color:"var(--muted)"}}>Re-evaluates the rule against the last 24h and queues matches.</span>
              </div>
            </div>
          </div>
        )}

        <div className="rb-foot">
          <div className="mono" style={{fontSize:11, color:"var(--muted)"}}>{isRule ? "v4 · 11 criteria set" : "11 criteria active"}</div>
          <div style={{display:"flex", gap:"8px"}}>
            {isRule
              ? <>
                  <button className="btn"><I.bolt size={13}/> Test</button>
                  <button className="btn primary"><I.check size={13}/> Save rule</button>
                </>
              : <>
                  <button className="btn"><I.save size={13}/> Save as search</button>
                  <button className="btn primary"><I.check size={13}/> Apply filter</button>
                </>}
          </div>
        </div>
      </div>
    </>
  );
};

Object.assign(window, { FilterRail, FilterStrip, RuleBuilder, ChkPill, FilterGroup, Slider });
