// SONARA — Producer Opportunità (feed richieste open + filter sidebar Airbnb-style)
const OpIcons = window.SonaraIcons;

/* ICONS */
const ChevIc = ({ size = 12 }) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="9 18 15 12 9 6"/></svg>;
const StarIc = ({ size = 12, filled = true }) => <svg width={size} height={size} viewBox="0 0 24 24" fill={filled ? "currentColor" : "none"} stroke="currentColor" strokeWidth="1.5"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>;
const ClockIc = ({ size = 12 }) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>;
const PinIc = ({ size = 12 }) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>;
const PaperIc = ({ size = 12 }) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>;
const UsersIc = ({ size = 12 }) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg>;
const SearchIc = ({ size = 14 }) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>;
const PlayIc = ({ size = 11 }) => <svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor"><path d="M5 3l14 9-14 9V3z"/></svg>;
const PaperclipIc = ({ size = 12 }) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>;
const BoltIc = ({ size = 11 }) => <svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor"><path d="M13 2L4.09 12.97a.5.5 0 0 0 .38.82h6.03l-1.49 7.42a.5.5 0 0 0 .89.37l8.91-10.97A.5.5 0 0 0 18.42 10h-5.92l1.4-7.45A.5.5 0 0 0 13 2z"/></svg>;
const XIc = ({ size = 12 }) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>;
const BookmarkIc = ({ size = 13 }) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>;
const BellIc = ({ size = 12 }) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9M13.73 21a2 2 0 0 1-3.46 0"/></svg>;

const NICO_GENRES = ["Trap", "Drill", "Hip-hop", "R&B"];

const REQS = [
  {
    id: "REQ-9214", kind: "Beat custom",
    title: "Cerco beat trap 808 pesante stile Travis Scott late-night",
    brief: "Sto lavorando ad un singolo per uscita estate. Cerco un beat trap con vibe notturna, 808 spaziali, BPM 138-145, key minore. Ref: Travis Scott — \"After Hours\", Drake — \"Headlines\" (cassa più tirata).\n\nDeve essere originale, non type beat caricato online. Mi piacerebbe collaborare a lungo termine se il primo round va bene.",
    artist: "AlessioRap", artistFull: "Alessio Marchetti", artistInit: "AR", verified: true,
    city: "Milano", country: "IT", rating: 4.7, pastProjects: 3,
    budget: { min: 100, max: 200 }, deadlineDays: 10, offers: 4,
    posted: "2h fa", expiresIn: "28gg", postedHours: 2,
    matchTags: ["Trap", "BPM 138-145"], genres: ["Trap"], bpm: 142, mkey: "F# min",
    attachments: [{ name: "ref-travis.mp3", size: "3.2 MB" }, { name: "topline-rough.m4a", size: "1.8 MB" }],
    new: true, briefComplete: true,
  },
  {
    id: "REQ-9210", kind: "Mix + Master",
    title: "Mix EP 5 tracce drill — stems pronti",
    brief: "EP completo 5 tracce drill UK-influence, già recorded. Stems puliti (vox + 808 + drums + melodie). Cerco mix radio-ready + master.",
    artist: "Quartiere9", artistFull: "Davide Romano", artistInit: "Q9", verified: true,
    city: "Roma", country: "IT", rating: 4.9, pastProjects: 12,
    budget: { min: 600, max: 900 }, deadlineDays: 21, offers: 7,
    posted: "5h fa", expiresIn: "27gg", postedHours: 5,
    matchTags: ["Drill", "Mix + Master"], genres: ["Drill"], briefComplete: true,
  },
  {
    id: "REQ-9203", kind: "Custom production",
    title: "Brano R&B con vibe Daniel Caesar / Brent Faiyaz",
    brief: "Beat + mix base per singolo R&B. Cerco mood notturno, chitarre arpeggiate, drum break leggero.",
    artist: "Lina Sky", artistFull: "Lina Sky", artistInit: "LS", verified: false,
    city: "Bologna", country: "IT", rating: null, pastProjects: 0,
    budget: { min: 400, max: 400, fixed: true }, deadlineDays: 7, offers: 2,
    posted: "Ieri", expiresIn: "23gg", postedHours: 26,
    matchTags: ["R&B"], genres: ["R&B"], new: true, briefComplete: false,
  },
  {
    id: "REQ-9198", kind: "Vocal production",
    title: "Vocal production + tuning su 1 brano trap melodico",
    brief: "Ho registrato voce e doppie ma serve tuning fine, comping, ad-lib di rinforzo e processing finale prima del mix.",
    artist: "DamoBeats", artistFull: "Damiano B.", artistInit: "DB", verified: true,
    city: "Milano", country: "IT", rating: 4.5, pastProjects: 2,
    budget: { min: 80, max: 150 }, deadlineDays: 4, offers: 3,
    posted: "2gg fa", expiresIn: "22gg", postedHours: 50,
    matchTags: ["Trap", "Vocal prod"], genres: ["Trap"], briefComplete: true,
  },
  {
    id: "REQ-9192", kind: "Beat custom",
    title: "Beat hip-hop boom bap con campioni vinilici",
    brief: "Cerco beat boom bap old-school, 90-95 BPM. Devono esserci sample vinilici puliti (no copyright). Per un EP indipendente.",
    artist: "Rino82", artistFull: "Riccardo N.", artistInit: "R8", verified: false,
    city: "Torino", country: "IT", rating: 4.2, pastProjects: 1,
    budget: { min: 150, max: 300 }, deadlineDays: 15, offers: 5,
    posted: "3gg fa", expiresIn: "20gg", postedHours: 74,
    matchTags: ["Hip-hop"], genres: ["Hip-hop"], bpm: 92, briefComplete: true,
  },
  {
    id: "REQ-9185", kind: "Mastering",
    title: "Master per singolo trap già missato",
    brief: "Mix già pronto, cerco master loud + adattamento Spotify + WAV/MP3 finali. 48h se possibile.",
    artist: "Yoshi K.", artistFull: "Yoshi K.", artistInit: "YK", verified: true,
    city: "Milano", country: "IT", rating: 4.8, pastProjects: 5,
    budget: { min: 70, max: 100 }, deadlineDays: 2, offers: 6,
    posted: "4gg fa", expiresIn: "16gg", postedHours: 98,
    matchTags: ["Trap", "Master"], genres: ["Trap"], briefComplete: true,
  },
  // Extra forYou — variety
  {
    id: "REQ-9176", kind: "Beat custom",
    title: "Beat drill UK 140 BPM scuro con flauti",
    brief: "Per featuring tra 2 artisti, vibe Central Cee / Headie One. Flauti dark, 808 scivolosi, switch alla seconda strofa.",
    artist: "Jaxon", artistFull: "Jaxon", artistInit: "JX", verified: true,
    city: "Milano", country: "IT", rating: 4.6, pastProjects: 4,
    budget: { min: 250, max: 450 }, deadlineDays: 8, offers: 9,
    posted: "1gg fa", expiresIn: "21gg", postedHours: 28,
    matchTags: ["Drill", "BPM 140"], genres: ["Drill"], bpm: 140, mkey: "C min", briefComplete: true,
  },
  {
    id: "REQ-9170", kind: "Mix stereo",
    title: "Mix singolo trap melodico — premium",
    brief: "Singolo solista, 808 puliti, voce in primo piano, autotune leggero. Cerco mix radio-ready stile Lazza/Sfera.",
    artist: "ElenaR.", artistFull: "Elena Rovera", artistInit: "ER", verified: true,
    city: "Napoli", country: "IT", rating: 4.9, pastProjects: 18,
    budget: { min: 180, max: 280 }, deadlineDays: 12, offers: 11,
    posted: "2gg fa", expiresIn: "20gg", postedHours: 52,
    matchTags: ["Trap", "Mix"], genres: ["Trap"], briefComplete: true,
  },
  // Out-of-match — only in "Tutte"
  {
    id: "REQ-9181", kind: "Songwriting",
    title: "Topline + testo per brano indie-pop italiano",
    brief: "Beat indie-pop pronto, cerco topliner per melodia + testo (italiano). Vibe Coma_Cose / Maneskin ballad.",
    artist: "Olivia M.", artistFull: "Olivia Marletti", artistInit: "OM", verified: false,
    city: "Firenze", country: "IT", rating: null, pastProjects: 0,
    budget: { min: 200, max: 400 }, deadlineDays: 14, offers: 2,
    posted: "5gg fa", expiresIn: "15gg", postedHours: 122,
    matchTags: ["Indie-pop"], genres: ["Indie-pop"], outOfMatch: true, briefComplete: false,
  },
  {
    id: "REQ-9174", kind: "Sound design",
    title: "Sound design per podcast true-crime",
    brief: "8 puntate, mi serve sigla + transizioni + atmo dark/tensione. Stile Serial / Indagini.",
    artist: "Studio Onda", artistFull: "Studio Onda", artistInit: "SO", verified: true,
    city: "Roma", country: "IT", rating: 4.6, pastProjects: 9,
    budget: { min: 800, max: 1500 }, deadlineDays: 30, offers: 11,
    posted: "1 sett. fa", expiresIn: "13gg", postedHours: 168,
    matchTags: ["Sound design"], genres: ["Cinematic"], outOfMatch: true, briefComplete: true,
  },
  {
    id: "REQ-9168", kind: "Mix + Master",
    title: "Mix album cantautorato acustico — 9 tracce",
    brief: "Album cantautorato registrato in analogico, stems puliti. Cerco mix che mantenga il sapore caldo dei nastri.",
    artist: "Pietro V.", artistFull: "Pietro Vannucci", artistInit: "PV", verified: true,
    city: "Verona", country: "IT", rating: 4.9, pastProjects: 7,
    budget: { min: 900, max: 1400 }, deadlineDays: 28, offers: 8,
    posted: "1 sett. fa", expiresIn: "13gg", postedHours: 170,
    matchTags: ["Cantautorato"], genres: ["Cantautorato"], outOfMatch: true, briefComplete: true,
  },
  {
    id: "REQ-9160", kind: "Beat custom",
    title: "Beat techno/house 124 BPM per DJ set",
    brief: "Producer techno emergente, cerco beat originale per chiusura set. 124 BPM, dark, build lungo.",
    artist: "K-Line", artistFull: "K-Line", artistInit: "KL", verified: false,
    city: "Berlino", country: "DE", rating: 4.3, pastProjects: 2,
    budget: { min: 300, max: 500 }, deadlineDays: 12, offers: 14,
    posted: "1 sett. fa", expiresIn: "10gg", postedHours: 175,
    matchTags: ["Techno"], genres: ["Elettronica"], bpm: 124, outOfMatch: true, briefComplete: true,
  },
];

const SAVED_SEARCHES = [
  { id: "ss1", name: "Trap Milano premium", desc: "Trap · Milano · €200+ · <5 offerte", count: 3, alert: "instant" },
  { id: "ss2", name: "Mix EP completo", desc: "Mix+Master · €500+ · 14gg+", count: 8, alert: "daily" },
  { id: "ss3", name: "Custom R&B", desc: "Custom · R&B · €300+", count: 1, alert: "weekly" },
];

const ALL_GENRES = ["Trap", "Drill", "Hip-hop", "R&B", "Pop", "Indie-pop", "Cantautorato", "Elettronica", "Cinematic", "Jazz", "Lofi", "Rock"];
const ALL_TYPES = ["Beat custom", "Mix stereo", "Mastering", "Mix + Master", "Vocal production", "Custom production", "Sound design", "Songwriting"];
const ALL_KEYS = ["C", "C# / Db", "D", "D# / Eb", "E", "F", "F# / Gb", "G", "G# / Ab", "A", "A# / Bb", "B"];

const DEFAULT_FILTERS = {
  search: "",
  types: [],
  genres: [],
  budget: [0, 2000],
  geo: "italy",      // italy | europe | world | near
  radius: 50,
  offers: "all",     // all | lt3 | mid | high
  deadline: "all",   // all | rush | week2 | week4 | longer
  verifiedOnly: false,
  briefCompleteOnly: false,
  noOffersOnly: false,
  bpmRange: [60, 200],
  keys: [],
};

/* ──────────────────────────────────────────────────────── */
/* PRIMITIVES                                               */
/* ──────────────────────────────────────────────────────── */

function FilterSection({ title, badge, children, defaultOpen = true }) {
  const [open, setOpen] = React.useState(defaultOpen);
  return (
    <div className={`fs-sec ${open ? "open" : ""}`}>
      <button className="fs-sec-head" onClick={() => setOpen(!open)}>
        <span className="fs-sec-title">{title}</span>
        {badge && <span className="fs-sec-badge">{badge}</span>}
        <span className={`fs-sec-chev ${open ? "open" : ""}`}><ChevIc size={12}/></span>
      </button>
      {open && <div className="fs-sec-body">{children}</div>}
    </div>
  );
}

function PillRow({ options, value, onChange, multi = true }) {
  return (
    <div className="fs-pills">
      {options.map(opt => {
        const v = typeof opt === "string" ? opt : opt.v;
        const l = typeof opt === "string" ? opt : opt.l;
        const active = multi ? value.includes(v) : value === v;
        return (
          <button
            key={v}
            className={`fs-pill ${active ? "active" : ""}`}
            onClick={() => {
              if (multi) onChange(active ? value.filter(x => x !== v) : [...value, v]);
              else onChange(v);
            }}
          >
            {l}
          </button>
        );
      })}
    </div>
  );
}

function Segmented({ options, value, onChange }) {
  return (
    <div className="fs-seg">
      {options.map(opt => (
        <button
          key={opt.v}
          className={`fs-seg-btn ${value === opt.v ? "active" : ""}`}
          onClick={() => onChange(opt.v)}
        >
          {opt.l}
          {opt.s && <span className="fs-seg-sub">{opt.s}</span>}
        </button>
      ))}
    </div>
  );
}

function FsToggle({ label, sub, checked, onChange }) {
  return (
    <label className="fs-toggle">
      <div className="fs-toggle-meta">
        <span className="t">{label}</span>
        {sub && <span className="s">{sub}</span>}
      </div>
      <button type="button" className={`pf-switch ${checked ? "on" : ""}`} onClick={() => onChange(!checked)}/>
    </label>
  );
}

function DualRange({ min, max, step = 1, value, onChange, fmt }) {
  const [low, high] = value;
  const span = max - min || 1;
  const lowPct = ((low - min) / span) * 100;
  const highPct = ((high - min) / span) * 100;
  return (
    <div className="fs-range">
      <div className="fs-range-track">
        <div className="fs-range-fill" style={{ left: `${lowPct}%`, right: `${100 - highPct}%` }}/>
      </div>
      <input
        type="range" min={min} max={max} step={step} value={low}
        onChange={(e) => {
          const v = Math.min(Number(e.target.value), high - step);
          onChange([v, high]);
        }}
      />
      <input
        type="range" min={min} max={max} step={step} value={high}
        onChange={(e) => {
          const v = Math.max(Number(e.target.value), low + step);
          onChange([low, v]);
        }}
      />
      <div className="fs-range-labels">
        <span className="fs-range-l">{fmt(low)}</span>
        <span className="fs-range-l">{fmt(high)}{high === max ? "+" : ""}</span>
      </div>
    </div>
  );
}

/* ──────────────────────────────────────────────────────── */
/* FILTER SIDEBAR                                           */
/* ──────────────────────────────────────────────────────── */

function FilterSidebar({ filters, setFilters, count, onClear, onSave, scope, location, onLocationConsent }) {
  const set = (k, v) => setFilters(f => ({ ...f, [k]: v }));
  const beatLike = filters.types.length === 0 || filters.types.some(t => t === "Beat custom" || t === "Custom production");

  return (
    <aside className="fs">
      <div className="fs-head">
        <div className="fs-head-l">
          <span className="fs-head-title">Filtri</span>
          <span className="fs-head-count">{count} risultati</span>
        </div>
        <button className="fs-clear" onClick={onClear}>Pulisci tutto</button>
      </div>

      <div className="fs-body">
        {/* SAVED SEARCHES */}
        <FilterSection title="Le tue ricerche salvate" badge={SAVED_SEARCHES.length} defaultOpen={false}>
          <div className="fs-saved">
            {SAVED_SEARCHES.map(s => (
              <button key={s.id} className="fs-saved-item">
                <div className="fs-saved-meta">
                  <div className="fs-saved-name">
                    <BookmarkIc size={11}/> {s.name}
                  </div>
                  <div className="fs-saved-desc">{s.desc}</div>
                </div>
                <div className="fs-saved-right">
                  <span className="fs-saved-count">{s.count}</span>
                  <span className={`fs-saved-alert ${s.alert}`}>
                    <BellIc size={10}/> {s.alert === "instant" ? "instant" : s.alert === "daily" ? "1/g" : "1/sett."}
                  </span>
                </div>
              </button>
            ))}
            <button className="fs-saved-new" onClick={onSave}>
              <BookmarkIc size={12}/> Salva ricerca corrente
            </button>
          </div>
        </FilterSection>

        {/* TIPO */}
        <FilterSection title="Tipo di lavoro" badge={filters.types.length || null} defaultOpen={true}>
          <PillRow
            options={ALL_TYPES}
            value={filters.types}
            onChange={(v) => set("types", v)}
          />
        </FilterSection>

        {/* GENERI */}
        <FilterSection title="Generi musicali" badge={filters.genres.length || null} defaultOpen={false}>
          {scope === "forYou" && (
            <div className="fs-hint">
              Suggeriti dal tuo profilo: <strong>{NICO_GENRES.join(", ")}</strong>
            </div>
          )}
          <PillRow
            options={ALL_GENRES}
            value={filters.genres}
            onChange={(v) => set("genres", v)}
          />
        </FilterSection>

        {/* BUDGET */}
        <FilterSection title="Budget" defaultOpen={false}>
          <DualRange
            min={0} max={2000} step={50}
            value={filters.budget}
            onChange={(v) => set("budget", v)}
            fmt={(n) => `€${n}`}
          />
        </FilterSection>

        {/* GEO */}
        <FilterSection title="Geolocalizzazione" defaultOpen={false}>
          <Segmented
            options={[
              { v: "italy",  l: "Italia",   s: "default" },
              { v: "europe", l: "Europa" },
              { v: "world",  l: "Mondo" },
              { v: "near",   l: "Vicino a me", s: location.status === "granted" ? `${location.city} · ${location.country}` : "richiede posizione" },
            ]}
            value={filters.geo}
            onChange={(v) => {
              if (v === "near" && location.status !== "granted") {
                onLocationConsent();
                return;
              }
              set("geo", v);
            }}
          />
          {filters.geo === "near" && location.status === "granted" && (
            <>
              <div className="fs-geo-detected">
                <span className="fs-geo-pin"><PinIc size={11}/></span>
                <div className="fs-geo-meta">
                  <div className="t">{location.city}, {location.country}</div>
                  <div className="s">Posizione rilevata automaticamente · <button className="fs-geo-change" onClick={onLocationConsent}>cambia</button></div>
                </div>
              </div>
              <div className="fs-radius">
                <div className="fs-radius-l">
                  <span>Raggio</span>
                  <strong>{filters.radius} km</strong>
                </div>
                <input
                  type="range" min="5" max="500" step="5"
                  value={filters.radius}
                  onChange={(e) => set("radius", Number(e.target.value))}
                  className="fs-single-range"
                />
                <div className="fs-range-labels">
                  <span>5 km</span>
                  <span>500 km</span>
                </div>
              </div>
            </>
          )}
          {location.status === "requesting" && (
            <div className="fs-geo-requesting">
              <span className="fs-geo-spinner"/>
              Sto rilevando la tua posizione…
            </div>
          )}
          {location.status === "denied" && (
            <div className="fs-geo-denied">
              Non sei riuscito a condividere la posizione. <button onClick={onLocationConsent}>Riprova</button>
            </div>
          )}
        </FilterSection>

        {/* OFFERTE */}
        <FilterSection title="Offerte già ricevute" defaultOpen={false}>
          <Segmented
            options={[
              { v: "all",  l: "Tutte" },
              { v: "lt3",  l: "< 3",  s: "early bird" },
              { v: "mid",  l: "3–10", s: "competizione media" },
              { v: "high", l: "10+",  s: "affollate" },
            ]}
            value={filters.offers}
            onChange={(v) => set("offers", v)}
          />
        </FilterSection>

        {/* DEADLINE */}
        <FilterSection title="Tempi consegna" defaultOpen={false}>
          <Segmented
            options={[
              { v: "all",     l: "Qualsiasi" },
              { v: "rush",    l: "Rush", s: "< 7gg" },
              { v: "week2",   l: "1-2 sett." },
              { v: "week4",   l: "2-4 sett." },
              { v: "longer",  l: "Più di 4 sett." },
            ]}
            value={filters.deadline}
            onChange={(v) => set("deadline", v)}
          />
        </FilterSection>

        {/* ARTISTA */}
        <FilterSection title="Qualità richiesta" defaultOpen={false}>
          <FsToggle
            label="Solo artisti verificati"
            sub="Identità + storico confermati"
            checked={filters.verifiedOnly}
            onChange={(v) => set("verifiedOnly", v)}
          />
          <FsToggle
            label="Solo brief completi"
            sub="Descrizione dettagliata + allegati"
            checked={filters.briefCompleteOnly}
            onChange={(v) => set("briefCompleteOnly", v)}
          />
          <FsToggle
            label="Senza offerte ancora"
            sub="Sei il primo a proporre"
            checked={filters.noOffersOnly}
            onChange={(v) => set("noOffersOnly", v)}
          />
        </FilterSection>

        {/* BPM + KEY */}
        {beatLike && (
          <FilterSection title="BPM & Key" defaultOpen={false}>
            <div className="fs-hint">Si applica solo a richieste Beat / Custom production</div>
            <DualRange
              min={60} max={200} step={1}
              value={filters.bpmRange}
              onChange={(v) => set("bpmRange", v)}
              fmt={(n) => `${n} BPM`}
            />
            <div className="fs-subtitle">Key</div>
            <PillRow
              options={ALL_KEYS}
              value={filters.keys}
              onChange={(v) => set("keys", v)}
            />
          </FilterSection>
        )}
      </div>

      <div className="fs-foot">
        <span className="fs-foot-count"><strong>{count}</strong> risultati</span>
        <button className="ord-btn primary fs-save-btn" onClick={onSave}>
          <BookmarkIc size={12}/> Salva ricerca
        </button>
      </div>
    </aside>
  );
}

/* ──────────────────────────────────────────────────────── */
/* ACTIVE FILTERS CHIPS                                     */
/* ──────────────────────────────────────────────────────── */

function ActiveChips({ filters, setFilters }) {
  const chips = [];
  const set = (k, v) => setFilters(f => ({ ...f, [k]: v }));

  filters.types.forEach(t => chips.push({ k: `t-${t}`, label: t, remove: () => set("types", filters.types.filter(x => x !== t)) }));
  filters.genres.forEach(g => chips.push({ k: `g-${g}`, label: g, remove: () => set("genres", filters.genres.filter(x => x !== g)) }));
  if (filters.budget[0] > 0 || filters.budget[1] < 2000) {
    chips.push({ k: "budget", label: `€${filters.budget[0]}–€${filters.budget[1]}${filters.budget[1] === 2000 ? "+" : ""}`, remove: () => set("budget", [0, 2000]) });
  }
  if (filters.geo !== "italy") {
    const map = { europe: "Europa", world: "Mondo", near: `Vicino a me · ${filters.radius}km` };
    chips.push({ k: "geo", label: map[filters.geo] || filters.geo, remove: () => { set("geo", "italy"); } });
  }
  if (filters.offers !== "all") {
    const m = { lt3: "< 3 offerte", mid: "3-10 offerte", high: "10+ offerte" };
    chips.push({ k: "offers", label: m[filters.offers], remove: () => set("offers", "all") });
  }
  if (filters.deadline !== "all") {
    const m = { rush: "Rush <7gg", week2: "1-2 sett.", week4: "2-4 sett.", longer: ">4 sett." };
    chips.push({ k: "deadline", label: m[filters.deadline], remove: () => set("deadline", "all") });
  }
  if (filters.verifiedOnly) chips.push({ k: "v", label: "Verificati", remove: () => set("verifiedOnly", false) });
  if (filters.briefCompleteOnly) chips.push({ k: "b", label: "Brief completo", remove: () => set("briefCompleteOnly", false) });
  if (filters.noOffersOnly) chips.push({ k: "n", label: "0 offerte", remove: () => set("noOffersOnly", false) });
  if (filters.bpmRange[0] > 60 || filters.bpmRange[1] < 200) {
    chips.push({ k: "bpm", label: `${filters.bpmRange[0]}–${filters.bpmRange[1]} BPM`, remove: () => set("bpmRange", [60, 200]) });
  }
  filters.keys.forEach(k => chips.push({ k: `k-${k}`, label: k, remove: () => set("keys", filters.keys.filter(x => x !== k)) }));

  if (chips.length === 0) return null;
  return (
    <div className="opp-active-chips">
      {chips.map(c => (
        <button key={c.k} className="opp-active-chip" onClick={c.remove}>
          {c.label} <XIc size={10}/>
        </button>
      ))}
      <button className="opp-active-clear" onClick={() => setFilters(DEFAULT_FILTERS)}>Pulisci tutto</button>
    </div>
  );
}

/* ──────────────────────────────────────────────────────── */
/* FILTERING LOGIC                                          */
/* ──────────────────────────────────────────────────────── */

function applyFilters(reqs, filters, scope) {
  return reqs.filter(r => {
    if (scope === "forYou" && r.outOfMatch) return false;
    if (filters.search) {
      const q = filters.search.toLowerCase();
      if (!(r.title + " " + r.brief).toLowerCase().includes(q)) return false;
    }
    if (filters.types.length > 0 && !filters.types.includes(r.kind)) return false;
    if (filters.genres.length > 0 && !r.genres.some(g => filters.genres.includes(g))) return false;
    if (r.budget.max < filters.budget[0]) return false;
    if (filters.budget[1] < 2000 && r.budget.min > filters.budget[1]) return false;
    // Geo
    if (filters.geo === "near" && r.city !== "Milano") return false;
    if (filters.geo === "italy" && r.country !== "IT") return false;
    if (filters.geo === "europe" && !["IT", "DE", "FR", "ES", "UK", "NL"].includes(r.country)) return false;
    // Offers
    if (filters.offers === "lt3" && r.offers >= 3) return false;
    if (filters.offers === "mid" && (r.offers < 3 || r.offers > 10)) return false;
    if (filters.offers === "high" && r.offers <= 10) return false;
    if (filters.noOffersOnly && r.offers > 0) return false;
    // Deadline
    if (filters.deadline === "rush" && r.deadlineDays >= 7) return false;
    if (filters.deadline === "week2" && (r.deadlineDays < 7 || r.deadlineDays > 14)) return false;
    if (filters.deadline === "week4" && (r.deadlineDays < 14 || r.deadlineDays > 30)) return false;
    if (filters.deadline === "longer" && r.deadlineDays <= 30) return false;
    if (filters.verifiedOnly && !r.verified) return false;
    if (filters.briefCompleteOnly && !r.briefComplete) return false;
    // BPM (only beat-like)
    if (r.bpm != null && (r.bpm < filters.bpmRange[0] || r.bpm > filters.bpmRange[1])) return false;
    return true;
  });
}

function sortReqs(items, sort) {
  const copy = [...items];
  if (sort === "match")    copy.sort((a, b) => (b.genres.some(g => NICO_GENRES.includes(g)) ? 1 : 0) - (a.genres.some(g => NICO_GENRES.includes(g)) ? 1 : 0));
  if (sort === "recent")   copy.sort((a, b) => a.postedHours - b.postedHours);
  if (sort === "budget")   copy.sort((a, b) => b.budget.max - a.budget.max);
  if (sort === "offers")   copy.sort((a, b) => a.offers - b.offers);
  if (sort === "deadline") copy.sort((a, b) => b.deadlineDays - a.deadlineDays);
  return copy;
}

/* ──────────────────────────────────────────────────────── */
/* CARD + DETAIL                                            */
/* ──────────────────────────────────────────────────────── */

function ReqCard({ r, active, onClick }) {
  const isHot = r.postedHours <= 6 && r.offers <= 4;
  return (
    <button className={`opp-card opp-card-v2 ${active ? "active" : ""} ${r.new ? "new" : ""} ${r.outOfMatch ? "out" : ""} ${isHot ? "hot" : ""}`} onClick={onClick}>
      <div className="opp-card-head">
        <div className="opp-card-head-l">
          <span className="opp-card-kind">{r.kind}</span>
          {r.new && <span className="opp-card-new">NEW</span>}
        </div>
        <span className="opp-card-time">{r.posted}</span>
      </div>
      <div className="opp-card-title">{r.title}</div>
      <div className="opp-card-foot opp-card-foot-v2">
        <span className="opp-card-budget">
          {r.budget.fixed ? <>€{r.budget.min}</> : <>€{r.budget.min}–€{r.budget.max}</>}
        </span>
        <span className="sep">·</span>
        <span className="off-av sm">{r.artistInit}</span>
        <span className="opp-card-artist">{r.artist}</span>
        <span className="sep">·</span>
        <span>{r.city}</span>
        <span className="sep">·</span>
        <span>{r.offers} offerte</span>
      </div>
    </button>
  );
}

function ReqDetail({ r }) {
  if (!r) return (
    <div className="opp-detail empty">
      <div className="cat-empty">
        <div className="ic"><OpIcons.target size={26}/></div>
        <div className="t">Seleziona una richiesta</div>
        <div className="s">Apri una richiesta dal feed per leggere il brief e inviare un'offerta.</div>
      </div>
    </div>
  );
  return (
    <div className="opp-detail">
      <div className="opp-detail-head">
        <div className="opp-detail-head-l">
          <span className="ord-status info">{r.kind}</span>
          {r.new && <span className="opp-card-new">NEW</span>}
          <span className="opp-card-time"><ClockIc size={11}/> Postata {r.posted} · scade tra {r.expiresIn}</span>
        </div>
        <button className="cat-icon-btn" title="Salva"><BookmarkIc size={13}/></button>
      </div>
      <h2 className="opp-detail-title">{r.title}</h2>

      <div className="opp-detail-meta-row">
        <span><strong>Budget</strong> {r.budget.fixed ? `€${r.budget.min}` : `€${r.budget.min}–€${r.budget.max}`}</span>
        <span className="sep">·</span>
        <span><strong>Deadline</strong> {r.deadlineDays}gg</span>
        <span className="sep">·</span>
        <span><strong>{r.offers}</strong> offerte ricevute</span>
        <span className="sep">·</span>
        <span>scade tra <strong>{r.expiresIn}</strong></span>
      </div>

      <section className="opp-detail-section">
        <div className="opp-detail-section-title">Artista</div>
        <a href="#" onClick={(e) => e.preventDefault()} className="opp-artist-card">
          <span className="opp-artist-av">{r.artistInit}</span>
          <div className="opp-artist-meta">
            <div className="opp-artist-name">
              {r.artistFull}
              {r.verified && <span className="pf-verified-badge" style={{ marginLeft: 6 }}>Verificato</span>}
            </div>
            <div className="opp-artist-sub">
              @{r.artist}
              <span className="sep">·</span>
              <PinIc size={10}/> {r.city}, {r.country}
              {r.rating != null && (
                <>
                  <span className="sep">·</span>
                  <StarIc size={10}/> {r.rating} ({r.pastProjects} progetti)
                </>
              )}
              {r.pastProjects === 0 && (
                <>
                  <span className="sep">·</span>
                  <span style={{ color: "var(--violet-bright,#7b55d0)" }}>Primo progetto</span>
                </>
              )}
            </div>
          </div>
          <span className="ord-btn ghost">Vedi profilo →</span>
        </a>
      </section>

      <section className="opp-detail-section">
        <div className="opp-detail-section-title">Brief</div>
        <p className="opp-brief">{r.brief}</p>
      </section>

      {r.attachments && (
        <section className="opp-detail-section">
          <div className="opp-detail-section-title">Allegati dell'artista <span className="count">{r.attachments.length}</span></div>
          <div className="opp-attach-list">
            {r.attachments.map((a, i) => (
              <div className="opp-attach" key={i}>
                <button className="opp-attach-play"><PlayIc size={10}/></button>
                <div className="opp-attach-meta">
                  <div className="t">{a.name}</div>
                  <div className="s">{a.size}</div>
                </div>
                <button className="cat-icon-btn"><PaperIc size={12}/></button>
              </div>
            ))}
          </div>
        </section>
      )}

      <SendOffer r={r}/>
    </div>
  );
}

function SendOffer({ r }) {
  const [price, setPrice] = React.useState(r.budget.max);
  const [days, setDays] = React.useState(r.deadlineDays);
  const [msg, setMsg] = React.useState("");
  const [sent, setSent] = React.useState(false);

  React.useEffect(() => { setPrice(r.budget.max); setDays(r.deadlineDays); setSent(false); setMsg(""); }, [r.id]);

  if (sent) {
    return (
      <section className="opp-detail-section">
        <div className="opp-sent">
          <span className="opp-sent-ic"><BoltIc size={14}/></span>
          <div>
            <div className="opp-sent-t">Offerta inviata!</div>
            <div className="opp-sent-s">Hai 14 offerte attive su 15 max. Ti notificheremo quando {r.artist} risponde.</div>
          </div>
          <a href="Ordini.html" className="ord-btn primary">Vedi offerte inviate</a>
        </div>
      </section>
    );
  }

  return (
    <section className="opp-detail-section opp-send">
      <div className="opp-detail-section-title">Invia un'offerta</div>
      <div className="opp-send-row two">
        <div className="opp-send-field">
          <label>Prezzo proposto (€)</label>
          <input type="number" min="5" value={price} onChange={(e) => setPrice(Number(e.target.value))}/>
          <div className="opp-send-hint">
            Budget artista: {r.budget.fixed ? `€${r.budget.min}` : `€${r.budget.min}–€${r.budget.max}`}
            <span className="sep">·</span>
            <span>Netto a te: <strong>€{(price * 0.88).toFixed(0)}</strong> (12% fee Sonara)</span>
          </div>
        </div>
        <div className="opp-send-field">
          <label>Consegna (giorni)</label>
          <input type="number" min="1" max="60" value={days} onChange={(e) => setDays(Number(e.target.value))}/>
          <div className="opp-send-hint">Artista chiede entro {r.deadlineDays}gg</div>
        </div>
      </div>
      <div className="opp-send-field">
        <label>Messaggio per l'artista <span className="count">{msg.length}/500</span></label>
        <textarea
          rows="4"
          placeholder="Presentati brevemente. Cosa porti di unico? Hai esempi simili? (50–500 caratteri)"
          value={msg}
          onChange={(e) => setMsg(e.target.value.slice(0, 500))}
        />
      </div>
      <div className="opp-send-attach">
        <button className="ord-btn ghost"><PaperclipIc size={12}/> Allega snippet beat (max 25 MB)</button>
        <span className="opp-send-hint">Opzionale ma alza le chance di accettazione</span>
      </div>
      <div className="opp-send-foot">
        <div className="opp-send-warn">
          <PaperIc size={12}/>
          Contratto standard Sonara · escrow attivo
        </div>
        <div className="opp-send-acts">
          <button className="ord-btn">Salva bozza</button>
          <button className="ord-btn primary" onClick={() => setSent(true)} disabled={msg.length < 50}>
            Invia offerta →
          </button>
        </div>
      </div>
    </section>
  );
}

/* ──────────────────────────────────────────────────────── */
/* SAVE SEARCH MODAL                                        */
/* ──────────────────────────────────────────────────────── */

function SaveSearchModal({ open, onClose }) {
  const [name, setName] = React.useState("");
  const [alert, setAlert] = React.useState("instant");
  if (!open) return null;
  return (
    <div className="fs-modal-overlay" onClick={onClose}>
      <div className="fs-modal" onClick={(e) => e.stopPropagation()}>
        <div className="fs-modal-head">
          <div>
            <div className="fs-modal-title">Salva questa ricerca</div>
            <div className="fs-modal-sub">Ricevi notifiche quando arrivano nuove richieste che matchano i tuoi filtri.</div>
          </div>
          <button className="cat-icon-btn" onClick={onClose}><XIc size={14}/></button>
        </div>
        <div className="fs-modal-body">
          <div className="opp-send-field">
            <label>Nome della ricerca</label>
            <input type="text" placeholder="Es. Trap Milano premium" value={name} onChange={(e) => setName(e.target.value)}/>
          </div>
          <div className="opp-send-field">
            <label>Frequenza notifiche</label>
            <div className="fs-seg">
              {[
                { v: "instant", l: "Immediata", s: "Push + email" },
                { v: "daily",   l: "1 volta al giorno", s: "Digest 8:00" },
                { v: "weekly",  l: "1 volta a settimana", s: "Lun. 9:00" },
                { v: "none",    l: "Solo salvata", s: "Niente notifica" },
              ].map(o => (
                <button key={o.v} className={`fs-seg-btn ${alert === o.v ? "active" : ""}`} onClick={() => setAlert(o.v)}>
                  {o.l}
                  {o.s && <span className="fs-seg-sub">{o.s}</span>}
                </button>
              ))}
            </div>
          </div>
        </div>
        <div className="fs-modal-foot">
          <button className="ord-btn ghost" onClick={onClose}>Annulla</button>
          <button className="ord-btn primary" disabled={!name} onClick={onClose}>
            <BookmarkIc size={12}/> Salva ricerca
          </button>
        </div>
      </div>
    </div>
  );
}

/* ──────────────────────────────────────────────────────── */
/* SORT MENU (custom dropdown)                              */
/* ──────────────────────────────────────────────────────── */

const SORT_OPTIONS = [
  { v: "match",    l: "Match score" },
  { v: "recent",   l: "Più recenti" },
  { v: "budget",   l: "Budget alto" },
  { v: "offers",   l: "Meno offerte" },
  { v: "deadline", l: "Deadline lontana" },
];

function SortMenu({ sort, setSort }) {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    const onDown = (e) => { if (open && ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", onDown);
    return () => document.removeEventListener("mousedown", onDown);
  }, [open]);
  const current = SORT_OPTIONS.find(o => o.v === sort) || SORT_OPTIONS[0];
  return (
    <div className="opp-sort" ref={ref}>
      <button className={`opp-sort-btn ${open ? "open" : ""}`} onClick={() => setOpen(!open)}>
        <span className="lbl">Ordina</span>
        <span className="val">{current.l}</span>
        <svg className="chev" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
      </button>
      {open && (
        <div className="opp-sort-menu">
          {SORT_OPTIONS.map(o => (
            <button key={o.v} className={`opp-sort-opt ${sort === o.v ? "active" : ""}`}
                    onClick={() => { setSort(o.v); setOpen(false); }}>
              {o.l}
              <svg className="check" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

/* ──────────────────────────────────────────────────────── */
/* PAGE                                                     */
/* ──────────────────────────────────────────────────────── */

function OpportunitaPage() {
  const [selected, setSelected] = React.useState("REQ-9214");
  const [scope, setScope] = React.useState("forYou");
  const [filters, setFilters] = React.useState(DEFAULT_FILTERS);
  const [sort, setSort] = React.useState("match");
  const [saveOpen, setSaveOpen] = React.useState(false);
  const [filtersMobileOpen, setFiltersMobileOpen] = React.useState(false);
  const [railOpen, setRailOpen] = React.useState(() => {
    try { return localStorage.getItem("sonara-opp-rail-open") !== "0"; } catch (e) { return true; }
  });
  React.useEffect(() => {
    try { localStorage.setItem("sonara-opp-rail-open", railOpen ? "1" : "0"); } catch (e) {}
  }, [railOpen]);
  const [location, setLocation] = React.useState({ status: "idle", city: null, country: null });

  const requestLocation = () => {
    setLocation({ status: "requesting", city: null, country: null });
    setTimeout(() => {
      setLocation({ status: "granted", city: "Milano", country: "IT" });
      setFilters(f => ({ ...f, geo: "near" }));
    }, 1400);
  };

  const filtered = applyFilters(REQS, filters, scope);
  const sorted = sortReqs(filtered, sort);
  const current = REQS.find(r => r.id === selected);
  const forYouCount = applyFilters(REQS, filters, "forYou").length;
  const allCount = applyFilters(REQS, filters, "all").length;

  const onClear = () => setFilters(DEFAULT_FILTERS);

  return (
    <main className="page opp-page-wide" data-screen-label="Opportunità Producer">
      <div className="cat-head">
        <div className="cat-head-l">
          <h1 className="cat-h1">Opportunità</h1>
          <div className="cat-head-stats">
            <span><strong>2</strong> offerte attive · 13 slot disponibili</span>
            <span className="sep">·</span>
            <span style={{ color: "var(--muted-foreground)" }}>10.247 richieste open sulla piattaforma</span>
          </div>
        </div>
        <button className="opp-mobile-filters" onClick={() => setFiltersMobileOpen(true)}>
          Filtri
        </button>
      </div>

      <div className="opp-scope-tabs">
        <button className={`opp-scope-tab ${scope === "forYou" ? "active" : ""}`} onClick={() => setScope("forYou")}>
          <BoltIc size={11}/>
          Per te
          <span className="c">{forYouCount}</span>
        </button>
        <button className={`opp-scope-tab ${scope === "all" ? "active" : ""}`} onClick={() => setScope("all")}>
          Tutte
          <span className="c">{allCount}</span>
        </button>
        <span className="opp-scope-info" title={scope === "forYou"
          ? "Algoritmo di ranking: match coi tuoi generi · boost geografico Milano 10% · match score"
          : "Tutte le richieste della piattaforma · senza filtri di genere automatici"}>
          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><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>
      </div>

      <ActiveChips filters={filters} setFilters={setFilters}/>

      <div className="opp-rail-toolbar">
        <button className={`rail-toggle-btn ${railOpen ? "open" : "closed"}`} onClick={() => setRailOpen(!railOpen)} aria-pressed={railOpen}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <line x1="4" y1="6" x2="20" y2="6"/>
            <line x1="6" y1="12" x2="18" y2="12"/>
            <line x1="9" y1="18" x2="15" y2="18"/>
          </svg>
          <span>{railOpen ? "Nascondi filtri" : "Mostra filtri"}</span>
        </button>
      </div>

      <div className={`opp-layout-v2 ${filtersMobileOpen ? "filters-open" : ""}`} data-rail={railOpen ? "open" : "closed"}>
        <FilterSidebar
          filters={filters}
          setFilters={setFilters}
          count={sorted.length}
          onClear={onClear}
          onSave={() => setSaveOpen(true)}
          scope={scope}
          location={location}
          onLocationConsent={requestLocation}
        />

        <div className="opp-feed-col">
          <div className="opp-feed-bar">
            <div className="cat-search" style={{ flex: "0 1 320px" }}>
              <SearchIc size={14}/>
              <input
                value={filters.search}
                onChange={(e) => setFilters(f => ({ ...f, search: e.target.value }))}
                placeholder="Cerca per titolo o keyword…"
              />
            </div>
            <div className="opp-feed-bar-spacer" style={{ flex: 1 }}/>
            <SortMenu sort={sort} setSort={setSort}/>
          </div>
          <div className="opp-feed-list opp-feed-list-v2">
            {sorted.map(r => (
              <ReqCard key={r.id} r={r} active={r.id === selected} onClick={() => setSelected(r.id)}/>
            ))}
            {sorted.length === 0 && (
              <div className="cat-empty" style={{ padding: "40px 16px" }}>
                <div className="t">Nessuna richiesta corrisponde</div>
                <div className="s">Allenta uno dei filtri o salva la ricerca per essere avvisato quando arriva.</div>
                <button className="pf-action-btn primary" style={{ marginTop: 14, display: "inline-flex" }} onClick={() => setSaveOpen(true)}>
                  <BookmarkIc size={12}/> Salva ricerca & avvisa
                </button>
              </div>
            )}
          </div>
        </div>

        <div className="opp-detail-wrap">
          <ReqDetail r={current}/>
        </div>

        {filtersMobileOpen && (
          <div className="fs-backdrop" onClick={() => setFiltersMobileOpen(false)}/>
        )}
      </div>

      <SaveSearchModal open={saveOpen} onClose={() => setSaveOpen(false)}/>
    </main>
  );
}

window.OpportunitaPage = OpportunitaPage;
