// LandAI — Updates page (/updates). Exposes window.Updates.
// Entries live in updates.json; each one is a dated note with the counties it
// touched, drawn on one shared map. Add an entry with scripts/add-update.mjs.
(function () {
const { useState, useEffect, useMemo, useRef } = React;

const INK = "#14171C";
const BLUE = "#2E6BF0";
const GREEN = "#2E7D5B";
const PAPER = "#F4F1EA";
const CREAM = "#EFEAE0";
const GRAY = "#5C6168";
const BORDER = "#E4DFD3";
const SERIF = "'Newsreader', Georgia, serif";
const SANS = "'Hanken Grotesk', system-ui, sans-serif";
const MONO = "'JetBrains Mono', monospace";
const WEB_APP_URL = "https://app.landai.app";
const openUrl = (u) => window.open(u, "_blank", "noopener");
const nav = (p, fallback) => () => { if (window.__landaiNav) window.__landaiNav(p); else if (fallback) location.href = fallback; };

const UPDATES_URL = "updates.json";
const COUNTIES_GEOJSON_URL = "https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json";
const NE_STATES_URL = "https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_110m_admin_1_states_provinces_lakes.geojson";

const STATE_NAMES = {
  AL:"Alabama",AK:"Alaska",AZ:"Arizona",AR:"Arkansas",CA:"California",CO:"Colorado",CT:"Connecticut",
  DE:"Delaware",FL:"Florida",GA:"Georgia",HI:"Hawaii",ID:"Idaho",IL:"Illinois",IN:"Indiana",
  IA:"Iowa",KS:"Kansas",KY:"Kentucky",LA:"Louisiana",ME:"Maine",MD:"Maryland",MA:"Massachusetts",
  MI:"Michigan",MN:"Minnesota",MS:"Mississippi",MO:"Missouri",MT:"Montana",NE:"Nebraska",NV:"Nevada",
  NH:"New Hampshire",NJ:"New Jersey",NM:"New Mexico",NY:"New York",NC:"North Carolina",ND:"North Dakota",
  OH:"Ohio",OK:"Oklahoma",OR:"Oregon",PA:"Pennsylvania",RI:"Rhode Island",SC:"South Carolina",
  SD:"South Dakota",TN:"Tennessee",TX:"Texas",UT:"Utah",VT:"Vermont",VA:"Virginia",WA:"Washington",
  WV:"West Virginia",WI:"Wisconsin",WY:"Wyoming",DC:"District of Columbia",
};

function useNarrow(max = 900) {
  const [narrow, setNarrow] = useState(() => window.innerWidth <= max);
  useEffect(() => {
    const onResize = () => setNarrow(window.innerWidth <= max);
    window.addEventListener("resize", onResize);
    return () => window.removeEventListener("resize", onResize);
  }, [max]);
  return narrow;
}

function formatDate(iso) {
  try {
    const [y, m, d] = String(iso).split("-").map(Number);
    return new Date(Date.UTC(y, m - 1, d)).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", timeZone: "UTC" });
  } catch (_) { return iso; }
}

function formatNumber(n) {
  return Number.isFinite(n) ? new Intl.NumberFormat("en-US").format(n) : "";
}

function shortCounty(name) {
  return String(name || "").replace(/\s+County\s*$/i, "").trim();
}

function countyList(entry) {
  return entry && Array.isArray(entry.counties) ? entry.counties : [];
}

function parcelTotal(entry) {
  return countyList(entry).reduce((a, c) => a + (Number(c.parcels) || 0), 0);
}

function Mark({ size = 34 }) {
  return (
    <div style={{ width: size, height: size, borderRadius: size * 0.28, background: "#fff", border: `1px solid ${BORDER}`, display: "flex", alignItems: "center", justifyContent: "center" }}>
      <svg width={size * 0.6} height={size * 0.6} viewBox="0 0 36 36">
        <path d="M18 5 L31 12 L18 19 L5 12 Z" fill="#2E7D5B" />
        <path d="M5 18 L18 25 L31 18" fill="none" stroke="#2E7D5B" strokeWidth="2.4" strokeLinejoin="round" />
        <path d="M5 24 L18 31 L31 24" fill="none" stroke="#9bbfae" strokeWidth="2.4" strokeLinejoin="round" />
      </svg>
    </div>
  );
}

function Nav({ narrow }) {
  const links = [["Mobile App", nav("mobile", "/")], ["Data Store", nav("store", "/data-store")], ["About", nav("about", "/about")], ["Contact Us", nav("contact", "/contact")]];
  const navItemStyle = {
    display: "inline-flex", alignItems: "center", gap: 7, fontSize: 15, fontWeight: 500, color: INK, opacity: 0.72,
    cursor: "pointer", padding: "7px 11px", borderRadius: 999, background: "transparent", border: "1px solid transparent",
  };
  return (
    <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: narrow ? 14 : 28, padding: narrow ? "16px 20px" : "22px 56px", borderBottom: `1px solid ${BORDER}`, position: "sticky", top: 0, background: PAPER, zIndex: 20, flexWrap: "wrap" }}>
      <div onClick={nav("mobile", "/")} style={{ display: "flex", alignItems: "center", gap: 11, cursor: "pointer" }}>
        <Mark size={34} /><span style={{ fontSize: 22, fontWeight: 700, color: INK, letterSpacing: -0.4 }}>LandAI</span>
      </div>
      <div style={{ display: narrow ? "none" : "flex", gap: 34 }}>
        {links.map(([l, fn], i) => <span key={i} onClick={fn} style={navItemStyle}>{l}</span>)}
      </div>
      <div style={{ display: "flex", gap: narrow ? 12 : 18, alignItems: "center", marginLeft: narrow ? "auto" : 0 }}>
        <span onClick={() => openUrl(WEB_APP_URL)} style={{ fontSize: 15, fontWeight: 500, color: INK, cursor: "pointer" }}>Web app</span>
        <button onClick={() => window.__landaiGetApp && window.__landaiGetApp()} style={{ border: "none", background: INK, color: "#fff", fontSize: 14, fontWeight: 600, padding: narrow ? "10px 16px" : "11px 22px", borderRadius: 99, cursor: "pointer", fontFamily: SANS }}>Get the app</button>
      </div>
    </div>
  );
}

function Eyebrow({ children }) {
  return <div style={{ fontFamily: MONO, fontSize: 11, letterSpacing: 1.4, color: "#9aa099", textTransform: "uppercase", fontWeight: 600 }}>{children}</div>;
}

function Hero({ entryCount, countyCount, narrow }) {
  return (
    <section style={{ padding: narrow ? "44px 20px 26px" : "64px 56px 34px", maxWidth: 1180, margin: "0 auto" }}>
      <Eyebrow>Updates</Eyebrow>
      <h1 style={{ fontFamily: SERIF, fontWeight: 500, fontSize: narrow ? 38 : 54, lineHeight: 1.05, letterSpacing: -0.8, color: INK, margin: "14px 0 16px", maxWidth: "16ch" }}>
        What we shipped, county by county.
      </h1>
      <p style={{ fontSize: narrow ? 16 : 18, lineHeight: 1.55, color: GRAY, maxWidth: "58ch", margin: 0 }}>
        When we refresh or add counties, it lands here with the date and a map of exactly which counties changed. Newest first.
      </p>
      <div style={{ display: "flex", gap: 10, flexWrap: "wrap", marginTop: 22 }}>
        <Stat label="Updates" value={formatNumber(entryCount)} />
        <Stat label="Counties touched" value={formatNumber(countyCount)} />
      </div>
    </section>
  );
}

function Stat({ label, value }) {
  return (
    <div style={{ display: "inline-flex", alignItems: "baseline", gap: 8, padding: "9px 14px", borderRadius: 12, background: "#fff", border: `1px solid ${BORDER}` }}>
      <span style={{ fontFamily: SERIF, fontSize: 22, color: INK }}>{value}</span>
      <span style={{ fontFamily: MONO, fontSize: 11, letterSpacing: 1, textTransform: "uppercase", color: "#9aa099" }}>{label}</span>
    </div>
  );
}

/** One Mapbox map; the selected entry's counties are filled, everything else stays quiet. */
function UpdateMap({ entry }) {
  const elRef = useRef(null);
  const mapRef = useRef(null);
  const geojsonRef = useRef(null);
  const [status, setStatus] = useState("loading"); // loading | ready | no-token | error
  const fipsKey = countyList(entry).map((c) => String(c.fips)).join(",");
  const fips = useMemo(() => (fipsKey ? fipsKey.split(",") : []), [fipsKey]);

  useEffect(() => {
    const el = elRef.current;
    if (!el || mapRef.current) return;
    const mapboxgl = window.mapboxgl;
    const token = String(window.MAPBOX_ACCESS_TOKEN || "").trim();
    if (!mapboxgl) { setStatus("error"); return; }
    if (!token) { setStatus("no-token"); return; }
    mapboxgl.accessToken = token;
    const styleUrl = window.LANDAI_MAPBOX_STYLE && String(window.LANDAI_MAPBOX_STYLE).trim() ? String(window.LANDAI_MAPBOX_STYLE).trim() : "mapbox://styles/mapbox/light-v11";
    let map;
    let cancelled = false;
    try {
      map = new mapboxgl.Map({ container: el, style: styleUrl, center: [-98, 39.3], zoom: 3.6, minZoom: 2.5, maxZoom: 12, attributionControl: true, cooperativeGestures: true });
    } catch (_) { setStatus("error"); return; }
    map.addControl(new mapboxgl.NavigationControl({ showCompass: false }), "top-right");
    mapRef.current = map;
    window.__updatesMap = map; // debugging handle; harmless in production

    // Only a style that never arrives is a real failure. Mapbox emits transient
    // errors during startup (a tile or glyph that 404s), and latching on those
    // showed "Map unavailable" over a map that was about to work.
    const failTimer = setTimeout(() => {
      if (!cancelled && !map.isStyleLoaded()) setStatus("error");
    }, 20000);

    const addLayers = () => {
      if (cancelled) return;
      clearTimeout(failTimer);
      map.addSource("updates-ne-states", { type: "geojson", data: NE_STATES_URL });
      map.addLayer({ id: "updates-state-lines", type: "line", source: "updates-ne-states", filter: ["==", ["get", "iso_a2"], "US"], paint: { "line-color": "#121826", "line-width": ["interpolate", ["linear"], ["zoom"], 2, 0.8, 6, 1.8], "line-opacity": 0.75 } });
      fetch(COUNTIES_GEOJSON_URL)
        .then((r) => { if (!r.ok) throw new Error(String(r.status)); return r.json(); })
        .then((gj) => {
          if (cancelled || !mapRef.current) return;
          geojsonRef.current = gj;
          const none = ["in", ["concat", ["get", "STATE"], ["get", "COUNTY"]], ["literal", []]];
          map.addSource("updates-counties", { type: "geojson", data: gj, tolerance: 0.5 });
          map.addLayer({ id: "updates-county-fill", type: "fill", source: "updates-counties", filter: none, paint: { "fill-color": GREEN, "fill-opacity": 0.62 } }, "updates-state-lines");
          map.addLayer({ id: "updates-county-outline", type: "line", source: "updates-counties", filter: none, paint: { "line-color": "#1f5a40", "line-width": ["interpolate", ["linear"], ["zoom"], 4, 0.4, 8, 1.2], "line-opacity": 0.9 } }, "updates-state-lines");
          map.addLayer({ id: "updates-county-labels", type: "symbol", source: "updates-counties", minzoom: 6.5, filter: none, layout: { "text-field": ["get", "NAME"], "text-size": ["interpolate", ["linear"], ["zoom"], 6.5, 9, 10, 12], "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"] }, paint: { "text-color": "#0f2a1e", "text-halo-color": "rgba(255,255,255,0.9)", "text-halo-width": 1.2 } });
          setStatus("ready");
        })
        .catch(() => { if (!cancelled) setStatus("error"); });
    };

    // "style.load" needs no frame, so the map still comes up in a background tab;
    // "load" additionally waits for a first paint and never fires while hidden.
    if (map.isStyleLoaded()) addLayers();
    else map.once("style.load", addLayers);

    return () => { cancelled = true; clearTimeout(failTimer); try { map.remove(); } catch (_) { /* noop */ } mapRef.current = null; };
  }, []);

  // Plotly's county GeoJSON carries the FIPS as STATE + COUNTY properties. Its features
  // do have string ids, but Mapbox drops those, so ["id"] would match nothing.
  useEffect(() => {
    const map = mapRef.current;
    const gj = geojsonRef.current;
    if (!map || !gj || status !== "ready") return;
    const wanted = new Set(fips);
    const filter = ["in", ["concat", ["get", "STATE"], ["get", "COUNTY"]], ["literal", fips]];
    ["updates-county-fill", "updates-county-outline", "updates-county-labels"].forEach((id) => { try { map.setFilter(id, filter); } catch (_) { /* noop */ } });
    if (!fips.length) return;
    let minX = 180, minY = 90, maxX = -180, maxY = -90, any = false;
    const walk = (coords) => {
      if (typeof coords[0] === "number") { any = true; if (coords[0] < minX) minX = coords[0]; if (coords[0] > maxX) maxX = coords[0]; if (coords[1] < minY) minY = coords[1]; if (coords[1] > maxY) maxY = coords[1]; return; }
      coords.forEach(walk);
    };
    gj.features.forEach((f) => { const pr = f.properties || {}; if (wanted.has(String(pr.STATE || "") + String(pr.COUNTY || ""))) walk(f.geometry.coordinates); });
    if (any) map.fitBounds([[minX, minY], [maxX, maxY]], { padding: 48, duration: 900, maxZoom: 8.5 });
  }, [fips, status]);

  const message = status === "loading" ? "Loading map…"
    : status === "no-token" ? "Map unavailable: Mapbox token not set."
    : "Map unavailable right now. The county list below is complete.";

  return (
    <div style={{ position: "relative", borderRadius: 18, overflow: "hidden", border: `1px solid ${BORDER}`, background: CREAM, height: "min(62vh, 560px)", minHeight: 340 }}>
      <div ref={elRef} style={{ position: "absolute", inset: 0 }} />
      {status !== "ready" && (
        <div role="status" style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", textAlign: "center", padding: 24, color: GRAY, fontFamily: MONO, fontSize: 12, letterSpacing: 0.4, background: status === "loading" ? "transparent" : CREAM, pointerEvents: "none" }}>
          {message}
        </div>
      )}
      {entry && (
        <div style={{ position: "absolute", left: 14, bottom: 14, padding: "10px 14px", borderRadius: 12, background: "rgba(255,255,255,0.94)", border: `1px solid ${BORDER}`, fontFamily: SANS, maxWidth: "min(70%, 420px)" }}>
          <div style={{ fontFamily: MONO, fontSize: 11, letterSpacing: 1, textTransform: "uppercase", color: "#9aa099" }}>{formatDate(entry.date)}</div>
          <div style={{ fontSize: 15, fontWeight: 700, color: INK, marginTop: 2 }}>{entry.title}</div>
          <div style={{ fontSize: 13, color: GRAY, marginTop: 2 }}>{formatNumber(countyList(entry).length)} counties · {formatNumber(parcelTotal(entry))} parcels</div>
        </div>
      )}
    </div>
  );
}

function CountyChips({ counties }) {
  const [open, setOpen] = useState(false);
  const shown = open ? counties : counties.slice(0, 18);
  return (
    <div>
      <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
        {shown.map((c) => (
          <span key={c.fips} title={c.parcels ? `${formatNumber(c.parcels)} parcels` : undefined} style={{ fontSize: 13, padding: "5px 10px", borderRadius: 999, background: "#fff", border: `1px solid ${BORDER}`, color: INK }}>{shortCounty(c.name)}</span>
        ))}
      </div>
      {counties.length > 18 && (
        <button
          onClick={(ev) => { ev.stopPropagation(); setOpen(!open); }}
          aria-expanded={open}
          style={{ marginTop: 10, border: "none", background: "transparent", color: BLUE, fontFamily: SANS, fontSize: 14, fontWeight: 600, cursor: "pointer", padding: 0 }}
        >
          {open ? "Show fewer" : `Show all ${counties.length} counties`}
        </button>
      )}
    </div>
  );
}

function EntryCard({ entry, selected, onSelect, narrow }) {
  const counties = countyList(entry);
  const parcels = parcelTotal(entry);
  const states = (entry.states || []).map((s) => STATE_NAMES[s] || s).join(", ");
  // The card is the only control that moves the map, so it has to be operable
  // from the keyboard and announce which entry is currently drawn.
  const onKeyDown = (ev) => {
    if (ev.key === "Enter" || ev.key === " " || ev.key === "Spacebar") { ev.preventDefault(); onSelect(); }
  };
  return (
    <article
      onClick={onSelect}
      onKeyDown={onKeyDown}
      role="button"
      tabIndex={0}
      aria-pressed={selected}
      aria-label={`${entry.title} — show these ${counties.length} counties on the map`}
      style={{ padding: narrow ? "20px 18px" : "26px 28px", borderRadius: 18, background: selected ? "#fff" : "transparent", border: `1px solid ${selected ? INK : BORDER}`, cursor: "pointer", transition: "border-color .15s, background .15s" }}
    >
      <div style={{ display: "flex", justifyContent: "space-between", gap: 12, flexWrap: "wrap", alignItems: "baseline" }}>
        <Eyebrow>{formatDate(entry.date)}{states ? ` · ${states}` : ""}</Eyebrow>
        <span style={{ fontFamily: MONO, fontSize: 11, color: selected ? GREEN : "#9aa099", letterSpacing: 1, textTransform: "uppercase" }}>{selected ? "On the map" : "Show on map"}</span>
      </div>
      <h2 style={{ fontFamily: SERIF, fontWeight: 500, fontSize: narrow ? 26 : 30, lineHeight: 1.15, color: INK, margin: "10px 0 10px" }}>{entry.title}</h2>
      <p style={{ fontSize: 16, lineHeight: 1.55, color: GRAY, margin: "0 0 14px", maxWidth: "70ch" }}>{entry.summary}</p>
      <div style={{ display: "flex", gap: 10, flexWrap: "wrap", marginBottom: 16 }}>
        <Stat label="Counties" value={formatNumber(counties.length)} />
        {parcels > 0 && <Stat label="Parcels" value={formatNumber(parcels)} />}
      </div>
      {entry.highlights && entry.highlights.length > 0 && (
        <ul style={{ margin: "0 0 16px", paddingLeft: 20, color: INK, fontSize: 15, lineHeight: 1.55 }}>
          {entry.highlights.map((h, i) => <li key={i}>{h}</li>)}
        </ul>
      )}
      <CountyChips counties={counties} />
      {entry.pending && entry.pending.length > 0 && (
        <div style={{ marginTop: 16, padding: "12px 14px", borderRadius: 12, background: CREAM, border: `1px solid ${BORDER}`, fontSize: 14, color: GRAY, lineHeight: 1.5 }}>
          <strong style={{ color: INK }}>Not in this update:</strong>{" "}
          {entry.pending.map((p, i) => <span key={i}>{shortCounty(p.name)}{p.reason ? ` (${p.reason})` : ""}{i < entry.pending.length - 1 ? "; " : "."}</span>)}
        </div>
      )}
    </article>
  );
}

function Footer({ narrow }) {
  const links = [["Mobile App", "mobile"], ["Data Store", "store"], ["API", "api"], ["About", "about"], ["Updates", "updates"], ["Contact", "contact"]];
  const go = (t) => {
    if (t === "mobile") return window.__landaiNav ? window.__landaiNav("mobile") : (location.href = "/");
    if (t === "store") return window.__landaiNav ? window.__landaiNav("store") : (location.href = "/data-store");
    if (t === "api") return window.__landaiNav ? window.__landaiNav("api") : (location.href = "/api");
    if (t === "about") return window.__landaiNav ? window.__landaiNav("about") : (location.href = "/about");
    if (t === "updates") return (location.href = "/updates");
    if (t === "contact") return window.__landaiNav ? window.__landaiNav("contact") : (location.href = "/contact");
  };
  return (
    <div style={{ background: PAPER, padding: narrow ? "34px 20px 28px" : "44px 56px 34px", borderTop: `1px solid ${BORDER}` }}>
      <div style={{ maxWidth: 1180, margin: "0 auto", display: "flex", flexDirection: narrow ? "column" : "row", justifyContent: "space-between", gap: 22 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 11 }}><Mark size={38} /><span style={{ fontSize: 21, fontWeight: 800, color: INK }}>LandAI</span></div>
        <div style={{ display: "flex", gap: narrow ? 18 : 28, flexWrap: "wrap", alignItems: "center" }}>
          {links.map(([label, target]) => <span key={label} onClick={() => go(target)} style={{ fontSize: 15.5, fontWeight: 700, color: INK, cursor: "pointer" }}>{label}</span>)}
          <div style={{ display: "flex", gap: 16, alignItems: "center", color: INK }}>
            <a href="https://www.linkedin.com/company/landai-app/" target="_blank" rel="noopener noreferrer" aria-label="LandAI on LinkedIn" style={{ color: "inherit", display: "inline-flex" }}>
              <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M4.98 3.5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5zM3 9h4v12H3zM9 9h3.8v1.64h.05c.53-1 1.83-2.05 3.76-2.05 4.02 0 4.76 2.64 4.76 6.08V21h-4v-4.87c0-1.16-.02-2.66-1.62-2.66-1.63 0-1.88 1.27-1.88 2.58V21H9z"/></svg>
            </a>
            <a href="https://www.instagram.com/landaiapp?utm_source=qr" target="_blank" rel="noopener noreferrer" aria-label="LandAI on Instagram" style={{ color: "inherit", display: "inline-flex" }}>
              <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><rect x="2.5" y="2.5" width="19" height="19" rx="5"/><circle cx="12" cy="12" r="4.2"/><circle cx="17.6" cy="6.4" r="1.1" fill="currentColor" stroke="none"/></svg>
            </a>
            <a href="https://x.com/landaiapp?s=11" target="_blank" rel="noopener noreferrer" aria-label="LandAI on X" style={{ color: "inherit", display: "inline-flex" }}>
              <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24h-6.66l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zM17.083 19.77h1.833L7.084 4.126H5.117z"/></svg>
            </a>
          </div>
        </div>
      </div>
    </div>
  );
}

function Updates() {
  const narrow = useNarrow(1040);
  const [entries, setEntries] = useState(null); // null = loading, "error" = failed, [] = none posted
  const [selectedId, setSelectedId] = useState(null);
  useEffect(() => {
    let live = true;
    fetch(UPDATES_URL, { cache: "no-cache" })
      .then((r) => { if (!r.ok) throw new Error(String(r.status)); return r.json(); })
      .then((d) => {
        if (!live) return;
        const list = ((d && d.entries) || [])
          .map((e) => ({ ...e, counties: Array.isArray(e.counties) ? e.counties : [] }))
          .sort((a, b) => String(b.date).localeCompare(String(a.date)));
        setEntries(list);
        setSelectedId(list.length ? list[0].id : null);
      })
      .catch(() => { if (live) setEntries("error"); });
    return () => { live = false; };
  }, []);
  const list = Array.isArray(entries) ? entries : [];
  const selected = useMemo(() => list.find((e) => e.id === selectedId) || null, [list, selectedId]);
  const countyCount = useMemo(() => { const s = new Set(); list.forEach((e) => countyList(e).forEach((c) => s.add(c.fips))); return s.size; }, [list]);
  return (
    <div style={{ background: PAPER, fontFamily: SANS, minHeight: "100vh" }}>
      <Nav narrow={narrow} />
      <Hero entryCount={list.length} countyCount={countyCount} narrow={narrow} />
      <section style={{ maxWidth: 1180, margin: "0 auto", padding: narrow ? "0 20px 56px" : "0 56px 72px", display: "grid", gridTemplateColumns: narrow ? "1fr" : "minmax(0, 1.1fr) minmax(0, 1fr)", gap: narrow ? 20 : 28, alignItems: "start" }}>
        <div style={{ position: narrow ? "static" : "sticky", top: 96 }}>
          <UpdateMap entry={selected} />
        </div>
        <div style={{ display: "grid", gap: 16 }}>
          {entries === null && <div style={{ color: GRAY, fontFamily: MONO, fontSize: 12 }}>Loading updates…</div>}
          {entries === "error" && <div style={{ color: GRAY }}>Couldn't load the updates list. Please refresh the page.</div>}
          {Array.isArray(entries) && entries.length === 0 && <div style={{ color: GRAY }}>No updates posted yet.</div>}
          {list.map((e) => <EntryCard key={e.id} entry={e} selected={e.id === selectedId} onSelect={() => setSelectedId(e.id)} narrow={narrow} />)}
        </div>
      </section>
      <Footer narrow={narrow} />
    </div>
  );
}

window.Updates = Updates;
if (!window.__landaiHost && document.getElementById("root")) {
  ReactDOM.createRoot(document.getElementById("root")).render(<Updates />);
}
})();
