function CatalogDrawer({ open, onClose, onAdd, offerLines = [] }) {
  const [q, setQ] = React.useState('');
  const [skrivet, setSkrivet] = React.useState('');
  const [openCats, setOpenCats] = React.useState({});
  const [antal, setAntal] = React.useState({});     // sku -> antal att lägga till
  const [senast, setSenast] = React.useState(null); // kvittens: vad som just lades till
  const sokRef = React.useRef(null);

  // Sökningen renderade tidigare om hela träfflistan vid varje tangenttryck,
  // utan tak och utan fördröjning. Med en Pyramid-katalog på flera tusen
  // artiklar hackade varje bokstav.
  React.useEffect(() => {
    const t = setTimeout(() => setQ(skrivet), 140);
    return () => clearTimeout(t);
  }, [skrivet]);

  React.useEffect(() => {
    if (open) setTimeout(() => { if (sokRef.current) sokRef.current.focus(); }, 30);
    else { setSenast(null); setAntal({}); }
  }, [open]);

  if (!open) return null;

  const MAX_TRAFFAR = 300;
  const toggleCat = (cat) => setOpenCats(o => ({ ...o, [cat]: !o[cat] }));
  // Vad som redan ligger på offerten — så man slipper lägga till samma sak två
  // gånger utan att märka det.
  const paOfferten = {};
  for (const l of offerLines) if (l.sku) paOfferten[l.sku] = (paOfferten[l.sku] || 0) + (Number(l.qty) || 0);

  const lagg = (cat, item) => {
    const n = Number(antal[item.sku]) > 0 ? Number(antal[item.sku]) : 1;
    onAdd(cat, item, n);
    setSenast({ namn: item.name, antal: n, enhet: item.unit });
    setAntal(a => ({ ...a, [item.sku]: '' }));
  };

  return (
    <div onMouseDown={e => { if (e.target === e.currentTarget) onClose(); }} style={{
      position:'fixed', inset:0, background:'rgba(0,54,95,.32)', zIndex:50,
      display:'flex', justifyContent:'flex-end',
    }}>
      <div role="dialog" aria-modal="true" aria-label="Tjänstekatalog" style={{
        width:560, maxWidth:'100%', height:'100%', background:'#fff', display:'flex', flexDirection:'column',
        boxShadow:'-12px 0 48px rgba(0,54,95,.16)',
      }}>
        <div style={{padding:'18px 22px', borderBottom:'1px solid var(--border)', display:'flex', justifyContent:'space-between', alignItems:'center'}}>
          <div>
            <div style={{fontSize:11, fontWeight:600, letterSpacing:'.16em', textTransform:'uppercase', color:'var(--fg-muted)'}}>Tjänstekatalog</div>
            <h3 style={{margin:'4px 0 0', fontFamily:'var(--font-serif)', fontWeight:500, fontSize:22, color:'var(--ulab-navy)'}}>Lägg till på offerten</h3>
          </div>
          <button onClick={onClose} aria-label="Stäng katalogen" style={{background:'transparent', border:'none', cursor:'pointer', color:'var(--ulab-navy)'}}><Icon name="close" size={20}/></button>
        </div>

        <div style={{padding:'14px 22px', borderBottom:'1px solid var(--border)'}}>
          <div style={{position:'relative'}}>
            <input ref={sokRef} value={skrivet} onChange={e => setSkrivet(e.target.value)}
              aria-label="Sök i katalogen" placeholder="Sök tjänst eller artikel..." style={{ ...inp, paddingLeft:36 }}/>
            <div style={{position:'absolute', left:12, top:11, color:'var(--fg-muted)'}}><Icon name="search" size={16} stroke="currentColor"/></div>
          </div>
        </div>

        {/* Kvittens. Lådan täcker offerten, så tidigare hände ingenting synligt
            alls när man klickade på en artikel — man fick stänga lådan för att
            se om den kommit med. */}
        {senast && (
          <div role="status" style={{
            padding:'10px 22px', background:'#eef7f4', borderBottom:'1px solid #9fcfc1',
            fontSize:12.5, color:'#2b7d70', fontFamily:'var(--font-sans)',
          }}>
            Lade till <b>{senast.antal} {senast.enhet}</b> {senast.namn} på offerten.
          </div>
        )}

        <div style={{flex:1, overflow:'auto', padding:'8px 0'}}>
          {CATALOG.map(g => {
            const alla = g.items.filter(i => !q || (i.name + i.sku).toLowerCase().includes(q.toLowerCase()));
            if (q && alla.length === 0) return null;
            const matches = alla.slice(0, MAX_TRAFFAR);
            const expanded = !!q || !!openCats[g.cat];
            return (
            <div key={g.cat}>
              <button onClick={() => toggleCat(g.cat)} disabled={!!q} style={{
                width:'100%', background:'transparent', border:'none', cursor: q ? 'default' : 'pointer',
                padding:'14px 22px 6px', display:'flex', alignItems:'center', gap:10, fontSize:11, fontWeight:600,
                letterSpacing:'.14em', textTransform:'uppercase', color:'var(--ulab-blue)', textAlign:'left',
              }}>
                <Icon name={g.icon} size={14} stroke="currentColor"/> {g.cat}
                <span style={{color:'var(--fg-muted)', fontWeight:500, letterSpacing:'normal', textTransform:'none'}}>({alla.length})</span>
                {!q && <span style={{marginLeft:'auto', color:'var(--fg-muted)', display:'inline-flex', transform: expanded ? 'rotate(90deg)' : 'none', transition:'transform 150ms'}}><Icon name="chevron" size={12} stroke="currentColor" sw={2}/></span>}
              </button>
              {expanded && matches.map(i => (
                <div key={i.sku} style={{
                  display:'grid', gridTemplateColumns:'1fr auto 72px auto',
                  gap:12, padding:'10px 22px', alignItems:'center',
                  borderTop:'1px solid var(--border)',
                }}
                onMouseEnter={e => e.currentTarget.style.background='var(--ulab-cream)'}
                onMouseLeave={e => e.currentTarget.style.background='transparent'}
                >
                  <div>
                    <div style={{fontSize:14, fontWeight:600, color:'var(--ulab-navy)'}}>{i.name}</div>
                    <div style={{fontSize:12, color:'var(--fg-muted)', marginTop:2}}>
                      {i.sku} · {i.unit}
                      {paOfferten[i.sku] ? <span style={{color:'var(--ulab-blue)', fontWeight:600}}> · {paOfferten[i.sku]} på offerten</span> : ''}
                    </div>
                  </div>
                  <div style={{fontSize:14, fontWeight:600, color:'var(--ulab-navy)', fontVariantNumeric:'tabular-nums'}}>{fmtKr(i.price)}</div>
                  {/* Antalet fylldes tidigare alltid i som 1 och fick rättas nere
                      i redigeraren efteråt, när lådan väl var stängd. */}
                  <input type="text" inputMode="decimal" value={antal[i.sku] ?? ''} placeholder="1"
                    aria-label={`Antal av ${i.name}`}
                    onChange={e => { const v = e.target.value; if (/^[\d\s.,]*$/.test(v)) setAntal(a => ({ ...a, [i.sku]: v })); }}
                    onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); lagg(g.cat, i); } }}
                    style={{
                      width:'100%', boxSizing:'border-box', fontFamily:'var(--font-sans)', fontSize:13,
                      padding:'7px 8px', textAlign:'right', border:'1px solid var(--border-strong)',
                      borderRadius:4, color:'var(--ulab-navy)',
                    }}/>
                  <button onClick={() => lagg(g.cat, i)} aria-label={`Lägg till ${i.name}`} title="Lägg till på offerten" style={{
                    background:'transparent', border:'1px solid var(--border-strong)', borderRadius:4,
                    padding:'6px 9px', cursor:'pointer', color:'var(--ulab-blue)', display:'flex', alignItems:'center',
                  }}><Icon name="plus" size={16}/></button>
                </div>
              ))}
              {expanded && alla.length > matches.length && (
                <div style={{padding:'10px 22px', fontSize:12, color:'var(--fg-muted)', borderTop:'1px solid var(--border)'}}>
                  Visar {matches.length} av {alla.length} träffar — förfina sökningen för att se fler.
                </div>
              )}
            </div>
            );
          })}
        </div>

        <div style={{padding:'12px 22px', borderTop:'1px solid var(--border)', fontSize:11.5, color:'var(--fg-muted)'}}>
          Skriv ett antal och tryck Enter för att lägga till direkt. Esc stänger katalogen.
        </div>
      </div>
    </div>
  );
}
window.CatalogDrawer = CatalogDrawer;
