// =============================================================
// page-rates.jsx — DAT lane-rate surface
//
// Public-facing visualization of our captured DAT market rates.
// Powers the "your lane vs market" sales pitch and the
// carrier-revenue-maximization engine. Reads from dat_lane_rates,
// dat_lane_heat (view), dat_lane_asymmetry (view), and dat_carriers.
// =============================================================

const {
  useState: useStateRt,
  useEffect: useEffectRt,
  useMemo: useMemoRt,
} = React;

// City → lat/lng for the lane endpoints we've captured. Hardcoded since
// these are all well-known major US cities; new lanes added later may
// need to extend this map.
const CITY_COORDS = {
  "los angeles,ca": [34.0522, -118.2437],
  "chicago,il":     [41.8781, -87.6298],
  "dallas,tx":      [32.7767, -96.7970],
  "atlanta,ga":     [33.7490, -84.3880],
  "new york,ny":    [40.7128, -74.0060],
  "mcallen,tx":     [26.2034, -98.2300],
  "salinas,ca":     [36.6777, -121.6555],
  "yakima,wa":      [46.6021, -120.5059],
  "lakeland,fl":    [28.0395, -81.9498],
  "detroit,mi":     [42.3314, -83.0458],
  "houston,tx":     [29.7604, -95.3698],
};

function coordsFor(city, state) {
  const k = `${(city || "").toLowerCase().trim()},${(state || "").toLowerCase().trim()}`;
  return CITY_COORDS[k];
}

// US lane map. Each lane = a line from origin coords to dest coords,
// colored by heat band (red=hot, gray=normal, blue=cold), thickness
// scaled to $/mi. Hover/click reveals the rate + load count.
function RatesUSMap({ lanes }) {
  const containerRef = React.useRef(null);
  const mapRef = React.useRef(null);
  const layerRef = React.useRef(null);

  React.useEffect(() => {
    if (!containerRef.current || !window.L) return;
    if (!mapRef.current) {
      mapRef.current = window.L.map(containerRef.current, {
        center: [38.5, -96], zoom: 4, scrollWheelZoom: false,
        attributionControl: true,
      });
      window.L.tileLayer("https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png", {
        attribution: "© OpenStreetMap contributors © CARTO",
        subdomains: "abcd", maxZoom: 18,
      }).addTo(mapRef.current);
    }
    if (layerRef.current) {
      layerRef.current.remove();
    }
    const grp = window.L.layerGroup();
    const cityPins = {};

    for (const l of lanes) {
      const a = coordsFor(l.origin_city, l.origin_state);
      const b = coordsFor(l.dest_city, l.dest_state);
      if (!a || !b) continue;
      const rate = parseFloat(l.lane_rate_per_mi);
      const heat = l.heat_band;
      const color =
        heat === "hot"  ? "#c0392b" :
        heat === "cold" ? "#2980b9" : "#7f8c8d";
      const weight = Math.max(2, Math.min(8, rate * 1.5));
      const opacity = heat === "hot" ? 0.85 : heat === "cold" ? 0.55 : 0.7;

      // Curve the line slightly for visual separation
      const midLat = (a[0] + b[0]) / 2;
      const midLng = (a[1] + b[1]) / 2;
      const dLat = b[0] - a[0], dLng = b[1] - a[1];
      const dist = Math.sqrt(dLat*dLat + dLng*dLng);
      const offsetSize = dist * 0.08;
      const ctrl = [midLat + offsetSize, midLng - offsetSize];

      const line = window.L.polyline([a, ctrl, b], {
        color, weight, opacity,
        smoothFactor: 1, lineCap: "round",
      });
      const equip = (l.equipment_type || "").toLowerCase();
      const equipIcon = equip === "reefer" ? "❄️" : "🚛";
      const html = `
        <div style="font-family:inherit;font-size:13px;line-height:1.4;min-width:200px">
          <div style="font-family:var(--font-serif);font-size:15px;font-weight:600">
            ${l.origin_city}, ${l.origin_state} → ${l.dest_city}, ${l.dest_state}
          </div>
          <div style="margin-top:6px;color:#666;font-family:var(--font-mono);font-size:11px;letter-spacing:0.06em">
            ${equipIcon} ${l.equipment_type || ""} · ${l.distance_mi || "?"} mi
          </div>
          <div style="margin-top:8px;font-family:var(--font-serif);font-size:24px;font-weight:600">
            $${rate.toFixed(2)}/mi
          </div>
          <div style="font-family:var(--font-mono);font-size:11px;color:#555">
            $${parseFloat(l.lane_rate_total).toLocaleString()} total · ${l.load_count + l.similar_count} loads
          </div>
          <div style="margin-top:8px;display:inline-block;padding:2px 8px;border-radius:3px;font-family:var(--font-mono);font-size:10px;letter-spacing:0.16em;font-weight:700;background:${color};color:#fff">
            ${heat ? heat.toUpperCase() : ""}
          </div>
        </div>`;
      line.bindPopup(html);
      grp.addLayer(line);

      // City endpoints — small circle markers
      for (const [coords, city, state] of [[a, l.origin_city, l.origin_state], [b, l.dest_city, l.dest_state]]) {
        const key = `${city},${state}`;
        if (!cityPins[key]) {
          cityPins[key] = window.L.circleMarker(coords, {
            radius: 5, color: "#222", weight: 1.5,
            fillColor: "#fff", fillOpacity: 1,
          }).bindTooltip(`<strong>${city}, ${state}</strong>`, { permanent: false });
          grp.addLayer(cityPins[key]);
        }
      }
    }
    grp.addTo(mapRef.current);
    layerRef.current = grp;
  }, [lanes]);

  return (
    <div style={{ width: "100%", height: 480, borderRadius: 8, overflow: "hidden", border: "1px solid var(--rule)" }}>
      <div ref={containerRef} style={{ width: "100%", height: "100%" }} />
    </div>
  );
}

function RatesPage({ onNav }) {
  const [lanes, setLanes]       = useStateRt([]);
  const [heat, setHeat]         = useStateRt([]);
  const [asym, setAsym]         = useStateRt([]);
  const [carriers, setCarriers] = useStateRt(0);
  const [ams, setAms]           = useStateRt([]);
  const [loading, setLoading]   = useStateRt(true);
  const [equipFilter, setEquipFilter] = useStateRt("all");

  useEffectRt(() => {
    let alive = true;
    if (!window.SI_DB || !window.SI_DB.raw) return;
    (async () => {
      try {
        const [l, h, a, c, u] = await Promise.all([
          window.SI_DB.raw.select("dat_lane_rates",
            "select=*&order=lane_rate_per_mi.desc&limit=200"),
          window.SI_DB.raw.select("dat_lane_heat",
            "select=*&order=lane_rate_per_mi.desc&limit=200"),
          window.SI_DB.raw.select("dat_lane_asymmetry",
            "select=*&order=pct_discount.asc&limit=50"),
          window.SI_DB.raw.count("dat_carriers"),
          window.SI_DB.raw.select("usda_ams_truck_rates_recent",
            "select=origin_market,commodity,destination,observed_date,rate_low,rate_high,rate_mostly_low,rate_mostly_high,trend&order=observed_date.desc&limit=400")
            .catch(() => []),
        ]);
        if (!alive) return;
        setLanes(l || []); setHeat(h || []); setAsym(a || []);
        setCarriers(c || 0);
        setAms(Array.isArray(u) ? u : []);
      } catch {}
      finally { if (alive) setLoading(false); }
    })();
    return () => { alive = false; };
  }, []);

  // Group recent USDA AMS rows by origin → list of (commodity, rate range)
  const amsByOrigin = useMemoRt(() => {
    const out = {};
    for (const r of ams) {
      if (!r.origin_market) continue;
      const key = r.origin_market;
      (out[key] = out[key] || []).push(r);
    }
    // Keep top 6 commodities per origin (by rate_mostly_high desc, then high)
    for (const k of Object.keys(out)) {
      out[k].sort((x, y) => (parseFloat(y.rate_mostly_high || y.rate_high || 0)
                            - parseFloat(x.rate_mostly_high || x.rate_high || 0)));
      out[k] = out[k].slice(0, 6);
    }
    return out;
  }, [ams]);

  const amsOriginKeys = useMemoRt(() => {
    const keys = Object.keys(amsByOrigin);
    keys.sort();
    return keys;
  }, [amsByOrigin]);

  const equipTypes = useMemoRt(() => {
    const s = new Set(lanes.map(l => l.equipment_type));
    return ["all", ...[...s].sort()];
  }, [lanes]);

  const visibleHeat = useMemoRt(() => (
    equipFilter === "all" ? heat : heat.filter(h => h.equipment_type === equipFilter)
  ), [heat, equipFilter]);

  const stats = useMemoRt(() => {
    if (lanes.length === 0) return { count: 0, range: "—", reefer: 0, van: 0 };
    const rates = lanes.map(l => parseFloat(l.lane_rate_per_mi));
    const min = Math.min(...rates), max = Math.max(...rates);
    return {
      count: lanes.length,
      range: `$${min.toFixed(2)}–$${max.toFixed(2)}/mi`,
      reefer: lanes.filter(l => l.equipment_type === "Reefer").length,
      van: lanes.filter(l => l.equipment_type === "Van").length,
    };
  }, [lanes]);

  return (
    <div className="rt-page">
      <style>{`
        .rt-page { background: var(--paper, #fff); min-height: 100vh; }
        .rt-hero { max-width: 1180px; margin: 0 auto; padding: 64px 24px 32px; }
        .rt-eyebrow {
          font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.32em;
          text-transform: uppercase; color: var(--ink-soft); margin-bottom: 18px;
          display: inline-flex; align-items: center; gap: 10px;
        }
        .rt-eyebrow .dot { width: 8px; height: 8px; border-radius: 50%;
          background: oklch(0.55 0.18 145); animation: rt-pulse 1.6s infinite; }
        @keyframes rt-pulse { 0%,100%{opacity:1} 50%{opacity:0.4} }
        .rt-h1 {
          font-family: var(--font-serif); font-size: 56px; line-height: 1.04;
          letter-spacing: -0.025em; margin: 0; font-weight: 500; color: var(--ink);
          max-width: 920px;
        }
        @media (max-width: 700px) { .rt-h1 { font-size: 36px; } }
        .rt-sub {
          margin-top: 18px; font-size: 18px; line-height: 1.5; color: var(--ink-soft);
          max-width: 780px;
        }

        .rt-stats {
          margin-top: 40px; padding: 24px 0; border-top: 1px solid var(--rule);
          border-bottom: 1px solid var(--rule);
          display: grid; grid-template-columns: repeat(4, 1fr); gap: 0;
        }
        @media (max-width: 720px) { .rt-stats { grid-template-columns: repeat(2, 1fr); } }
        .rt-stat { padding: 0 24px; border-right: 1px solid var(--rule); }
        .rt-stat:first-child { padding-left: 0; }
        .rt-stat:last-child { border-right: 0; padding-right: 0; }
        @media (max-width: 720px) {
          .rt-stat:nth-child(2n) { border-right: 0; }
          .rt-stat:nth-child(2n+1) { padding-left: 0; }
        }
        .rt-stat-num {
          font-family: var(--font-serif); font-size: 32px; line-height: 1;
          letter-spacing: -0.02em; font-weight: 600; color: var(--ink);
        }
        .rt-stat-l {
          margin-top: 6px; font-family: var(--font-mono); font-size: 10px;
          letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-soft);
        }

        .rt-section { max-width: 1180px; margin: 40px auto; padding: 0 24px; }
        .rt-section-h {
          font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.32em;
          text-transform: uppercase; color: var(--ink-soft);
          padding-bottom: 14px; border-bottom: 1px solid var(--rule); margin-bottom: 20px;
          display: flex; justify-content: space-between; align-items: baseline; flex-wrap: wrap; gap: 12px;
        }

        .rt-pills { display: flex; gap: 8px; }
        .rt-pill {
          padding: 6px 12px; border: 1px solid var(--rule); border-radius: 999px;
          background: var(--paper); color: var(--ink-soft); cursor: pointer;
          font: inherit; font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase;
        }
        .rt-pill.active { background: var(--ink); color: #fff; border-color: var(--ink); }

        .rt-heat {
          display: grid; grid-template-columns: 1fr; gap: 0;
          border-top: 1px solid var(--rule);
        }
        .rt-heat-row {
          display: grid;
          grid-template-columns: 1fr 90px 80px 100px 1fr;
          gap: 16px; align-items: center;
          padding: 14px 0; border-bottom: 1px solid var(--rule);
          font-size: 14px;
        }
        @media (max-width: 880px) {
          .rt-heat-row { grid-template-columns: 1fr 80px 90px; }
          .rt-heat-row .rt-equip, .rt-heat-row .rt-bar { display: none; }
        }
        .rt-route {
          font-family: var(--font-serif); font-size: 17px; color: var(--ink);
          letter-spacing: -0.005em;
        }
        .rt-equip {
          font-family: var(--font-mono); font-size: 11px;
          color: var(--ink-soft); letter-spacing: 0.06em; text-transform: uppercase;
        }
        .rt-rate {
          font-family: var(--font-serif); font-size: 18px; font-weight: 600;
          color: var(--ink); text-align: right;
        }
        .rt-band {
          font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.16em;
          text-transform: uppercase; padding: 3px 10px; border-radius: 4px;
          font-weight: 700; text-align: center;
        }
        .rt-band-hot    { background: oklch(0.95 0.10 25);  color: oklch(0.40 0.20 25); }
        .rt-band-cold   { background: oklch(0.95 0.06 250); color: oklch(0.40 0.16 250); }
        .rt-band-normal { background: oklch(0.96 0.02 145); color: oklch(0.42 0.10 145); }
        .rt-bar {
          height: 8px; background: oklch(0.94 0.02 145); border-radius: 4px;
          position: relative; overflow: hidden;
        }
        .rt-bar-fill {
          position: absolute; top: 0; bottom: 0; left: 0;
          background: linear-gradient(90deg, oklch(0.55 0.16 145) 0%, oklch(0.55 0.18 25) 100%);
          border-radius: 4px;
        }

        .rt-asym-table {
          border-top: 1px solid var(--rule);
        }
        .rt-asym-row {
          display: grid;
          grid-template-columns: 1.4fr 80px 80px 80px 100px;
          gap: 16px; align-items: center;
          padding: 14px 0; border-bottom: 1px solid var(--rule);
          font-size: 14px;
        }
        @media (max-width: 760px) {
          .rt-asym-row { grid-template-columns: 1fr 70px 70px; gap: 8px; }
          .rt-asym-row .rt-asym-back, .rt-asym-row .rt-asym-spread { display: none; }
        }
        .rt-asym-pct {
          font-family: var(--font-serif); font-size: 18px; font-weight: 600;
          color: oklch(0.45 0.20 25); text-align: right;
        }
        .rt-asym-saved {
          font-family: var(--font-mono); font-size: 12px;
          color: var(--ink-soft); text-align: right;
        }
        .rt-asym-saved strong { color: oklch(0.45 0.18 145); font-size: 14px; }

        .rt-callout {
          margin: 32px auto; padding: 24px;
          background: oklch(0.97 0.03 145); border: 1px solid oklch(0.85 0.08 145);
          border-radius: 8px; max-width: 1180px;
        }
        .rt-callout-h {
          font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.22em;
          text-transform: uppercase; color: oklch(0.40 0.14 145); margin-bottom: 8px;
        }
        .rt-callout-t {
          font-family: var(--font-serif); font-size: 22px; letter-spacing: -0.01em;
          margin: 0 0 6px; color: var(--ink);
        }
        .rt-callout-s { color: var(--ink-soft); font-size: 14px; line-height: 1.5; margin: 0; }
      `}</style>

      <section className="rt-hero">
        <div className="rt-eyebrow"><span className="dot" /> Market rates · live</div>
        <h1 className="rt-h1">Live truckload market rates by lane.</h1>
        <p className="rt-sub">
          The board posts what freight is actually paying right now — every captured
          origin–destination pair, classified into hot / normal / cold bands against
          the equipment-type median. Two structural reads come out of the same data
          most desks only see one side of: <strong>headhaul-vs-backhaul asymmetry</strong>,
          computed for every paired lane (Dallas → LA pays one number; LA → Dallas pays
          another, and the gap is where margin lives), and a <strong>reefer premium</strong>
          quantified against the dry-van rate on identical geography, so cold-chain
          shippers can see the cost of temperature in dollars per mile rather than
          percentages off a quote.
        </p>
        <p className="rt-sub" style={{ marginTop: 12 }}>
          Use it to check a quote before you accept it. Use it to find the lane your
          equipment is underpriced on. Use it to call a customer back with a number you
          can defend. Refreshed continuously; the snapshot below is what the market
          looks like today.
        </p>

        <div className="rt-stats">
          <div className="rt-stat">
            <div className="rt-stat-num">{loading ? "…" : stats.count}</div>
            <div className="rt-stat-l">Lanes captured</div>
          </div>
          <div className="rt-stat">
            <div className="rt-stat-num">{loading ? "…" : stats.range}</div>
            <div className="rt-stat-l">$/mi range observed</div>
          </div>
          <div className="rt-stat">
            <div className="rt-stat-num">{loading ? "…" : stats.van + " · " + stats.reefer}</div>
            <div className="rt-stat-l">Van · Reefer lanes</div>
          </div>
          <div className="rt-stat">
            <div className="rt-stat-num">{loading ? "…" : carriers.toLocaleString()}</div>
            <div className="rt-stat-l">Carriers indexed</div>
          </div>
        </div>
      </section>

      {/* US map of lanes — visual proof of geographic + rate concentration */}
      <section className="rt-section">
        <div className="rt-section-h">
          <span>US lane map · click any lane</span>
          <span style={{ fontSize: 10, letterSpacing: "0.16em", display: "flex", gap: 12, flexWrap: "wrap" }}>
            <span><span style={{display:"inline-block",width:14,height:3,background:"#c0392b",marginRight:4,verticalAlign:"middle"}}/>Hot</span>
            <span><span style={{display:"inline-block",width:14,height:3,background:"#7f8c8d",marginRight:4,verticalAlign:"middle"}}/>Normal</span>
            <span><span style={{display:"inline-block",width:14,height:3,background:"#2980b9",marginRight:4,verticalAlign:"middle"}}/>Cold</span>
            <span style={{ color: "var(--ink-soft)" }}>· thickness = $/mi</span>
          </span>
        </div>
        {!loading && heat.length > 0 && <RatesUSMap lanes={heat} />}
        {!loading && heat.length === 0 && (
          <div style={{ padding: 24, textAlign: "center", fontSize: 13, color: "var(--ink-soft)", border: "1px solid var(--rule)", borderRadius: 6 }}>
            No lane data yet.
          </div>
        )}
      </section>

      {/* Lane heat */}
      <section className="rt-section">
        <div className="rt-section-h">
          <span>Lane heat · ranked by $/mi</span>
          <div className="rt-pills">
            {equipTypes.map(t => (
              <button key={t}
                className={`rt-pill ${equipFilter === t ? "active" : ""}`}
                onClick={() => setEquipFilter(t)}>
                {t === "all" ? "All" : t}
              </button>
            ))}
          </div>
        </div>
        <div className="rt-heat">
          {visibleHeat.length === 0 && !loading && (
            <div style={{padding:"32px 0", textAlign:"center", color:"var(--ink-soft)", fontSize:14}}>
              No lane data yet for this filter.
            </div>
          )}
          {visibleHeat.map(h => {
            const rate = parseFloat(h.lane_rate_per_mi);
            const pct  = parseFloat(h.pct_vs_median || 0);
            const fillPct = Math.min(100, Math.max(15, (rate / 5) * 100));  // 0-$5/mi visual scale
            // Resolve city names to metro-report IDs so a row links into the
            // canonical metro page. Returns null when the city isn't tracked.
            const cityIdFor = (name) => {
              if (!name || !window.SI_DATA?.ALL_CITIES) return null;
              const target = name.toLowerCase().trim();
              const hit = window.SI_DATA.ALL_CITIES.find(c => c.name.toLowerCase() === target);
              return hit ? hit.id : null;
            };
            const oCityId = cityIdFor(h.origin_city);
            const dCityId = cityIdFor(h.dest_city);
            const goCity = (id) => (e) => {
              e.stopPropagation(); e.preventDefault();
              if (!id) return;
              window.location.hash = `#/report/${id}`;
            };
            return (
              <div key={`${h.origin_city}-${h.dest_city}-${h.equipment_type}`} className="rt-heat-row">
                <div className="rt-route">
                  {oCityId
                    ? <a href={`#/report/${oCityId}`} onClick={goCity(oCityId)} style={{ color: "inherit", borderBottom: "1px dotted var(--rule)" }}>{h.origin_city}, {h.origin_state}</a>
                    : <>{h.origin_city}, {h.origin_state}</>}
                  {" → "}
                  {dCityId
                    ? <a href={`#/report/${dCityId}`} onClick={goCity(dCityId)} style={{ color: "inherit", borderBottom: "1px dotted var(--rule)" }}>{h.dest_city}, {h.dest_state}</a>
                    : <>{h.dest_city}, {h.dest_state}</>}
                </div>
                <div className="rt-equip">{h.equipment_type}</div>
                <div className="rt-rate">${rate.toFixed(2)}/mi</div>
                <div className={`rt-band rt-band-${h.heat_band}`}>{h.heat_band}</div>
                <div className="rt-bar" title={`${pct >= 0 ? "+" : ""}${pct}% vs ${h.equipment_type} median`}>
                  <div className="rt-bar-fill" style={{ width: `${fillPct}%` }} />
                </div>
              </div>
            );
          })}
        </div>
      </section>

      {/* Asymmetry */}
      {asym.length > 0 && (
        <section className="rt-section">
          <div className="rt-section-h">
            <span>Headhaul vs backhaul asymmetry</span>
            <span style={{ fontSize: 10, letterSpacing: "0.16em" }}>The {Math.abs(parseFloat(asym[0]?.pct_discount || 0))}% rule</span>
          </div>
          <div className="rt-asym-table">
            <div className="rt-asym-row" style={{ background: "oklch(0.98 0.02 60)" }}>
              <div className="rt-equip" style={{fontSize:10}}>Lane</div>
              <div className="rt-equip" style={{fontSize:10, textAlign:"right"}}>Head $/mi</div>
              <div className="rt-equip rt-asym-back" style={{fontSize:10, textAlign:"right"}}>Back $/mi</div>
              <div className="rt-equip" style={{fontSize:10, textAlign:"right"}}>% diff</div>
              <div className="rt-equip rt-asym-spread" style={{fontSize:10, textAlign:"right"}}>$ saved/truck</div>
            </div>
            {asym.map((r, i) => (
              <div key={i} className="rt-asym-row">
                <div className="rt-route" style={{fontSize:15}}>{r.lane}</div>
                <div className="rt-rate" style={{fontSize:15}}>${parseFloat(r.headhaul_per_mi).toFixed(2)}</div>
                <div className="rt-rate rt-asym-back" style={{fontSize:15, color:"var(--ink-soft)"}}>${parseFloat(r.backhaul_per_mi).toFixed(2)}</div>
                <div className="rt-asym-pct">{parseFloat(r.pct_discount).toFixed(0)}%</div>
                <div className="rt-asym-saved rt-asym-spread"><strong>${Math.abs(parseFloat(r.spread_total_dollars || 0)).toLocaleString()}</strong> per truck</div>
              </div>
            ))}
          </div>
        </section>
      )}

      {/* USDA AMS — federal weekly produce truck rates. Sits alongside DAT
          to give shippers a third-party benchmark on produce-specific lanes
          where AMS publishes (Salinas, Yakima, McAllen / Lower Rio Grande,
          South Florida, Atlanta market, etc). Rates are per total truckload,
          not per mile, so compare loosely against the DAT $/mi data above. */}
      {amsOriginKeys.length > 0 && (
        <section className="rt-section">
          <div className="rt-section-h">
            <span>Federal produce truck rates · USDA AMS</span>
            <span style={{ fontSize: 10, letterSpacing: "0.16em" }}>
              {ams.length} active commodity rows · weekly
            </span>
          </div>
          <p style={{ margin: "0 0 16px", color: "var(--ink-soft)", fontSize: 14, lineHeight: 1.5, maxWidth: 820 }}>
            <strong style={{ color: "var(--ink)" }}>The federal benchmark.</strong>{" "}
            USDA Specialty Crops Market News collects negotiated truck rates
            at every major produce shipping point each week. Per-truckload
            ranges (low to "mostly" to high), free, federally collected. Use
            alongside the DAT $/mi heat board above as a third-party check on
            produce lanes — South Florida, Salinas, Yakima, Lower Rio Grande,
            and the major terminal markets.
          </p>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(280px, 1fr))", gap: 16 }}>
            {amsOriginKeys.map(origin => (
              <div key={origin} style={{ background: "#fff", border: "1px solid var(--rule)", borderRadius: 8, padding: 16 }}>
                <div style={{ fontFamily: "var(--font-mono)", fontSize: 10, letterSpacing: "0.18em", textTransform: "uppercase", color: "var(--ink-soft)", marginBottom: 8 }}>
                  {origin}
                </div>
                {amsByOrigin[origin].map((r, i) => {
                  const lo = r.rate_mostly_low  || r.rate_low;
                  const hi = r.rate_mostly_high || r.rate_high;
                  const range = (lo != null && hi != null)
                    ? `$${Math.round(parseFloat(lo)).toLocaleString()}–$${Math.round(parseFloat(hi)).toLocaleString()}`
                    : "—";
                  return (
                    <div key={i} style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: 8, padding: "8px 0", borderBottom: i < amsByOrigin[origin].length - 1 ? "1px solid var(--rule)" : "none", alignItems: "baseline" }}>
                      <div>
                        <div style={{ fontFamily: "var(--font-serif)", fontSize: 15, color: "var(--ink)" }}>{r.commodity}</div>
                        {r.destination && r.destination !== "various" && (
                          <div style={{ fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--ink-soft)" }}>→ {r.destination}</div>
                        )}
                      </div>
                      <div style={{ textAlign: "right" }}>
                        <div style={{ fontFamily: "var(--font-mono)", fontSize: 13, color: "var(--ink)", fontWeight: 600 }}>{range}</div>
                        {r.trend && (
                          <div style={{ fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--ink-soft)" }}>{r.trend}</div>
                        )}
                      </div>
                    </div>
                  );
                })}
              </div>
            ))}
          </div>
          <p style={{ margin: "16px 0 0", color: "var(--ink-soft)", fontSize: 12, lineHeight: 1.5, fontStyle: "italic" }}>
            Source: USDA AMS Specialty Crops Market News, marsapi.ams.usda.gov. Rate range = low to "mostly" inner band to high, per total truckload (not per mile). Updated weekly.
          </p>
        </section>
      )}

      <div className="rt-callout">
        <div className="rt-callout-h">For carriers · revenue maximization</div>
        <h3 className="rt-callout-t">Backhaul-aware lane planning saves up to 34% vs blind dispatch.</h3>
        <p className="rt-callout-s">
          The asymmetry table above is the math: a carrier running LA→Dallas headhaul at $2.64/mi but returning empty leaves $1,311/truck on the table per round trip. Optimization engine in development will rank loads by $/HOS-hour, not just $/mile, and account for destination load density to minimize next-load deadhead. Capacity verification via authority + safety lookup is built into our carrier directory.
        </p>
      </div>

      {/* Crowdsource — every spot quote operators submit makes the heat
          board less DAT-dependent. Sealed by default, aggregated at 3+
          submissions per lane. The trust contract is the same as the
          driver intel pipeline: they choose visibility, we never publish
          identity, aggregations only. */}
      <SpotQuoteForm />

      {/* Rate sources transparency — surfaces what's currently feeding the
          heat board so operators trust the number. */}
      <RateSourceTransparency />

      {/* Cross-links — connect this surface to the rest of the graph so a
          visitor on rates can move into verification, into the shipper
          directory, or into a city report without navigating up to the nav. */}
      <section className="rt-section">
        <div className="rt-section-h">
          <span>Connected surfaces</span>
          <span style={{ fontSize: 10, letterSpacing: "0.16em" }}>From rates · into the rest of the graph</span>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))", gap: 12 }}>
          <a href="#/find-carrier" onClick={(e) => { e.preventDefault(); window.location.hash = "#/find-carrier"; }}
             style={{ textDecoration: "none", color: "inherit", padding: 16, border: "1px solid var(--rule)", borderRadius: 6, background: "#fff", display: "block" }}>
            <div style={{ fontFamily: "var(--font-mono)", fontSize: 10, letterSpacing: "0.16em", textTransform: "uppercase", color: "var(--ink-soft)", marginBottom: 6 }}>Verify carrier →</div>
            <div style={{ fontFamily: "var(--font-serif)", fontSize: 17, color: "var(--ink)", lineHeight: 1.3 }}>
              Run any carrier name, DOT#, or MC# against the federal registry before you tender.
            </div>
          </a>
          <a href="#/shippers" onClick={(e) => { e.preventDefault(); window.location.hash = "#/shippers"; }}
             style={{ textDecoration: "none", color: "inherit", padding: 16, border: "1px solid var(--rule)", borderRadius: 6, background: "#fff", display: "block" }}>
            <div style={{ fontFamily: "var(--font-mono)", fontSize: 10, letterSpacing: "0.16em", textTransform: "uppercase", color: "var(--ink-soft)", marginBottom: 6 }}>Shipper directory →</div>
            <div style={{ fontFamily: "var(--font-serif)", fontSize: 17, color: "var(--ink)", lineHeight: 1.3 }}>
              See which shippers operate in the metros at either end of these lanes — 29,762 indexed.
            </div>
          </a>
          <a href="#/opportunities" onClick={(e) => { e.preventDefault(); window.location.hash = "#/opportunities"; }}
             style={{ textDecoration: "none", color: "inherit", padding: 16, border: "1px solid var(--rule)", borderRadius: 6, background: "#fff", display: "block" }}>
            <div style={{ fontFamily: "var(--font-mono)", fontSize: 10, letterSpacing: "0.16em", textTransform: "uppercase", color: "var(--ink-soft)", marginBottom: 6 }}>Opportunities →</div>
            <div style={{ fontFamily: "var(--font-serif)", fontSize: 17, color: "var(--ink)", lineHeight: 1.3 }}>
              Specific findings rate-based actions you can take this quarter — for carriers, brokers, and shippers.
            </div>
          </a>
        </div>
      </section>

      <div style={{ height: 96 }} />
    </div>
  );
}

// =============================================================
// Spot-quote crowdsource form. Operators submit a recent rate
// they quoted or paid. Sealed by default, aggregated at 3+ subs
// per lane via the lane_rate_crowd_agg view. Same trust contract
// as the driver intel pipeline.
// =============================================================
function SpotQuoteForm() {
  const [form, setForm] = React.useState({
    origin_city: "", origin_state: "",
    dest_city: "", dest_state: "",
    equipment_type: "van", rate_per_mi: "",
    shipper_company: "",
    attested: false,
    email: "", notes: "",
  });
  const [status, setStatus] = React.useState("idle"); // idle | sending | sent | err
  const [errMsg, setErrMsg] = React.useState("");

  function update(k, v) { setForm(f => ({ ...f, [k]: v })); }

  async function submit(e) {
    e.preventDefault();
    if (!form.origin_city || !form.dest_city || !form.rate_per_mi) {
      setErrMsg("Origin, destination, and $/mi are required.");
      return;
    }
    if (!form.attested) {
      setErrMsg("Please confirm you're a shipper paying for this freight.");
      return;
    }
    const rate = parseFloat(form.rate_per_mi);
    if (!rate || rate < 0.5 || rate > 15) {
      setErrMsg("Rate must be between $0.50 and $15.00 per mile.");
      return;
    }
    setStatus("sending"); setErrMsg("");

    // Hash the email client-side via SubtleCrypto so the server only
    // ever sees an opaque token. Lets operators delete their own
    // submissions without us holding their email.
    let token = null;
    if (form.email) {
      try {
        const enc = new TextEncoder().encode(form.email.toLowerCase().trim() + ":sc-spot-v1");
        const h = await crypto.subtle.digest("SHA-256", enc);
        token = Array.from(new Uint8Array(h)).map(b => b.toString(16).padStart(2, "0")).join("");
      } catch {}
    }

    const payload = {
      origin_city: form.origin_city.trim(),
      origin_state: form.origin_state.trim().toUpperCase().slice(0, 2),
      dest_city: form.dest_city.trim(),
      dest_state: form.dest_state.trim().toUpperCase().slice(0, 2),
      equipment_type: form.equipment_type,
      rate_per_mi: rate,
      source_role: "shipper",
      shipper_company: form.shipper_company.trim() || null,
      attested: true,
      email_token: token,
      notes: form.notes.trim() || null,
    };

    try {
      const r = await window.SI_DB.raw.insert("lane_rate_submissions", payload);
      if (r === null || r === undefined) throw new Error("insert returned null");
      setStatus("sent");
      setForm({
        origin_city: "", origin_state: "", dest_city: "", dest_state: "",
        equipment_type: "van", rate_per_mi: "", source_role: "broker",
        email: "", notes: "",
      });
    } catch (err) {
      setStatus("err");
      setErrMsg("Couldn't submit — try again. " + (err?.message || ""));
    }
  }

  if (status === "sent") {
    return (
      <section className="rt-section">
        <div className="rt-section-h">
          <span>Spot-quote submitted</span>
          <span style={{ fontSize: 10, letterSpacing: "0.16em" }}>Sealed · aggregate-only</span>
        </div>
        <div style={{ padding: 24, background: "oklch(0.97 0.04 145)", border: "1px solid oklch(0.85 0.10 145)", borderRadius: 8, textAlign: "center" }}>
          <div style={{ fontFamily: "var(--font-serif)", fontSize: 22, marginBottom: 6 }}>✓ Got it. Thanks.</div>
          <div style={{ color: "var(--ink-soft)", fontSize: 14, lineHeight: 1.5, maxWidth: 580, margin: "0 auto" }}>
            Your quote is sealed and won't be published verbatim. Once 3+ operators submit on the same lane, the median rate appears in the heat board, no names attached. <a href="#" style={{ color: "var(--ink)", borderBottom: "1px solid var(--rule)" }} onClick={(e) => { e.preventDefault(); setStatus("idle"); }}>Submit another</a>
          </div>
        </div>
      </section>
    );
  }

  return (
    <section className="rt-section">
      <div className="rt-section-h">
        <span>Shippers · submit what you actually paid</span>
        <span style={{ fontSize: 10, letterSpacing: "0.16em" }}>Shipper-only · sealed · 3+ subs to publish</span>
      </div>
      <div style={{ background: "#fff", border: "1px solid var(--rule)", borderRadius: 8, padding: 24 }}>
        <p style={{ margin: "0 0 12px", color: "var(--ink-soft)", fontSize: 14, lineHeight: 1.55, maxWidth: 780 }}>
          <strong style={{ color: "var(--ink)" }}>Only shippers can submit.</strong> Brokers and carriers want
          published rates to look <em>high</em> — that's how they justify a quote
          or an ask. Shippers are the ones writing the check. Their rates have
          the opposite manipulation incentive, which makes them the cleanest
          ground truth on what freight is actually being paid for. Same
          trust-symmetry as our driver pipeline.
        </p>
        <p style={{ margin: "0 0 18px", color: "var(--ink-soft)", fontSize: 14, lineHeight: 1.55, maxWidth: 780 }}>
          Sealed by default. We never publish your company, contact, or notes.
          Once 3+ shippers submit on the same lane, the median rate appears
          in the heat board with no names attached.
        </p>
        <form onSubmit={submit}>
          <style>{`
            .sq-grid { display: grid; gap: 12px; }
            .sq-row { display: grid; grid-template-columns: 1fr 100px; gap: 8px; }
            .sq-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
            @media (max-width: 700px) { .sq-pair { grid-template-columns: 1fr; } }
            .sq-label {
              font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.16em;
              text-transform: uppercase; color: var(--ink-soft); margin-bottom: 4px;
            }
            .sq-input, .sq-select {
              width: 100%; box-sizing: border-box;
              padding: 10px 12px; font: inherit; font-size: 14px;
              border: 1px solid var(--rule); border-radius: 6px; background: #fff;
              color: var(--ink);
            }
            .sq-input:focus, .sq-select:focus { outline: none; border-color: var(--ink); }
            .sq-submit-row { display: flex; gap: 12px; align-items: center; margin-top: 8px; flex-wrap: wrap; }
            .sq-btn {
              padding: 12px 22px; background: var(--ink); color: var(--paper);
              border: 1px solid var(--ink); border-radius: 6px;
              font: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
            }
            .sq-btn:disabled { opacity: 0.6; cursor: wait; }
            .sq-err { color: oklch(0.55 0.18 25); font-size: 13px; }
          `}</style>
          <div className="sq-grid">
            <div className="sq-pair">
              <div>
                <div className="sq-label">Origin</div>
                <div className="sq-row">
                  <input className="sq-input" placeholder="City" value={form.origin_city} onChange={e => update("origin_city", e.target.value)} />
                  <input className="sq-input" placeholder="ST" maxLength={2} value={form.origin_state} onChange={e => update("origin_state", e.target.value)} />
                </div>
              </div>
              <div>
                <div className="sq-label">Destination</div>
                <div className="sq-row">
                  <input className="sq-input" placeholder="City" value={form.dest_city} onChange={e => update("dest_city", e.target.value)} />
                  <input className="sq-input" placeholder="ST" maxLength={2} value={form.dest_state} onChange={e => update("dest_state", e.target.value)} />
                </div>
              </div>
            </div>
            <div className="sq-pair">
              <div>
                <div className="sq-label">Equipment</div>
                <select className="sq-select" value={form.equipment_type} onChange={e => update("equipment_type", e.target.value)}>
                  <option value="van">Van — dry</option>
                  <option value="reefer">Reefer</option>
                  <option value="flatbed">Flatbed</option>
                  <option value="step_deck">Step deck</option>
                  <option value="rgn">RGN / lowboy</option>
                  <option value="conestoga">Conestoga</option>
                  <option value="tanker">Tanker</option>
                  <option value="box_truck">26' box truck</option>
                </select>
              </div>
              <div>
                <div className="sq-label">$/mi</div>
                <input className="sq-input" type="number" step="0.01" min="0.5" max="15" placeholder="2.45" value={form.rate_per_mi} onChange={e => update("rate_per_mi", e.target.value)} />
              </div>
            </div>
            <div className="sq-pair">
              <div>
                <div className="sq-label">Your company · optional · sealed</div>
                <input className="sq-input" placeholder="Acme Distribution" value={form.shipper_company} onChange={e => update("shipper_company", e.target.value)} />
              </div>
              <div>
                <div className="sq-label">Email · optional · for delete requests</div>
                <input className="sq-input" type="email" placeholder="you@company.com" value={form.email} onChange={e => update("email", e.target.value)} />
              </div>
            </div>
            <div>
              <div className="sq-label">Notes · optional · sealed</div>
              <input className="sq-input" placeholder="Spot vs contract, rush, accessorials, anything." value={form.notes} onChange={e => update("notes", e.target.value)} />
            </div>
            <label style={{ display: "flex", alignItems: "flex-start", gap: 10, padding: "12px 14px", background: "var(--paper-2, #f5f1e8)", border: "1px solid var(--rule)", borderRadius: 6, cursor: "pointer", marginTop: 4 }}>
              <input type="checkbox" checked={form.attested} onChange={e => update("attested", e.target.checked)} style={{ marginTop: 3 }} />
              <span style={{ fontSize: 13, color: "var(--ink)", lineHeight: 1.5 }}>
                <strong>I'm a shipper paying for this freight.</strong> The
                rate I'm submitting is what was actually paid (or quoted to
                me as the shipper) on this lane in the last 60 days, not
                a number I'm marketing.
              </span>
            </label>
            <div className="sq-submit-row">
              <button type="submit" className="sq-btn" disabled={status === "sending" || !form.attested}>
                {status === "sending" ? "Submitting…" : "Submit your paid rate"}
              </button>
              {errMsg && <span className="sq-err">{errMsg}</span>}
            </div>
          </div>
        </form>
      </div>
    </section>
  );
}

// =============================================================
// Rate-source transparency strip. Tells operators what's actually
// feeding the heat board. The trust contract works only if we're
// honest about provenance.
// =============================================================
function RateSourceTransparency() {
  const sources = [
    { name: "Open load board", note: "Live snapshot of the major freight load board, captured continuously.", count: "18 lanes" },
    { name: "Shipper-only crowdsource", note: "Rates submitted by shippers paying for freight — the audience with no incentive to inflate the published median. Sealed; aggregated at 3+ subs per lane.", count: "Building" },
    { name: "Federal indices", note: "BTS Truck Tonnage, EIA truck transportation cost, the macro layer.", count: "Monthly" },
    { name: "FreightWaves SONAR public daily", note: "NTI · OTRI · OTVI — broker-grade volume and rate signal, ingested daily.", count: "Daily" },
  ];
  return (
    <section className="rt-section">
      <div className="rt-section-h">
        <span>Rate sources · what feeds the heat board</span>
        <span style={{ fontSize: 10, letterSpacing: "0.16em" }}>Trust contract · we say what's in the number</span>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))", gap: 10 }}>
        {sources.map((s, i) => (
          <div key={i} style={{ background: "#fff", border: "1px solid var(--rule)", borderRadius: 6, padding: 14 }}>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 6 }}>
              <div style={{ fontFamily: "var(--font-serif)", fontSize: 16, color: "var(--ink)", fontWeight: 600 }}>{s.name}</div>
              <div style={{ fontFamily: "var(--font-mono)", fontSize: 10, letterSpacing: "0.1em", textTransform: "uppercase", color: "var(--ink-soft)" }}>{s.count}</div>
            </div>
            <div style={{ fontSize: 13, color: "var(--ink-soft)", lineHeight: 1.45 }}>{s.note}</div>
          </div>
        ))}
      </div>
    </section>
  );
}

window.RatesPage = RatesPage;
