// Top-100 directory page — drives traffic into /#/c, /#/b, /#/sh dossiers.
//
// Single polymorphic component, three exports (CarriersTop100Page,
// BrokersTop100Page, ShippersTop100Page). Each entity type renders the
// same table shell with type-specific columns, search, sort, and
// filter. Click a row → its dossier page.
//
// The directories pull straight from the curated carriers_top100 /
// brokers_top100 / shippers_top100 tables via the publishable Supabase
// key. Each table has an anon SELECT policy so this page works with no
// auth; service role is only used by /api/(carrier|broker|shipper)-detail
// for joining research_dossiers under RLS.

const { useState: useStateT100, useEffect: useEffectT100, useMemo: useMemoT100 } = React;

const T100_CONFIG = {
  carrier: {
    table: "carriers_top100",
    title: "Top 100 U.S. for-hire carriers",
    subtitle: "Transport Topics 2024 ranking · cross-referenced against SEC EDGAR + FMCSA SAFER",
    routePrefix: "c",
    select: "rank,name,parent,slug,hq_city,hq_state,segment,fleet_size_estimate,revenue_2024_usd,public,ticker,ownership,founded,notes,verdict_override",
    columns: [
      { key: "rank", label: "#", width: 56, sortable: true, align: "right" },
      { key: "name", label: "Carrier", flex: 3, sortable: true },
      { key: "segment", label: "Segment", flex: 1, sortable: true, formatter: (v) => v ? v.toUpperCase() : "—" },
      { key: "hq", label: "HQ", flex: 1.4, formatter: (_, r) => r.hq_city ? `${r.hq_city}, ${r.hq_state || ""}` : "—" },
      { key: "fleet_size_estimate", label: "Fleet", flex: 1, sortable: true, align: "right", formatter: (v) => v ? v.toLocaleString("en-US") : "—" },
      { key: "revenue_2024_usd", label: "Revenue 2024", flex: 1.2, sortable: true, align: "right", formatter: fmtRevT100 },
      { key: "ownership", label: "Ownership", flex: 1, sortable: true, formatter: (v) => v ? v.toUpperCase() : "—" },
      { key: "verdict", label: "Verdict", flex: 0.9, formatter: (_, r) => verdictPill(deriveTierT100(r)) },
    ],
    filters: ["segment", "ownership"],
  },
  broker: {
    table: "brokers_top100",
    title: "Top 100 U.S. freight brokers",
    subtitle: "Transport Topics 2024 broker ranking · gross + net revenue, loads/year, TMS platform",
    routePrefix: "b",
    select: "rank,name,parent,slug,hq_city,hq_state,type,gross_revenue_2024_usd,net_revenue_2024_usd,loads_per_year_estimate,public,ticker,ownership,founded,tms_platform,notes,verdict_override",
    columns: [
      { key: "rank", label: "#", width: 56, sortable: true, align: "right" },
      { key: "name", label: "Broker", flex: 3, sortable: true },
      { key: "type", label: "Type", flex: 1, sortable: true, formatter: (v) => v ? v.toUpperCase().replace(/-/g, " ") : "—" },
      { key: "hq", label: "HQ", flex: 1.4, formatter: (_, r) => r.hq_city ? `${r.hq_city}, ${r.hq_state || ""}` : "—" },
      { key: "loads_per_year_estimate", label: "Loads/yr", flex: 1, sortable: true, align: "right", formatter: fmtLoadsT100 },
      { key: "gross_revenue_2024_usd", label: "Gross 2024", flex: 1.2, sortable: true, align: "right", formatter: fmtRevT100 },
      { key: "tms_platform", label: "TMS", flex: 1.1, formatter: (v) => v || "—" },
      { key: "verdict", label: "Verdict", flex: 0.9, formatter: (_, r) => verdictPill(deriveTierT100(r)) },
    ],
    filters: ["type", "ownership"],
  },
  shipper: {
    table: "shippers_top100",
    title: "Top 100 U.S. shippers",
    subtitle: "Ranked by inferred annual freight spend · 86 SEC-verified public + 14 private",
    routePrefix: "sh",
    select: "rank,name,parent,slug,hq_city,hq_state,industry,sub_industry,annual_revenue_2024_usd,estimated_freight_spend_usd,freight_spend_pct_of_revenue,public,ticker,ownership,us_dc_count_estimate,us_store_count_estimate,founded,notes,verdict_override",
    columns: [
      { key: "rank", label: "#", width: 56, sortable: true, align: "right" },
      { key: "name", label: "Shipper", flex: 3, sortable: true },
      { key: "industry", label: "Industry", flex: 1.4, sortable: true, formatter: (v) => v ? v.toUpperCase().replace(/-/g, " ") : "—" },
      { key: "estimated_freight_spend_usd", label: "Freight spend", flex: 1.3, sortable: true, align: "right", formatter: fmtRevT100 },
      { key: "annual_revenue_2024_usd", label: "Revenue 2024", flex: 1.2, sortable: true, align: "right", formatter: fmtRevT100 },
      { key: "us_dc_count_estimate", label: "DCs", flex: 0.7, sortable: true, align: "right", formatter: (v) => v ? v.toLocaleString("en-US") : "—" },
      { key: "ownership", label: "Ownership", flex: 1, sortable: true, formatter: (v) => v ? v.toUpperCase() : "—" },
      { key: "verdict", label: "Verdict", flex: 0.9, formatter: (_, r) => verdictPill(deriveTierT100(r)) },
    ],
    filters: ["industry", "ownership"],
  },
};

function fmtRevT100(usd) {
  if (!usd) return "—";
  if (usd >= 1e9) return `$${(usd / 1e9).toFixed(1)}B`;
  if (usd >= 1e6) return `$${(usd / 1e6).toFixed(0)}M`;
  return `$${(usd / 1e3).toFixed(0)}K`;
}
function fmtLoadsT100(n) {
  if (!n) return "—";
  if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
  if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
  return n.toLocaleString("en-US");
}

// Verdict logic — same heuristic as the dossier pages but without the
// research_dossiers escalation (we don't fetch dossiers for the directory).
//
// Distress: explicit failure / fraud / regulator events. Phrases must be
//   specific enough to not match company names ('covenant violation' not
//   'covenant', because 'Covenant Logistics' would false-positive).
// Watch: substantive material events that warrant attention. Bare 'pe-backed'
//   or 'acquired' aren't watch-list events — most large brokers are PE-owned
//   or have made acquisitions; these are status, not signals.
// No-flags: nothing in the curated note matched. NOT a positive verification
//   claim — just "we have no public-record red flags on file."
// Editorial gate: an explicit verdict_override always wins. When the
// override is null, we run the heuristic — but a heuristic-derived
// "distress" tier is DOWNGRADED to "watch" because Distress claims need
// human approval before they're displayed publicly (defamation risk).
// Editors approve via /#/admin → Verdicts tab → POST /api/admin/verdict-override.
// Verdict policy (2026-05): tier strictly from VERIFIED, LINKED public-
// record evidence. No heuristic substring matching on free-text notes —
// that path produced false positives on reputable carriers (Marten,
// Werner, FedEx) AND created defamation exposure (we can't safely flag
// a major carrier "Watch" based on a phrase in our own seed file).
//
// Until the evidence-driven pipeline is wired (joins to intel_alerts,
// bankruptcy_filings, nlrb_filings, warn_notices, osha_injury_reports
// with carrier-name match + clickable source URL), every carrier
// defaults to "no_flags." Distress / Watch will return only when the
// dossier can SHOW the linked evidence.
//
// Editorial overrides (verdict_override column) are no longer read —
// every flag must come from public-record evidence we can cite.
function deriveTierT100(_row) {
  return "no_flags";
}

function verdictPill(tier) {
  const config = {
    distress: { label: "Distress", color: "#ff6868", bg: "rgba(255,104,104,0.12)" },
    watch:    { label: "Watch",    color: "#ffb84d", bg: "rgba(255,184,77,0.12)" },
    no_flags: { label: "No flags", color: "#5fd9a8", bg: "rgba(95,217,168,0.12)" },
  }[tier];
  // Tiny horizontal stoplight — same red/yellow/green order, only the
  // lamp matching the tier is lit. Mirrors the big Stoplight component
  // on the dossier page so the visual language is consistent.
  const lamps = ["distress", "watch", "no_flags"];
  const lampColor = (t) => t === "distress" ? "#ff4646" : t === "watch" ? "#ffc53d" : "#3ddb88";
  return (
    <span style={{
      display: "inline-flex", alignItems: "center", gap: 6,
      fontSize: 11, fontWeight: 600, padding: "3px 9px 3px 5px",
      borderRadius: 999, color: config.color, background: config.bg,
      whiteSpace: "nowrap",
    }}>
      <span style={{
        display: "inline-flex", gap: 2, padding: "2px 3px", borderRadius: 4,
        background: "rgba(0,0,0,0.45)",
      }}>
        {lamps.map((l) => {
          const on = l === tier;
          return (
            <span key={l} style={{
              width: 6, height: 6, borderRadius: "50%",
              background: on ? lampColor(l) : "rgba(255,255,255,0.12)",
              boxShadow: on ? `0 0 4px ${lampColor(l)}` : "none",
            }} />
          );
        })}
      </span>
      {config.label}
    </span>
  );
}

// Generic directory page — works for carriers / brokers / shippers via
// the entity-type config above.
function Top100Directory({ entityType, onNav, embedded }) {
  const cfg = T100_CONFIG[entityType];
  const [rows, setRows] = useStateT100([]);
  const [loading, setLoading] = useStateT100(true);
  const [error, setError] = useStateT100(null);
  const [search, setSearch] = useStateT100("");
  const [sort, setSort] = useStateT100({ key: "rank", dir: "asc" });
  const [filterValues, setFilterValues] = useStateT100({});

  useEffectT100(() => {
    let cancelled = false;
    setLoading(true);
    // limit=500 — the carriers_top100 table extends to ~225 rows after
    // the carriers_top500_addendum.sql migration. 500 is a comfortable
    // ceiling for any future expansion without paginating client-side.
    const url = `https://fspqxobtsgyzzcdvpqul.supabase.co/rest/v1/${cfg.table}?select=${cfg.select}&order=rank.asc&limit=500`;
    const headers = window.SI_DB && window.SI_DB.headers
      ? window.SI_DB.headers
      : { apikey: "sb_publishable_0zNSihwdiVEKHD_HQj7Oqw_wEGGPFf6", Authorization: "Bearer sb_publishable_0zNSihwdiVEKHD_HQj7Oqw_wEGGPFf6" };
    fetch(url, { headers })
      .then(async (r) => {
        if (cancelled) return;
        if (!r.ok) {
          setError(`HTTP ${r.status}`);
          setLoading(false);
          return;
        }
        const data = await r.json();
        setRows(Array.isArray(data) ? data : []);
        setLoading(false);
      })
      .catch((e) => { if (!cancelled) { setError(String(e.message || e)); setLoading(false); }});
    return () => { cancelled = true; };
  }, [cfg.table, cfg.select]);

  // Derive filter dropdown options from the actual loaded data.
  const filterOptions = useMemoT100(() => {
    const out = {};
    for (const f of cfg.filters) {
      const set = new Set();
      for (const r of rows) if (r[f]) set.add(r[f]);
      out[f] = ["All", ...[...set].sort()];
    }
    return out;
  }, [rows, cfg.filters]);

  const filtered = useMemoT100(() => {
    const q = search.trim().toLowerCase();
    return rows.filter((r) => {
      if (q) {
        const hay = `${r.name || ""} ${r.parent || ""} ${r.hq_city || ""} ${r.hq_state || ""} ${r.industry || ""} ${r.segment || ""} ${r.type || ""}`.toLowerCase();
        if (!hay.includes(q)) return false;
      }
      for (const [k, v] of Object.entries(filterValues)) {
        if (v && v !== "All" && r[k] !== v) return false;
      }
      return true;
    });
  }, [rows, search, filterValues]);

  const sorted = useMemoT100(() => {
    const arr = [...filtered];
    const { key, dir } = sort;
    arr.sort((a, b) => {
      let av = a[key], bv = b[key];
      if (av == null) av = (typeof bv === "number") ? -Infinity : "";
      if (bv == null) bv = (typeof av === "number") ? -Infinity : "";
      if (typeof av === "number" && typeof bv === "number") return dir === "asc" ? av - bv : bv - av;
      return dir === "asc"
        ? String(av).localeCompare(String(bv))
        : String(bv).localeCompare(String(av));
    });
    return arr;
  }, [filtered, sort]);

  const verdictCounts = useMemoT100(() => {
    const counts = { distress: 0, watch: 0, no_flags: 0 };
    for (const r of rows) counts[deriveTierT100(r)]++;
    return counts;
  }, [rows]);

  function setSortKey(key) {
    setSort((prev) => prev.key === key
      ? { key, dir: prev.dir === "asc" ? "desc" : "asc" }
      : { key, dir: key === "rank" || key === "name" ? "asc" : "desc" });
  }

  return (
    <div style={embedded ? { color: "#fff", fontFamily: '"Helvetica Neue", Helvetica, Arial, sans-serif' } : pageStyleT100}>
      <div style={{ maxWidth: 1320, margin: "0 auto", padding: embedded ? "16px 24px 80px" : "32px 24px 80px" }}>

        {!embedded && (
          <>
            <div style={{ marginBottom: 8, color: "rgba(255,255,255,0.65)", fontSize: 13 }}>
              <a href="#/" style={linkT100}>← Shipping Clarity</a>
              <span style={{ margin: "0 8px", opacity: 0.4 }}>/</span>
              <span style={{ opacity: 0.7 }}>{entityType === "carrier" ? "Carrier" : entityType === "broker" ? "Broker" : "Shipper"} directory</span>
            </div>

            <h1 style={{ fontSize: 38, fontWeight: 800, letterSpacing: "-0.02em", margin: "10px 0 6px" }}>
              {cfg.title}
            </h1>
            <p style={{ fontSize: 15, opacity: 0.7, margin: 0, maxWidth: 740 }}>{cfg.subtitle}</p>
          </>
        )}
        {embedded && (
          <p style={{ fontSize: 14, opacity: 0.6, margin: "8px 0 0", maxWidth: 740 }}>{cfg.subtitle}</p>
        )}

        <div style={{ display: "flex", gap: 12, marginTop: 24, marginBottom: 20, flexWrap: "wrap" }}>
          <Stat label="Total" value={rows.length} />
          <Stat label="No flags" value={verdictCounts.no_flags} color="#5fd9a8" />
          <Stat label="Watch" value={verdictCounts.watch} color="#ffb84d" />
          <Stat label="Distress" value={verdictCounts.distress} color="#ff6868" />
        </div>

        {entityType === "carrier" && (
          <EquipmentButtonRow
            rows={rows}
            active={filterValues.segment || "All"}
            onPick={(seg) => setFilterValues((prev) => ({
              ...prev,
              segment: seg === "All" ? undefined : seg,
            }))}
          />
        )}

        <div style={{ display: "flex", gap: 12, flexWrap: "wrap", marginBottom: 20 }}>
          <input
            type="text"
            value={search}
            onChange={(e) => setSearch(e.target.value)}
            placeholder={`Search ${rows.length} ${entityType}s by name, HQ, parent…`}
            style={{
              flex: "1 1 280px", minWidth: 240, padding: "10px 14px",
              background: "rgba(255,255,255,0.06)", border: "1px solid rgba(255,255,255,0.15)",
              borderRadius: 6, color: "#fff", fontSize: 14,
              fontFamily: '"Helvetica Neue", sans-serif',
            }}
          />
          {cfg.filters.map((f) => (
            <select
              key={f}
              value={filterValues[f] || "All"}
              onChange={(e) => setFilterValues((prev) => ({ ...prev, [f]: e.target.value }))}
              style={{
                padding: "10px 14px", background: "rgba(255,255,255,0.06)",
                border: "1px solid rgba(255,255,255,0.15)", borderRadius: 6,
                color: "#fff", fontSize: 14, cursor: "pointer",
              }}
            >
              {(filterOptions[f] || ["All"]).map((opt) => (
                <option key={opt} value={opt} style={{ background: "#0a1224" }}>
                  {opt === "All" ? `All ${f}s` : (typeof opt === "string" ? opt.replace(/-/g, " ").toUpperCase() : opt)}
                </option>
              ))}
            </select>
          ))}
        </div>

        {loading && <div style={{ padding: 64, textAlign: "center", opacity: 0.6 }}>Loading {entityType}s…</div>}
        {error && <div style={{ padding: 24, color: "#ff8a8a" }}>Couldn't load: {error}</div>}

        {!loading && !error && (
          <>
            <div style={{ fontSize: 13, opacity: 0.55, marginBottom: 8 }}>
              Showing {sorted.length} of {rows.length}
            </div>
            <div style={{
              background: "rgba(255,255,255,0.04)", border: "1px solid rgba(255,255,255,0.10)",
              borderRadius: 10, overflow: "hidden",
            }}>
              <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
                <thead>
                  <tr style={{ background: "rgba(255,255,255,0.04)", borderBottom: "1px solid rgba(255,255,255,0.10)" }}>
                    {cfg.columns.map((col) => {
                      const sortable = col.sortable;
                      const isSorted = sort.key === col.key;
                      return (
                        <th
                          key={col.key}
                          onClick={() => sortable && setSortKey(col.key)}
                          style={{
                            padding: "12px 14px",
                            textAlign: col.align || "left",
                            fontSize: 11, textTransform: "uppercase", letterSpacing: "0.08em",
                            color: "rgba(255,255,255,0.65)", fontWeight: 600,
                            cursor: sortable ? "pointer" : "default",
                            userSelect: "none", whiteSpace: "nowrap",
                            ...(col.width ? { width: col.width } : {}),
                          }}
                        >
                          {col.label}
                          {sortable && isSorted && (
                            <span style={{ marginLeft: 4, color: "#5fa9ff" }}>{sort.dir === "asc" ? "▲" : "▼"}</span>
                          )}
                        </th>
                      );
                    })}
                  </tr>
                </thead>
                <tbody>
                  {sorted.map((r) => (
                    <tr
                      key={r.slug}
                      onClick={() => { window.location.hash = `#/${cfg.routePrefix}/${r.slug}`; }}
                      style={{
                        cursor: "pointer", borderBottom: "1px solid rgba(255,255,255,0.05)",
                        transition: "background 0.12s",
                      }}
                      onMouseEnter={(e) => { e.currentTarget.style.background = "rgba(95,169,255,0.06)"; }}
                      onMouseLeave={(e) => { e.currentTarget.style.background = "transparent"; }}
                    >
                      {cfg.columns.map((col) => (
                        <td
                          key={col.key}
                          style={{
                            padding: "11px 14px",
                            textAlign: col.align || "left",
                            color: col.key === "name" ? "#fff" : "rgba(255,255,255,0.78)",
                            fontWeight: col.key === "name" || col.key === "rank" ? 600 : 400,
                            fontVariantNumeric: col.align === "right" ? "tabular-nums" : "normal",
                            whiteSpace: col.key === "name" ? "nowrap" : "nowrap",
                          }}
                        >
                          {col.formatter ? col.formatter(r[col.key], r) : (r[col.key] || "—")}
                          {col.key === "name" && r.parent && r.parent !== r.name && (
                            <div style={{ fontSize: 11, opacity: 0.5, fontWeight: 400, marginTop: 2 }}>
                              {r.parent}
                            </div>
                          )}
                          {col.key === "name" && r.public && r.ticker && (
                            <span style={{ fontSize: 10, opacity: 0.6, fontWeight: 600, marginLeft: 8, padding: "2px 6px", border: "1px solid rgba(95,169,255,0.4)", borderRadius: 3, color: "#5fa9ff" }}>
                              {r.ticker}
                            </span>
                          )}
                        </td>
                      ))}
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
            {sorted.length === 0 && (
              <div style={{ padding: 32, textAlign: "center", opacity: 0.6 }}>
                No {entityType}s match the current filters.
              </div>
            )}
          </>
        )}

      </div>
    </div>
  );
}

// --- Atoms ----------------------------------------------------------------

const pageStyleT100 = {
  background: "radial-gradient(circle at 30% 0%, #1d2d52 0%, #0a1224 55%, #050913 100%)",
  minHeight: "100vh", color: "#fff",
  fontFamily: '"Helvetica Neue", Helvetica, Arial, sans-serif',
};
const linkT100 = { color: "#5fa9ff", textDecoration: "none" };

function Stat({ label, value, color }) {
  return (
    <div style={{
      padding: "10px 16px", background: "rgba(255,255,255,0.04)",
      border: "1px solid rgba(255,255,255,0.10)", borderRadius: 8,
      minWidth: 96,
    }}>
      <div style={{ fontSize: 10, textTransform: "uppercase", letterSpacing: "0.10em", opacity: 0.55 }}>{label}</div>
      <div style={{ fontSize: 22, fontWeight: 800, marginTop: 4, color: color || "#fff", letterSpacing: "-0.02em" }}>
        {value}
      </div>
    </div>
  );
}

// =============================================================================
// EQUIPMENT BUTTON ROW — fun, oversized "Reefer Carriers" / "Dry Van" / etc.
// buttons that filter the directory by `segment`. One emoji icon per button,
// each row of buttons rotates through a different prop set on each visit
// (tires, steering wheels, snowflakes, fuel pumps) so the directory feels
// alive instead of stamped.
//
// Buttons are big and high-contrast on purpose — the editor explicitly asked
// for "easy and hard to miss what it is."
// =============================================================================

// Equipment-button kinds. CARGO-FORWARD design — each icon shows what a
// carrier in that segment HAULS, not the truck itself. Truckers describe
// themselves by what they haul ("I run reefer," "I'm a flatbedder"), so
// the cargo icon matches the mental model better than truck silhouettes
// (which all look similar at small sizes).
const EQUIP_BUTTONS = [
  { seg: "All",         label: "All carriers", kind: "all" },
  { seg: "reefer",      label: "Reefer",       kind: "reefer" },
  { seg: "tl",          label: "Dry van",      kind: "boxes" },
  { seg: "ltl",         label: "LTL",          kind: "ltl_pallets" },
  { seg: "flatbed",     label: "Flatbed",      kind: "lumber" },
  { seg: "tanker",      label: "Tanker",       kind: "drum" },
  { seg: "drayage",     label: "Drayage",      kind: "container" },
  { seg: "intermodal",  label: "Intermodal",   kind: "rail" },
  { seg: "parcel",      label: "Parcel",       kind: "parcels" },
  { seg: "dedicated",   label: "Dedicated",    kind: "shield" },
  { seg: "auto-hauler", label: "Auto hauler",  kind: "cars" },
  { seg: "mixed",       label: "Mixed",        kind: "mixed" },
];

// CargoIcon — what the carrier HAULS, not the truck. Truckers describe
// themselves by their cargo ("I run reefer," "I'm a flatbedder"), and
// the cargo silhouettes are far more distinct than truck silhouettes
// (which all look similar at button size). Bright iconic colors per type.
function TruckIcon({ kind, active }) {
  const stroke = active ? "#5fa9ff" : "#2c3a5e";
  const dark   = "#0a1224";
  const ink    = "#1a2238";
  const sw     = active ? 1.4 : 1.1;

  return (
    <svg viewBox="0 0 64 56" width="56" height="50" style={{ display: "block", overflow: "visible" }}>

      {/* ALL — mixed-cargo stack: brown box + reefer crate + drum */}
      {kind === "all" && (<>
        {/* drum on left */}
        <rect x="3" y="24" width="14" height="22" rx="1" fill="#6b6b75" stroke={ink} strokeWidth={sw} />
        <line x1="3" y1="30" x2="17" y2="30" stroke={ink} strokeWidth="0.7" />
        <line x1="3" y1="40" x2="17" y2="40" stroke={ink} strokeWidth="0.7" />
        {/* brown box middle */}
        <rect x="20" y="20" width="22" height="26" fill="#a16a3c" stroke={ink} strokeWidth={sw} />
        <line x1="31" y1="20" x2="31" y2="46" stroke={ink} strokeWidth="0.6" opacity="0.5" />
        <line x1="20" y1="33" x2="42" y2="33" stroke={ink} strokeWidth="0.6" opacity="0.5" />
        {/* small box right */}
        <rect x="45" y="28" width="16" height="18" fill="#5fa9ff" stroke={ink} strokeWidth={sw} />
        {/* pallet */}
        <rect x="2" y="46" width="60" height="3" fill="#8b6f47" stroke={ink} strokeWidth="0.6" />
        <rect x="3" y="49" width="3" height="4" fill="#8b6f47" />
        <rect x="30" y="49" width="3" height="4" fill="#8b6f47" />
        <rect x="58" y="49" width="3" height="4" fill="#8b6f47" />
      </>)}

      {/* REEFER — big snowflake on an icy crate */}
      {kind === "reefer" && (<>
        {/* frosted crate */}
        <rect x="6" y="14" width="52" height="34" rx="2" fill="#cce8f5"
              stroke={ink} strokeWidth={sw} />
        {/* frost shimmer */}
        <line x1="6" y1="22" x2="58" y2="22" stroke="#fff" strokeWidth="2" opacity="0.7" />
        <line x1="6" y1="36" x2="58" y2="36" stroke="#fff" strokeWidth="1" opacity="0.5" />
        {/* big snowflake centered */}
        <g transform="translate(32, 31)" stroke="#0a3a8a" strokeWidth="2.4" strokeLinecap="round">
          <line x1="-10" y1="0"   x2="10"  y2="0" />
          <line x1="0"   y1="-10" x2="0"   y2="10" />
          <line x1="-7"  y1="-7"  x2="7"   y2="7" />
          <line x1="-7"  y1="7"   x2="7"   y2="-7" />
          {/* tiny tips */}
          <line x1="10" y1="0" x2="8" y2="-2" />
          <line x1="10" y1="0" x2="8" y2="2" />
          <line x1="-10" y1="0" x2="-8" y2="-2" />
          <line x1="-10" y1="0" x2="-8" y2="2" />
        </g>
      </>)}

      {/* DRY VAN — stack of brown cardboard boxes with tape */}
      {kind === "boxes" && (<>
        {/* large bottom box */}
        <rect x="4" y="24" width="56" height="24" fill="#a16a3c" stroke={ink} strokeWidth={sw} />
        <line x1="32" y1="24" x2="32" y2="48" stroke={ink} strokeWidth="0.7" opacity="0.6" />
        {/* tape across */}
        <rect x="4" y="34" width="56" height="3" fill="#d4a574" opacity="0.85" />
        {/* upper smaller box */}
        <rect x="14" y="6" width="36" height="18" fill="#b87a48" stroke={ink} strokeWidth={sw} />
        <rect x="14" y="13" width="36" height="2.5" fill="#d4a574" opacity="0.85" />
        <line x1="32" y1="6" x2="32" y2="24" stroke={ink} strokeWidth="0.7" opacity="0.6" />
      </>)}

      {/* LTL — three pallets at different sizes (the 'less than truckload' visual) */}
      {kind === "ltl_pallets" && (<>
        {/* pallet base */}
        <rect x="2" y="42" width="60" height="3" fill="#8b6f47" stroke={ink} strokeWidth="0.6" />
        <rect x="3" y="45" width="3" height="4" fill="#8b6f47" />
        <rect x="30" y="45" width="3" height="4" fill="#8b6f47" />
        <rect x="58" y="45" width="3" height="4" fill="#8b6f47" />
        {/* short box */}
        <rect x="4" y="30" width="16" height="12" fill="#5fa9ff" stroke={ink} strokeWidth={sw} />
        {/* tall box */}
        <rect x="22" y="14" width="18" height="28" fill="#a16a3c" stroke={ink} strokeWidth={sw} />
        <line x1="22" y1="28" x2="40" y2="28" stroke={ink} strokeWidth="0.6" opacity="0.5" />
        {/* medium box */}
        <rect x="42" y="22" width="18" height="20" fill="#f5a142" stroke={ink} strokeWidth={sw} />
      </>)}

      {/* FLATBED — stack of lumber with strap */}
      {kind === "lumber" && (<>
        {/* lumber stack — alternating golden boards */}
        {[0, 1, 2, 3, 4].map(i => (
          <rect key={i} x="4" y={20 + i*5} width="56" height="4"
                fill={i % 2 ? "#c19562" : "#a17b48"} stroke={ink} strokeWidth="0.6" />
        ))}
        {/* end-grain detail on left edge */}
        {[0, 1, 2, 3, 4].map(i => (
          <line key={i} x1="6" y1={22 + i*5} x2="9" y2={22 + i*5}
                stroke="#7a5a30" strokeWidth="0.6" />
        ))}
        {/* tie-down straps — black diagonals across the stack */}
        <line x1="16" y1="16" x2="16" y2="48" stroke={ink} strokeWidth="2.4" />
        <line x1="48" y1="16" x2="48" y2="48" stroke={ink} strokeWidth="2.4" />
        {/* ratchet buckles */}
        <rect x="14" y="14" width="4" height="3" fill="#3a3a3a" />
        <rect x="46" y="14" width="4" height="3" fill="#3a3a3a" />
      </>)}

      {/* TANKER — chemical/oil drum w/ HAZMAT placard */}
      {kind === "drum" && (<>
        {/* drum body */}
        <rect x="14" y="6" width="36" height="46" rx="2"
              fill="#7a8499" stroke={ink} strokeWidth={sw} />
        {/* top rim */}
        <ellipse cx="32" cy="6" rx="18" ry="2.4" fill="#9aa3b6" stroke={ink} strokeWidth={sw} />
        {/* manhole cap */}
        <rect x="27" y="3" width="10" height="3" fill="#3a3a3a" />
        {/* bands */}
        <line x1="14" y1="18" x2="50" y2="18" stroke={ink} strokeWidth="0.8" />
        <line x1="14" y1="40" x2="50" y2="40" stroke={ink} strokeWidth="0.8" />
        {/* HAZMAT diamond placard */}
        <g transform="translate(32, 28) rotate(45)">
          <rect x="-7" y="-7" width="14" height="14"
                fill="#ffd84d" stroke={ink} strokeWidth="1" />
        </g>
        <text x="32" y="31" textAnchor="middle" fill={ink}
              fontSize="6" fontWeight="800" fontFamily="ui-monospace, monospace">3</text>
      </>)}

      {/* DRAYAGE — standalone shipping container, corrugated red */}
      {kind === "container" && (<>
        <rect x="2" y="14" width="60" height="32" fill="#c84141"
              stroke={ink} strokeWidth={sw} />
        {/* corrugation */}
        {[8, 13, 18, 23, 28, 33, 38, 43, 48, 53, 58].map(x => (
          <line key={x} x1={x} y1="14" x2={x} y2="46"
                stroke={ink} strokeWidth="0.5" opacity="0.5" />
        ))}
        {/* container ID */}
        <text x="32" y="32" textAnchor="middle" fill="#fff"
              fontSize="9" fontWeight="800" fontFamily="ui-monospace, monospace"
              letterSpacing="1">MSCU</text>
        {/* corner castings */}
        <rect x="2" y="14" width="4" height="4" fill={ink} />
        <rect x="58" y="14" width="4" height="4" fill={ink} />
        <rect x="2" y="42" width="4" height="4" fill={ink} />
        <rect x="58" y="42" width="4" height="4" fill={ink} />
      </>)}

      {/* INTERMODAL — green container on a clear rail track */}
      {kind === "rail" && (<>
        {/* container — green */}
        <rect x="2" y="10" width="60" height="28" fill="#2a8757"
              stroke={ink} strokeWidth={sw} />
        {[8, 14, 20, 26, 32, 38, 44, 50, 56].map(x => (
          <line key={x} x1={x} y1="10" x2={x} y2="38"
                stroke={ink} strokeWidth="0.5" opacity="0.5" />
        ))}
        <text x="32" y="27" textAnchor="middle" fill="#fff"
              fontSize="8" fontWeight="800" fontFamily="ui-monospace, monospace"
              letterSpacing="1">RAIL</text>
        {/* well car wheels */}
        <circle cx="10" cy="42" r="3" fill={ink} />
        <circle cx="18" cy="42" r="3" fill={ink} />
        <circle cx="46" cy="42" r="3" fill={ink} />
        <circle cx="54" cy="42" r="3" fill={ink} />
        {/* rail track w/ sleepers */}
        <line x1="0" y1="50" x2="64" y2="50" stroke="#7a8499" strokeWidth="2" />
        {[2, 10, 18, 26, 34, 42, 50, 58].map(x => (
          <rect key={x} x={x - 1.5} y="48" width="3" height="4" fill="#5a4a30" />
        ))}
      </>)}

      {/* PARCEL — small package stack with shipping label */}
      {kind === "parcels" && (<>
        {/* base box */}
        <rect x="6" y="22" width="34" height="24" fill="#a16a3c" stroke={ink} strokeWidth={sw} />
        <rect x="6" y="29" width="34" height="3" fill="#d4a574" opacity="0.85" />
        {/* shipping label on box */}
        <rect x="10" y="34" width="14" height="8" fill="#fff" stroke={ink} strokeWidth="0.7" />
        <line x1="11" y1="36" x2="22" y2="36" stroke={ink} strokeWidth="0.6" />
        <line x1="11" y1="38" x2="20" y2="38" stroke={ink} strokeWidth="0.6" />
        {/* upper smaller package */}
        <rect x="24" y="6" width="22" height="16" fill="#b87a48" stroke={ink} strokeWidth={sw} />
        <rect x="24" y="11" width="22" height="2.5" fill="#d4a574" opacity="0.85" />
        {/* small package on right */}
        <rect x="44" y="32" width="16" height="14" fill="#7a5a30" stroke={ink} strokeWidth={sw} />
      </>)}

      {/* DEDICATED — interstate highway shield (US 1, etc.) */}
      {kind === "shield" && (<>
        {/* shield outline */}
        <path d="M32 6 L56 12 L56 28 Q56 42 32 50 Q8 42 8 28 L8 12 Z"
              fill="#fff" stroke={ink} strokeWidth="1.6" />
        {/* INTERSTATE blue band */}
        <path d="M32 6 L56 12 L56 18 L8 18 L8 12 Z" fill="#0a3a8a" />
        <text x="32" y="15" textAnchor="middle" fill="#fff"
              fontSize="6" fontWeight="800" fontFamily="Helvetica, Arial, sans-serif"
              letterSpacing="1">INTERSTATE</text>
        {/* dedicated route number */}
        <text x="32" y="40" textAnchor="middle" fill={ink}
              fontSize="22" fontWeight="800" fontFamily="Helvetica, Arial, sans-serif">99</text>
      </>)}

      {/* AUTO HAULER — two cars, side profile */}
      {kind === "cars" && (<>
        {/* upper car (sedan, blue) */}
        <g transform="translate(0, 4)">
          <path d="M6 18 L10 10 Q14 6 22 6 L40 6 Q48 6 52 10 L58 18 Z"
                fill="#5fa9ff" stroke={ink} strokeWidth={sw} />
          <path d="M14 10 L20 7 L42 7 L48 10 Z" fill="#cce8f5" stroke={ink} strokeWidth="0.6" />
          <line x1="32" y1="7" x2="32" y2="10" stroke={ink} strokeWidth="0.6" />
          <rect x="5" y="17" width="54" height="3" fill={ink} />
          <circle cx="14" cy="20" r="3" fill={ink} />
          <circle cx="50" cy="20" r="3" fill={ink} />
          <circle cx="14" cy="20" r="1" fill="#7a8499" />
          <circle cx="50" cy="20" r="1" fill="#7a8499" />
        </g>
        {/* lower car (SUV, orange) */}
        <g transform="translate(0, 28)">
          <path d="M4 20 L8 10 Q12 6 20 6 L42 6 Q50 6 54 10 L60 20 Z"
                fill="#f5a142" stroke={ink} strokeWidth={sw} />
          <path d="M12 10 L18 7 L44 7 L50 10 Z" fill="#fde0c2" stroke={ink} strokeWidth="0.6" />
          <line x1="32" y1="7" x2="32" y2="10" stroke={ink} strokeWidth="0.6" />
          <rect x="3" y="19" width="58" height="3" fill={ink} />
          <circle cx="14" cy="22" r="3.4" fill={ink} />
          <circle cx="50" cy="22" r="3.4" fill={ink} />
          <circle cx="14" cy="22" r="1.2" fill="#7a8499" />
          <circle cx="50" cy="22" r="1.2" fill="#7a8499" />
        </g>
      </>)}

      {/* MIXED — three different cargo types overlapping w/ shuffle accent */}
      {kind === "mixed" && (<>
        {/* brown box */}
        <rect x="4" y="20" width="22" height="26" fill="#a16a3c" stroke={ink} strokeWidth={sw} />
        <line x1="4" y1="32" x2="26" y2="32" stroke={ink} strokeWidth="0.5" opacity="0.6" />
        {/* drum */}
        <rect x="22" y="12" width="18" height="34" rx="1" fill="#7a8499" stroke={ink} strokeWidth={sw} />
        <line x1="22" y1="20" x2="40" y2="20" stroke={ink} strokeWidth="0.6" />
        <line x1="22" y1="38" x2="40" y2="38" stroke={ink} strokeWidth="0.6" />
        {/* container slice */}
        <rect x="38" y="22" width="22" height="24" fill="#c84141" stroke={ink} strokeWidth={sw} />
        {[42, 47, 52, 57].map(x => (
          <line key={x} x1={x} y1="22" x2={x} y2="46"
                stroke={ink} strokeWidth="0.5" opacity="0.5" />
        ))}
        {/* shuffle arrow accent in corner */}
        <g transform="translate(54, 8)" stroke="#5fa9ff" strokeWidth="1.6" fill="none" strokeLinecap="round">
          <path d="M-6 -2 Q-2 -6 4 -2" />
          <path d="M2 -4 L4 -2 L2 0" />
        </g>
      </>)}
    </svg>
  );
}

function EquipmentButtonRow({ rows, active, onPick }) {
  // Count carriers per segment so we only show buttons we have data for.
  const counts = React.useMemo(() => {
    const c = { All: rows.length };
    for (const r of rows) if (r.segment) c[r.segment] = (c[r.segment] || 0) + 1;
    return c;
  }, [rows]);

  const visible = EQUIP_BUTTONS.filter((b) => b.seg === "All" || (counts[b.seg] || 0) > 0);

  return (
    <div style={{ marginBottom: 22 }}>
      <style>{`
        @keyframes eq-bounce {
          0%, 100% { transform: translateY(0); }
          50%      { transform: translateY(-3px); }
        }
        .eq-btn {
          position: relative;
          display: flex; flex-direction: column; align-items: center; justify-content: center;
          gap: 4px; padding: 14px 10px; min-width: 110px; flex: 1 1 110px;
          background: rgba(255,255,255,0.04);
          border: 1.5px solid rgba(255,255,255,0.12);
          border-radius: 10px; color: #fff; cursor: pointer;
          font-family: inherit; font-size: 13px; font-weight: 600;
          transition: transform 140ms, background 140ms, border-color 140ms;
        }
        .eq-btn:hover {
          background: rgba(95,169,255,0.10);
          border-color: rgba(95,169,255,0.55);
          transform: translateY(-2px);
        }
        .eq-btn:hover .eq-btn-icon { animation: eq-bounce 0.6s ease-in-out 2; }
        .eq-btn.active {
          background: linear-gradient(135deg, rgba(95,169,255,0.22) 0%, rgba(95,169,255,0.12) 100%);
          border-color: #5fa9ff;
          box-shadow: 0 6px 18px rgba(95,169,255,0.25);
        }
        .eq-btn-icon {
          height: 32px; line-height: 0;
          display: inline-flex; align-items: center; justify-content: center;
          filter: drop-shadow(0 2px 4px rgba(0,0,0,0.45));
        }
        .eq-btn-label { font-size: 13px; letter-spacing: 0.01em; }
        .eq-btn-count {
          font-family: var(--font-mono, ui-monospace, monospace);
          font-size: 10px; opacity: 0.55; letter-spacing: 0.06em;
        }
      `}</style>
      <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
        {visible.map((b) => {
          const isActive = active === b.seg;
          const n = counts[b.seg] || 0;
          return (
            <button
              key={b.seg}
              className={`eq-btn ${isActive ? "active" : ""}`}
              onClick={() => onPick(b.seg)}
              title={`${b.label} (${n})`}
            >
              <span className="eq-btn-icon">
                <TruckIcon kind={b.kind} active={isActive} />
              </span>
              <span className="eq-btn-label">{b.label}</span>
              <span className="eq-btn-count">{n}</span>
            </button>
          );
        })}
      </div>
    </div>
  );
}

window.CarriersTop100Page = (props) => <Top100Directory entityType="carrier" {...props} />;
window.BrokersTop100Page = (props) => <Top100Directory entityType="broker" {...props} />;
window.ShippersTop100Page = (props) => <Top100Directory entityType="shipper" {...props} />;

// Hub page — single tabbed entry point at /#/top100. The header nav and home
// tile both point here; users land on Carriers by default and can switch
// tabs to Brokers / Shippers without leaving the page. URL stays in sync
// (/#/top100/brokers, /#/top100/shippers) so individual tabs are bookmarkable.
function Top100HubPage({ initialTab, onNav }) {
  const [tab, setTab] = useStateT100(initialTab || "carrier");

  // Keep URL in sync without forcing a page reload.
  useEffectT100(() => {
    const target = `#/top100/${tab === "carrier" ? "carriers" : tab === "broker" ? "brokers" : "shippers"}`;
    if (window.location.hash !== target) {
      // Use replaceState to avoid spamming history with tab switches.
      history.replaceState(null, "", target);
    }
  }, [tab]);

  const tabs = [
    { key: "carrier", label: "Carriers", n: "165" },
    { key: "broker",  label: "Brokers",  n: "100" },
    { key: "shipper", label: "Shippers", n: "100" },
  ];

  return (
    <div style={pageStyleT100}>
      <div style={{ maxWidth: 1320, margin: "0 auto", padding: "32px 24px 0" }}>
        <div style={{ marginBottom: 8, color: "rgba(255,255,255,0.65)", fontSize: 13 }}>
          <a href="#/" style={linkT100}>← Shipping Clarity</a>
          <span style={{ margin: "0 8px", opacity: 0.4 }}>/</span>
          <span style={{ opacity: 0.7 }}>Top 100</span>
        </div>
        <h1 style={{ fontSize: 38, fontWeight: 800, letterSpacing: "-0.02em", margin: "10px 0 4px" }}>
          Top 100 — the company-intelligence triangle
        </h1>
        <p style={{ fontSize: 15, opacity: 0.7, margin: 0, maxWidth: 740 }}>
          Curated, ranked, cross-referenced against SEC EDGAR + FMCSA SAFER. 365 dossiers in one place.
        </p>
        <div style={{
          marginTop: 24, display: "inline-flex",
          background: "rgba(255,255,255,0.04)",
          border: "1px solid rgba(255,255,255,0.10)",
          borderRadius: 8, padding: 4,
        }}>
          {tabs.map((t) => {
            const active = tab === t.key;
            return (
              <button
                key={t.key}
                onClick={() => setTab(t.key)}
                style={{
                  padding: "10px 18px",
                  background: active ? "#5fa9ff" : "transparent",
                  color: active ? "#0a1224" : "#fff",
                  border: "none", borderRadius: 6,
                  fontSize: 14, fontWeight: 600,
                  cursor: "pointer",
                  fontFamily: "inherit",
                }}
              >
                {t.label} <span style={{ opacity: active ? 0.55 : 0.45, fontWeight: 500, marginLeft: 4 }}>{t.n}</span>
              </button>
            );
          })}
        </div>
      </div>
      {/* Render the directory for the active tab. We pass `embedded` so the
          inner directory skips re-rendering its own page chrome. */}
      <Top100Directory entityType={tab} onNav={onNav} embedded />
    </div>
  );
}

window.Top100HubPage = Top100HubPage;

