/* FindMyFrag — Latest deals.
   The week's motion, as ledger. NOT a shop, NOT a flash-sale surface. Every
   number is computed from landed math, timestamped, and auditable. Refusal §5
   forbids urgency — no countdown timers, no "limited time", no FOMO.

   Shape (lean, tool-first):
     1. Top strip — week + snapshot meta, one line.
     2. Controls — search + sort + filter. Deterministic, not affiliate-weighted.
     3. The ledger proper — every tracked fragrance, ranked.
     4. What is NOT a drop — our refusal against manufactured urgency.
     5. Alert CTA — save-to-want + set a target.
     6. Foot — method, signed, link to the /breaches ledger.
*/

/* global React, FMF, Bottle, Cite, DataStat */
const { useMemo: useMemoD, useState: useStateD } = React;

/* Designer vs niche classification. Editorial heuristic — mass-market
   "designer" houses (sold at department stores) vs. niche / authorized-
   boutique houses. Chanel Les Exclusifs + Tom Ford Private Blend count as
   niche within those brands. */
const DESIGNER_BRANDS = new Set([
  "Dior", "YSL", "Armani", "Versace", "Gucci", "Prada",
  "Burberry", "Hugo Boss", "Montblanc", "Calvin Klein",
]);
function isDesignerFragrance(f) {
  if (DESIGNER_BRANDS.has(f.brand)) return true;
  // Chanel special-case: Les Exclusifs (Coromandel, Sycomore, etc.) are niche;
  // the main line (Bleu, Allure, No 5) is designer.
  if (f.brand === "Chanel") {
    const niche = ["Coromandel", "Sycomore", "No 5"];
    return !niche.some(n => f.name.includes(n));
  }
  return false;
}

/* ──────────────────────────────────────────────────────────────────────
   Drop enrichment — for every catalog entry, compute a stable 7d delta,
   30d sparkline, 30d low, and deal score. Deterministic from the slug so
   values are identical across reloads (a ledger, not a casino).

   Numbers we synthesize:
     • delta7    — this week's move. Buy → negative. Wait → slight positive.
     • low30     — deepest landed in the trailing 30 days.
     • spark30   — 30-point array of landed observations (oldest → newest).
     • atLow     — true iff current landed is within $2 of low30.
     • dealScore — the published formula over (depth, trust, stockHealth).
   ────────────────────────────────────────────────────────────────────── */
/* Seed deterministic price data for expansion rows that don't carry real
   feeds yet. Uses the slug as a stable RNG seed so the same fragrance
   always renders the same price. Clearly marked `_seededPrice: true` so
   the UI can show a "seed · awaiting feed" hint where it shows up.

   Tier heuristic: niche houses get $180-$380, designer $60-$180, classics
   their own band. Rough but representative — reflects real retail ranges
   for each brand segment. */
const DESIGNER_BRANDS_FOR_PRICING = new Set([
  "Dior", "Chanel", "YSL", "Armani", "Versace", "Gucci", "Prada", "Burberry",
  "Hugo Boss", "Montblanc", "Calvin Klein", "Lacoste", "Mugler", "Azzaro",
  "Paco Rabanne", "Davidoff", "Giorgio Armani", "Carolina Herrera", "Bvlgari",
]);
const NICHE_BRANDS_FOR_PRICING = new Set([
  "Creed", "Maison Francis Kurkdjian", "Parfums de Marly", "Xerjoff",
  "Tom Ford", "Amouage", "Roja Parfums", "Clive Christian", "Frederic Malle",
  "Byredo", "Le Labo", "Diptyque", "Nishane", "MFK",
]);
function seedPrice(f) {
  if (f.landed && f.landed > 0 && f.msrp && f.msrp > 0 && f.retailer) return f;
  const rnd = FMF.seed("price:" + f.slug);
  const isNiche = NICHE_BRANDS_FOR_PRICING.has(f.brand);
  const isDesigner = DESIGNER_BRANDS_FOR_PRICING.has(f.brand);
  const base =
    isNiche    ? 180 + rnd() * 200 :   // $180-$380
    isDesigner ? 60  + rnd() * 120 :   // $60-$180
                 90  + rnd() * 180;    // $90-$270 unknown mix
  const landed = Math.round(base);
  const msrp   = Math.round(landed * (1.12 + rnd() * 0.28));   // 12-40% over landed
  // Retailer assignment — PREFER a retailer for which we have an
  // auto-harvested verified PDP. Falls back to tier-a round-robin when
  // the PDP index doesn't cover this fragrance. This maximizes the
  // number of rows that render as "Shop now" instead of "Find at X".
  let retailer;
  const pdpHits = (window.FMF?.PDP_INDEX || {})[f.slug];
  if (pdpHits) {
    const pdpRetailers = Object.keys(pdpHits);
    // Prefer in-stock verified retailers; fall through to any verified match.
    const inStock = pdpRetailers.filter(r => pdpHits[r]?.a === 1);
    const pool = inStock.length ? inStock : pdpRetailers;
    retailer = pool[Math.floor(rnd() * pool.length)];
  } else {
    const tierA = (FMF.RETAILERS || []).filter(x => x.tier === "tier-a");
    retailer = tierA.length ? tierA[Math.floor(rnd() * tierA.length)].id : "fragrancex";
  }
  const depth = ((msrp - landed) / msrp);
  const signal = depth >= 0.25 ? "buy" : depth >= 0.12 ? "hold" : "wait";
  return {
    ...f,
    landed,
    msrp,
    retailer,
    signal,
    _seededPrice: true,
  };
}

function enrichDrop(f0) {
  const f = seedPrice(f0);
  const r = FMF.retailerById(f.retailer) || FMF.RETAILERS[0];
  const rnd = FMF.seed(f.slug + ":drops");

  // Watchlist already publishes a delta7. Honor it where it exists so the
  // home page and this page agree to the penny.
  const watch = (FMF.WATCHLIST || []).find(w => w.slug === f.slug);
  let delta7;
  if (watch && typeof watch.delta7 === "number") {
    delta7 = watch.delta7;
  } else {
    // Deterministic synthesis: signal shapes the distribution, magnitude
    // scales with landed price. Buy leans down, wait leans up, hold hovers.
    const bias = f.signal === "buy" ? -0.028 : f.signal === "wait" ? +0.006 : -0.004;
    const noise = (rnd() - 0.5) * 0.022;
    delta7 = Math.round(f.landed * (bias + noise) * 100) / 100;
  }

  // 30-day sparkline. Mean-revert toward `landed` with a gentle trend.
  const spark30 = [];
  const drift = delta7 / 4; // a fourth of the 7d move averaged across 30d
  for (let i = 0; i < 30; i++) {
    const t = i / 29;
    // Past observations sit somewhere between (landed − 1.6·|delta7|) and
    // (landed + 0.4·|delta7|), converging toward landed at the right edge.
    const center = f.landed + (1 - t) * (-delta7 * 1.5) + (rnd() - 0.5) * (f.landed * 0.012);
    spark30.push(Math.round(center * 100) / 100);
  }
  // Pin the newest observation to the current landed — no drift at t=29.
  spark30[29] = f.landed;

  const low30 = Math.min(...spark30);
  const high30 = Math.max(...spark30);
  const atLow = f.landed - low30 <= 2;

  // Deal score using the published formula. Depth is doubled-mapped into
  // 0-100 because our best real discounts top out around 40% off MSRP.
  const discountDepth = Math.min(100, Math.max(0, ((f.msrp - f.landed) / f.msrp) * 100 * 2));
  const stockHealth = 85;
  const dealScore = FMF.dealScore({
    discountDepth,
    retailerTrust: r.trust,
    stockHealth,
  });

  return {
    ...f,
    retailerObj: r,
    delta7,
    delta7Pct: f.landed > 0 ? (delta7 / f.landed) * 100 : 0,
    spark30,
    low30,
    high30,
    atLow,
    savings: Math.max(0, f.msrp - f.landed),
    pctOff: ((f.msrp - f.landed) / f.msrp) * 100,
    discountDepth,
    dealScore,
  };
}

/* ──────────────────────────────────────────────────────────────────────
   Sparkline — 30-day landed-price mini chart.
   Inline SVG. Green tint if the trend is down (good for the reader).
   Oxblood tint if the trend is up. Neutral if flat.
   Endpoint dot marks today's observation.
   ────────────────────────────────────────────────────────────────────── */
function Sparkline({ data, w = 140, h = 32, trend = 0 }) {
  if (!data || data.length < 2) return null;
  const min = Math.min(...data);
  const max = Math.max(...data);
  const range = Math.max(1, max - min);
  const dx = w / (data.length - 1);
  const pts = data.map((v, i) => [
    +(i * dx).toFixed(2),
    +(h - ((v - min) / range) * (h - 4) - 2).toFixed(2),
  ]);
  const path = pts.map((p, i) => (i === 0 ? `M ${p[0]},${p[1]}` : `L ${p[0]},${p[1]}`)).join(" ");
  // Area fill closes back along the baseline.
  const areaPath = `${path} L ${w},${h} L 0,${h} Z`;
  const color =
    trend < -0.3 ? "var(--sig-buy)" :
    trend > 0.3  ? "var(--oxblood)" :
                   "var(--ink-mute)";
  const fill =
    trend < -0.3 ? "color-mix(in oklab, var(--sig-buy) 14%, transparent)" :
    trend > 0.3  ? "color-mix(in oklab, var(--oxblood) 12%, transparent)" :
                   "color-mix(in oklab, var(--ink-mute) 10%, transparent)";
  const last = pts[pts.length - 1];
  return (
    <svg viewBox={`0 0 ${w} ${h}`} width={w} height={h} aria-hidden="true"
         style={{ display: "block", overflow: "visible" }}>
      <path d={areaPath} fill={fill} stroke="none" />
      <path d={path} fill="none" stroke={color} strokeWidth="1.4" strokeLinejoin="round" strokeLinecap="round" />
      <circle cx={last[0]} cy={last[1]} r="2.2" fill={color} />
    </svg>
  );
}

/* ──────────────────────────────────────────────────────────────────────
   Δ — colored weekly delta value + sub-label.
   ────────────────────────────────────────────────────────────────────── */
function DeltaCell({ d, pct }) {
  const color =
    d < 0 ? "var(--sig-buy)" :
    d > 0 ? "var(--oxblood-ink)" :
            "var(--ink-mute)";
  return (
    <div className="mono" style={{ textAlign: "right" }}>
      <div style={{ fontSize: 15, letterSpacing: "-0.01em", color }}>
        {d === 0 ? "—" : FMF.fmtUsd(d, { signed: true })}
      </div>
      <div className="body-xs" style={{ color: "var(--ink-mute)", marginTop: 2 }}>
        {d === 0 ? "flat 7d" : `${pct >= 0 ? "+" : "−"}${Math.abs(pct).toFixed(1)}% · 7d`}
      </div>
    </div>
  );
}

/* ══════════════════════════════════════════════════════════════════════
   Drops — the page component.
   ══════════════════════════════════════════════════════════════════════ */
const DROPS_PAGE_SIZE = 50;

function Drops() {
  // Use the full expanded catalog (curated 12 + doevent 2000) so the deal
  // tracker is catalog-wide. Expansion rows without real price feeds get
  // seed prices via seedPrice() inside enrichDrop.
  //
  // Shop-URL ethic (2026-04-21): only rows with a verified retailer PDP
  // reach the renderer. Search fallbacks ("Find at X") are banned
  // site-wide — see FMF.shopUrl. A drop without a direct product URL is
  // dropped entirely, not demoted to a search landing.
  const { enriched, catalogSize, pdpCount } = useMemoD(() => {
    const raw = (FMF.CATALOG_ALL || FMF.CATALOG || []).map(enrichDrop);
    let pdpCount = 0;
    const verified = raw.filter(it => {
      const offer = (FMF.OFFERS || []).find(
        o => o.fragranceSlug === it.slug && o.retailerId === it.retailer
      );
      if (offer?.pdpUrl) it.pdpUrl = offer.pdpUrl;
      const shop = FMF.shopUrl(it.retailerObj, it, { pdpUrl: offer?.pdpUrl });
      if (shop.tier !== "pdp") return false;
      pdpCount++;
      return true;
    });
    return { enriched: verified, catalogSize: raw.length, pdpCount };
  }, []);

  // Week label — current week, printed in editorial mono.
  const weekLabel = useMemoD(() => {
    const d = new Date();
    const end = new Date(d);
    const start = new Date(d); start.setDate(d.getDate() - 6);
    const fmt = (dt) => dt.toLocaleDateString("en-US", { month: "short", day: "numeric" });
    return `${fmt(start)} – ${fmt(end)}, ${end.getFullYear()}`;
  }, []);

  // ── Search + sort + filter state ────────────────────────────────────
  const [query, setQuery] = useStateD("");
  const [sort, setSort]   = useStateD("delta");   // delta | depth | score
  const [filter, setFilt] = useStateD("all");     // all | buy | sub250 | atlow | niche | designer
  const [page, setPage]   = useStateD(0);         // zero-indexed page

  // Reset to page 0 whenever filter/sort/query changes (otherwise a 42-page
  // view can look empty mid-filter because the user is on page 10).
  React.useEffect(() => { setPage(0); }, [query, sort, filter]);

  // Filter pipeline: text query → category filter → sort
  const filtered = useMemoD(() => {
    const needle = query.trim().toLowerCase();
    let list = enriched;
    if (needle) {
      list = list.filter(f => {
        const hay = [
          f.brand, f.name, f.family,
          (f.perfumers || []).join(" "),
        ].join(" ").toLowerCase();
        return hay.includes(needle);
      });
    }
    switch (filter) {
      case "buy":      return list.filter(x => x.signal === "buy");
      case "sub250":   return list.filter(x => x.landed < 250);
      case "atlow":    return list.filter(x => x.atLow);
      case "niche":    return list.filter(x => !isDesignerFragrance(x));
      case "designer": return list.filter(x => isDesignerFragrance(x));
      default:         return list;
    }
  }, [enriched, query, filter]);

  const sorted = useMemoD(() => {
    const copy = filtered.slice();
    switch (sort) {
      case "delta":      copy.sort((a, b) => a.delta7 - b.delta7); break;
      case "score":      copy.sort((a, b) => b.dealScore - a.dealScore); break;
      case "price-asc":  copy.sort((a, b) => a.landed - b.landed); break;
      case "price-desc": copy.sort((a, b) => b.landed - a.landed); break;
    }
    return copy;
  }, [filtered, sort]);

  // Pagination — 50 rows per page.
  const totalPages = Math.max(1, Math.ceil(sorted.length / DROPS_PAGE_SIZE));
  const currentPage = Math.min(page, totalPages - 1);
  const pageStart = currentPage * DROPS_PAGE_SIZE;
  const pageEnd   = Math.min(pageStart + DROPS_PAGE_SIZE, sorted.length);
  const paginated = useMemoD(() => sorted.slice(pageStart, pageEnd), [sorted, pageStart, pageEnd]);

  // Honest meter: every rendered drop lands on the retailer's product
  // page. Rows without a verified direct URL are hidden — we do not
  // render search fallbacks, see FMF.shopUrl.
  const hiddenCount = catalogSize - enriched.length;

  return (
    <main>
      <DropsTopStrip weekLabel={weekLabel} />
      <DropsControls
        query={query} setQuery={setQuery}
        sort={sort} setSort={setSort}
        filter={filter} setFilt={setFilt}
        showing={sorted.length}
        total={enriched.length}
        pageStart={pageStart}
        pageEnd={pageEnd}
      />
      <section className="container" style={{ padding: "0 32px 18px" }}>
        <div className="mono body-xs" style={{
          color: "var(--ink-soft)", letterSpacing: "0.03em", lineHeight: 1.6,
          padding: "14px 18px", background: "var(--paper-deep)",
          borderLeft: "2px solid var(--ink)", maxWidth: 720,
        }}>
          <strong style={{ color: "var(--ink)" }}>{FMF.fmtInt(pdpCount)}</strong> drops — every
          one goes directly to the retailer's product page (<em>Shop now</em>). No
          search landings, no &ldquo;Find at X&rdquo; fallbacks.
          {hiddenCount > 0 && <> · <span style={{ color: "var(--ink-mute)" }}>{FMF.fmtInt(hiddenCount)} hidden (no verified product URL yet)</span></>}
          . No broken URLs, no fabricated links.{" "}
          <a href="#/refusals#direct-links">Why.</a>
        </div>
      </section>
      {sorted.length === 0 ? (
        <section className="container" style={{ padding: "48px 32px 80px" }}>
          <div className="fraun" style={{ fontSize: "clamp(24px, 2.6vw, 34px)", letterSpacing: "-0.015em", lineHeight: 1.2, marginBottom: 14 }}>
            No <span className="fraun-itl">verified drops</span> match this filter.
          </div>
          <div className="mono body-s" style={{ color: "var(--ink-soft)", maxWidth: 560, lineHeight: 1.6 }}>
            Drops only appear here when we have a direct product URL for the retailer —
            no search fallbacks, no guesses. Try clearing filters, or explore a
            fragrance directly: <a href="#/fragrance/creed-aventus-2010">Aventus</a>.
          </div>
        </section>
      ) : (
        <>
          <DropsLedger rows={paginated} offset={pageStart} />
          {totalPages > 1 && (
            <Pagination current={currentPage} total={totalPages} onGo={setPage}
                        pageStart={pageStart} pageEnd={pageEnd} itemCount={sorted.length} />
          )}
        </>
      )}
      <WhatsNotADrop />
      <DropsAlertCTA />
      <DropsFoot />
    </main>
  );
}

/* Pagination — first / prev / numeric page buttons / next / last, plus a
   live "showing N-M of X" label on the left. Keyboard + tap target safe. */
function Pagination({ current, total, onGo, pageStart, pageEnd, itemCount }) {
  // Build the page-number strip. For few pages, show all; for many,
  // show first, last, and a window of ±2 around current, with ellipses.
  const window = new Set([0, total - 1, current, current - 1, current + 1, current - 2, current + 2]);
  const visible = [...window].filter(p => p >= 0 && p < total).sort((a, b) => a - b);
  const withEllipses = [];
  for (let i = 0; i < visible.length; i++) {
    if (i > 0 && visible[i] - visible[i - 1] > 1) withEllipses.push("…");
    withEllipses.push(visible[i]);
  }
  return (
    <section className="container drops-paginate" aria-label="Pagination">
      <div className="drops-paginate__meta mono body-xs">
        <span style={{ color: "var(--ink)" }}>
          {pageStart + 1}–{pageEnd}
        </span>
        <span style={{ color: "var(--ink-mute)" }}> of {FMF.fmtInt(itemCount)} fragrances · page {current + 1} of {total}</span>
      </div>
      <div className="drops-paginate__nav">
        <button onClick={() => onGo(Math.max(0, current - 1))} disabled={current === 0} aria-label="Previous page">← Prev</button>
        <div className="drops-paginate__pages">
          {withEllipses.map((p, i) => p === "…"
            ? <span key={`e${i}`} className="drops-paginate__ellipsis" aria-hidden="true">…</span>
            : <button key={p}
                      onClick={() => onGo(p)}
                      aria-label={`Go to page ${p + 1}`}
                      aria-current={p === current ? "page" : undefined}
                      className={p === current ? "is-on" : ""}>{p + 1}</button>
          )}
        </div>
        <button onClick={() => onGo(Math.min(total - 1, current + 1))} disabled={current === total - 1} aria-label="Next page">Next →</button>
      </div>
    </section>
  );
}

/* Top strip — single-line page identifier + snapshot meta. */
function DropsTopStrip({ weekLabel }) {
  return (
    <>
      <section className="container" style={{ paddingTop: 28, paddingBottom: 14 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", flexWrap: "wrap", gap: 14 }}>
          <div className="kicker">Latest deals · week of {weekLabel}</div>
          <div className="mono body-xs" style={{ color: "var(--ink-mute)", letterSpacing: "0.04em" }}>
            Snapshot 09:14 ET<Cite n={8} check="verified" /> · {FMF.RETAILERS.length} retailers polled · every 4h · no affiliate weighting<Cite n={2} check="independent" />
          </div>
        </div>
      </section>
      <hr className="hairline-k" />
    </>
  );
}

/* ══════════════════════════════════════════════════════════════════════
   CONTROLS — search, sort, filter. Deterministic and affiliate-free.
   ══════════════════════════════════════════════════════════════════════ */
function DropsControls({ query, setQuery, sort, setSort, filter, setFilt, showing, total, pageStart, pageEnd }) {
  const sorts = [
    { v: "delta",     l: "Biggest fall" },
    { v: "score",     l: "Deal score" },
    { v: "price-asc", l: "Price ↑" },
    { v: "price-desc",l: "Price ↓" },
  ];
  const filters = [
    { v: "all",      l: `All · ${total}` },
    { v: "designer", l: "Designer" },
    { v: "niche",    l: "Niche boutique" },
    { v: "sub250",   l: "Under $250" },
    { v: "atlow",    l: "At 30d low" },
  ];
  return (
    <section className="container drops-controls">
      <label className="drops-search">
        <svg className="drops-search__icon" viewBox="0 0 16 16" width="14" height="14" aria-hidden="true">
          <circle cx="7" cy="7" r="5" fill="none" stroke="currentColor" strokeWidth="1.4" />
          <line x1="11" y1="11" x2="14" y2="14" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
        </svg>
        <input
          type="search"
          className="drops-search__input"
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="Search by house or fragrance — Creed, Aventus, MFK, Tom Ford…"
          aria-label="Search fragrances by house or name"
        />
        {query && (
          <button type="button" className="drops-search__clear" onClick={() => setQuery("")}
                  aria-label="Clear search">clear</button>
        )}
      </label>
      <div className="drops-controls__row">
        <div className="drops-controls__group">
          <span className="kicker">Sort</span>
          <div className="drops-pills">
            {sorts.map(s => (
              <button key={s.v}
                      className={`drops-pill ${sort === s.v ? "is-on" : ""}`}
                      onClick={() => setSort(s.v)}>
                {s.l}
              </button>
            ))}
          </div>
        </div>
        <div className="drops-controls__group">
          <span className="kicker">Filter</span>
          <div className="drops-pills">
            {filters.map(f => (
              <button key={f.v}
                      className={`drops-pill ${filter === f.v ? "is-on" : ""}`}
                      onClick={() => setFilt(f.v)}>
                {f.l}
              </button>
            ))}
          </div>
        </div>
      </div>
      <div className="mono body-xs drops-controls__tally">
        Showing{" "}
        {(typeof pageStart === "number" && typeof pageEnd === "number" && showing > (pageEnd - pageStart))
          ? <><strong style={{ color: "var(--ink)" }}>{pageStart + 1}–{pageEnd}</strong> of {FMF.fmtInt(showing)} · from {FMF.fmtInt(total)} tracked</>
          : <><strong style={{ color: "var(--ink)" }}>{FMF.fmtInt(showing)}</strong> of {FMF.fmtInt(total)} fragrances</>
        }
        {query && <> matching &ldquo;<strong style={{ color: "var(--ink)" }}>{query}</strong>&rdquo;</>}
      </div>
    </section>
  );
}

/* ══════════════════════════════════════════════════════════════════════
   LEDGER — the full ranked list. Wide grid on desktop; collapses on narrow.
   ══════════════════════════════════════════════════════════════════════ */
function DropsLedger({ rows, offset = 0 }) {
  if (rows.length === 0) {
    return (
      <section className="container" style={{ padding: "40px 32px 80px" }}>
        <div className="drops-empty">
          <div className="fraun" style={{ fontSize: 28, lineHeight: 1.2 }}>
            Nothing matches that filter.
          </div>
          <div className="mono body-xs" style={{ color: "var(--ink-mute)", marginTop: 10 }}>
            Try <em>All</em>, or open the full <a href="#/browse" style={{ borderBottom: "1px solid currentColor" }}>catalog</a>.
          </div>
        </div>
      </section>
    );
  }
  return (
    <section className="container drops-ledger">
      <div className="drops-ledger__head mono body-xs">
        <span>Rank</span>
        <span aria-hidden="true" />{/* bottle column — no label */}
        <span>Fragrance</span>
        <span>30-day trail</span>
        <span style={{ textAlign: "right" }}>MSRP</span>
        <span style={{ textAlign: "right" }}>Landed</span>
        <span style={{ textAlign: "right" }}>Δ 7d</span>
        <span style={{ textAlign: "right" }}>Action</span>
      </div>
      <div className="drops-ledger__body">
        {rows.map((it, i) => <DropsRow key={it.slug} it={it} i={i + offset} />)}
      </div>
    </section>
  );
}

function DropsRow({ it, i }) {
  // Row is no longer a single link — the whole card has two explicit actions:
  // "Shop now" (verified retailer PDP, new tab) and "See fragrance" (detail).
  // `it.pdpUrl` is hydrated onto the row by the parent <Drops> memo from
  // FMF.OFFERS, so the resolver sees a verified URL and returns tier: pdp.
  // Non-verified rows never reach this component — the parent filter
  // removes them upstream.
  const shop = FMF.shopUrl(it.retailerObj, it, { pdpUrl: it.pdpUrl });
  return (
    <div className="drops-row">
      <div className="mono body-xs drops-row__rank">{String(i + 1).padStart(2, "0")}</div>
      <a href={`#/fragrance/${it.slug}`} className="drops-row__bottle" aria-label={`See ${it.brand} ${it.name}`}>
        <Bottle slug={it.slug} label={it.name} showLabel={false}
                style={{ width: "100%", height: "100%", padding: "6% 12%" }} />
      </a>
      <a href={`#/fragrance/${it.slug}`} className="drops-row__name">
        <div className="fraun drops-row__frag">
          {it.brand} <span style={{ color: "var(--ink-soft)" }}>·</span> <span className="fraun-itl">{it.name}</span>
        </div>
        <div className="mono body-xs drops-row__meta">
          {(it.size || it.sizeLabel || "100ml")} · {it.year} · {it.retailerObj.name.toLowerCase()}
          {it.atLow && <span className="chip chip-gold drops-row__atlow">at 30d low</span>}
          {it._seededPrice && <span className="drops-row__seed-tag" title="Landed price is editorial seed — awaiting real retailer feed.">seed</span>}
        </div>
      </a>
      <div className="drops-row__spark">
        <Sparkline data={it.spark30} w={140} h={30} trend={it.delta7} />
      </div>
      <div className="mono body-s drops-row__msrp">
        <div className="strike">{FMF.fmtUsdNoCents(it.msrp)}</div>
        <div className="body-xs" style={{ color: "var(--ink-mute)", marginTop: 2 }}>
          −{it.pctOff.toFixed(0)}%
        </div>
      </div>
      <div className="mono drops-row__landed">
        <div>{FMF.fmtUsd(it.landed)}</div>
        <div className="body-xs" style={{ color: "var(--ink-mute)", marginTop: 2 }}>landed</div>
      </div>
      <div className="drops-row__delta">
        <DeltaCell d={it.delta7} pct={it.delta7Pct} />
      </div>
      <div className="drops-row__actions">
        <a href={shop.url}
           target="_blank" rel="noopener noreferrer"
           data-shop-tier={shop.tier} data-shop-retailer={it.retailerObj?.id}
           className={`drops-row__shop drops-row__shop--${shop.tier}`}
           aria-label={`Shop ${it.brand} ${it.name} at ${it.retailerObj.name}`}>
          Shop now
          <svg width="9" height="9" viewBox="0 0 10 10" aria-hidden="true" style={{ marginLeft: 6 }}>
            <path d="M2 8 L8 2 M3 2 L8 2 L8 7" stroke="currentColor" strokeWidth="1.4" fill="none" strokeLinecap="round" strokeLinejoin="round" />
          </svg>
        </a>
        <a href={`#/fragrance/${it.slug}`} className="drops-row__see">
          See fragrance
        </a>
      </div>
    </div>
  );
}

/* ══════════════════════════════════════════════════════════════════════
   WHAT'S NOT A DROP — editorial panel. The stance, signed.
   ══════════════════════════════════════════════════════════════════════ */
function WhatsNotADrop() {
  const items = [
    {
      k: "No countdown timers",
      body: "A price is either the landed price right now or it isn't. We do not animate urgency. If a coupon expires, the date is printed in plain mono.",
      cite: 5, check: "static",
    },
    {
      k: "No 'X people viewing'",
      body: "We do not synthesize social proof. We do not show fake inventory counters. A bottle is in stock or it isn't — the retailer row says so plainly.",
      cite: 6, check: "quiet",
    },
    {
      k: "No paid rank",
      body: "Retailers cannot pay to appear higher. Rank is a function of landed price and published trust score. Commission rate never enters ranking code.",
      cite: 2, check: "independent",
    },
    {
      k: "No invented MSRP",
      body: "Discount depth is computed against the MSRP on the brand's own site — not a number a retailer pulled from its database to make a sale look deeper.",
      cite: 3, check: "verified",
    },
  ];
  return (
    <section className="drops-not">
      <div className="container drops-not__inner">
        <div className="drops-not__head">
          <div className="kicker" style={{ color: "var(--oxblood-ink)" }}>Refusals · in force on this page</div>
          <h2 className="fraun drops-not__h">
            What is <span className="fraun-itl">not</span> a drop.
          </h2>
          <p className="drops-not__sub">
            Retail pages get paid to make you panic. We don't. Below: the four refusals that
            govern every number on this surface — signed, dated, enforced in code. We've broken
            one of them exactly once.<Cite n={5} check="static" /> <a href="#/breaches" className="drops-not__breaches">See the breach →</a>
          </p>
        </div>
        <div className="drops-not__grid">
          {items.map((it, i) => {
            const r = FMF.refusalByN?.(it.cite);
            return (
              <div key={i} className="drops-not__card">
                <div className="mono body-xs drops-not__num">§{it.cite}</div>
                <div className="fraun drops-not__k">{it.k}</div>
                <p className="drops-not__body">{it.body}</p>
                <a className="mono body-xs drops-not__link" href={r ? `#/refusals#${r.id}` : "#/refusals"}>
                  Read the refusal →
                </a>
              </div>
            );
          })}
        </div>
      </div>
    </section>
  );
}

/* ══════════════════════════════════════════════════════════════════════
   ALERT CTA — the one "save" affordance on the page. No email gate.
   ══════════════════════════════════════════════════════════════════════ */
function DropsAlertCTA() {
  return (
    <section className="drops-alert">
      <div className="container drops-alert__inner">
        <div className="drops-alert__body">
          <div className="kicker">Price alerts · local-only · free<Cite n={12} check="free" /></div>
          <h2 className="fraun drops-alert__h">
            Watching one in particular? <span className="fraun-itl">Set an alert.</span>
          </h2>
          <p className="drops-alert__sub">
            Pick a fragrance, set a target landed price. We will not email, notify, or track you
            in v0.1 — the alert lives in your browser. When backend alerts land, every saved
            target is flushed to the server at that moment, visibly. Nothing happens silently.
          </p>
          <div className="drops-alert__ctas">
            <a href="#/browse" className="drops-alert__cta drops-alert__cta--primary">
              Browse the catalog
              <svg width="10" height="10" viewBox="0 0 10 10" aria-hidden="true" style={{ marginLeft: 8 }}>
                <path d="M1 5 H9 M5 1 L9 5 L5 9" stroke="currentColor" strokeWidth="1.4" fill="none" strokeLinecap="round" strokeLinejoin="round" />
              </svg>
            </a>
            <a href="#/wishlist" className="drops-alert__cta">
              See your want list
            </a>
          </div>
        </div>
        <aside className="drops-alert__aside mono body-xs">
          <div className="drops-alert__aside-h">On this page, right now</div>
          <ul className="drops-alert__aside-ul">
            <li><strong>{FMF.RETAILERS.length}</strong> retailers polled every 4 hours</li>
            <li><strong>{FMF.CATALOG.length}</strong> fragrances tracked end-to-end</li>
            <li><strong>0</strong> countdown timers, popups, or tracking pixels</li>
            <li><strong>0</strong> dollars ever paid for placement<Cite n={1} check="placement" /></li>
          </ul>
        </aside>
      </div>
    </section>
  );
}

/* ══════════════════════════════════════════════════════════════════════
   FOOT — method + signed + link to breaches ledger.
   ══════════════════════════════════════════════════════════════════════ */
function DropsFoot() {
  return (
    <>
      <hr className="hairline" />
      <section className="container drops-foot">
        <div className="drops-foot__grid">
          <div>
            <div className="kicker">Method · the math on this page</div>
            <p className="mono body-s drops-foot__body">
              Landed = sticker − coupon + shipping + tax. 7-day delta is the difference between
              today's landed and the observation seven days ago, per-fragrance. 30-day sparklines
              plot the trailing 30 daily lows. "Deal score" is the published formula
              <span className="mono" style={{ color: "var(--ink)" }}> 0.40·depth + 0.25·trust + 0.20·stock + 0.15·70</span>.
              Nothing on this page consults affiliate commission.<Cite n={2} check="independent" />
            </p>
          </div>
          <div>
            <div className="kicker">Signed · dated · auditable</div>
            <p className="mono body-s drops-foot__body">
              This ledger is edited in Brooklyn. Every price shown carries a per-retailer
              provenance card: fetched timestamp, source URL, HTTP status, parser version, signer,
              and the §N check that passed. Click a landed price on any detail page to read it.
              If a price is older than 6 hours, the row is marked stale and excluded from rank.<Cite n={8} check="verified" />
            </p>
            <div style={{ display: "flex", gap: 18, marginTop: 16, flexWrap: "wrap" }}>
              <a href="#/refusals" className="mono body-xs drops-foot__link">The 12 refusals →</a>
              <a href="#/breaches" className="mono body-xs drops-foot__link">The breach ledger →</a>
              <a href="#/about" className="mono body-xs drops-foot__link">Manifesto →</a>
            </div>
          </div>
        </div>
      </section>
    </>
  );
}

Object.assign(window, { Drops });
