/* RADAR1 — full-frame tile packages (fill the hero inset) */
const DSt = window.Radar1DesignSystem_1f4661;

/* Shared package chrome: header band + washed backdrop */
function TileShell({ kicker, title, accent = "var(--signal-cyan)", right = null, children }) {
  return (
    <div style={{ position: "absolute", inset: 0, background: "linear-gradient(150deg, #0D1B30 0%, #081120 55%, #060C17 100%)", fontFamily: "var(--font-ui)", display: "flex", flexDirection: "column" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 16, padding: "18px 34px 0", animation: "r1-enter-down 380ms var(--ease-broadcast) both" }}>
        <div style={{ display: "flex", alignItems: "center", padding: "6px 13px", background: accent, color: title === undefined ? "#fff" : (accent === "var(--signal-amber)" ? "var(--text-on-amber)" : "#fff"), fontWeight: "var(--fw-black)", fontSize: 13.5, letterSpacing: "var(--ls-widest)", borderRadius: "var(--sweep-sm)" }}>{kicker}</div>
        <div style={{ fontFamily: "var(--font-display)", fontWeight: 800, fontSize: 33, letterSpacing: "var(--ls-tightest)", color: "var(--text-strong)" }}>{title}</div>
        <div style={{ marginLeft: "auto", marginRight: 216 }}>{right}</div>
      </div>
      <div style={{ flex: 1, minHeight: 0, padding: "20px 34px 30px", animation: "r1-enter-up 420ms var(--ease-broadcast) 80ms both" }}>{children}</div>
    </div>
  );
}

/* ---------- WEATHER ---------- */
function TileWeather({ h24 }) {
  const now = useNow();
  const L = window.R1DATA.locales || [];
  const featIdx = useCycle(L.length, 7000);
  const feat = L[featIdx];
  if (!feat) return null; /* no live locales yet */
  const grid = Array.from({ length: 6 }, (_, k) => L[(featIdx + 1 + k) % L.length]);
  useLucide([featIdx]);
  return (
    <TileShell kicker="WEATHER" title="World Conditions" accent="var(--signal-cyan)"
      right={<Eyebrow>{L.length} CITIES · UPDATED {fmtTime(now, "UTC", true)} UTC</Eyebrow>}>
      <div style={{ display: "grid", gridTemplateColumns: "440px 1fr", gap: 24, height: "100%" }}>
        {/* featured locale */}
        <div key={featIdx} style={{ position: "relative", overflow: "hidden", border: "1px solid var(--border-soft)", borderLeft: `3px solid ${feat.alert ? "var(--signal-amber)" : "var(--signal-cyan)"}`, borderRadius: "var(--sweep-lg)", background: "radial-gradient(130% 120% at 20% 0%, #10233C 0%, #081120 70%)", padding: "26px 28px", display: "flex", flexDirection: "column", animation: "r1-feedfade 450ms var(--ease-out) both" }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
            <div>
              <Eyebrow color="var(--text-muted)">{feat.city.toUpperCase()}</Eyebrow>
              <div style={{ color: "var(--text-faint)", fontSize: 13, fontWeight: "var(--fw-semibold)", letterSpacing: "var(--ls-wide)", marginTop: 3, fontVariantNumeric: "tabular-nums" }}>LOCAL {fmtTime(now, feat.tz, h24)}</div>
            </div>
            <i data-lucide={feat.icon} style={{ width: 44, height: 44, color: feat.alert ? "var(--signal-amber)" : "var(--signal-cyan)" }} />
          </div>
          <div style={{ marginTop: "auto" }}>
            <div style={{ fontFamily: "var(--font-display)", fontWeight: 900, fontSize: 120, lineHeight: 0.95, letterSpacing: "var(--ls-tightest)", color: "var(--text-strong)", fontVariantNumeric: "tabular-nums" }}>{feat.temp}°</div>
            <div style={{ color: "var(--text-body)", fontWeight: "var(--fw-medium)", fontSize: 22, marginTop: 8 }}>{feat.cond}</div>
            <div style={{ display: "flex", gap: 22, marginTop: 10, color: "var(--text-muted)", fontWeight: "var(--fw-semibold)", fontSize: 15, letterSpacing: "var(--ls-wide)", fontVariantNumeric: "tabular-nums" }}>
              <span>H {feat.hi}°</span><span>L {feat.lo}°</span>
              {feat.alert && <span style={{ color: "var(--signal-amber)" }}>▲ ADVISORY IN EFFECT</span>}
            </div>
          </div>
        </div>
        {/* grid of others */}
        <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gridTemplateRows: "1fr 1fr", gap: 16 }}>
          {grid.map((l, i) => (
            <div key={l.city} style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", padding: "14px 16px", background: "rgba(15,24,38,0.7)", border: "1px solid var(--border-hairline)", borderLeft: `3px solid ${l.alert ? "var(--signal-amber)" : "var(--border-soft)"}`, borderRadius: "var(--sweep-md)" }}>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 8 }}>
                <Eyebrow style={{ fontSize: 12 }}>{l.city.toUpperCase()}</Eyebrow>
                <i data-lucide={l.icon} style={{ width: 20, height: 20, color: l.alert ? "var(--signal-amber)" : "var(--signal-cyan)", flex: "none" }} />
              </div>
              <div style={{ display: "flex", alignItems: "baseline", gap: 10 }}>
                <span style={{ fontFamily: "var(--font-display)", fontWeight: 800, fontSize: 38, color: "var(--text-strong)", fontVariantNumeric: "tabular-nums", letterSpacing: "var(--ls-tight)" }}>{l.temp}°</span>
                <span style={{ color: "var(--text-muted)", fontSize: 13.5, fontWeight: "var(--fw-medium)", overflow: "hidden", whiteSpace: "nowrap", textOverflow: "ellipsis" }}>{l.cond}</span>
              </div>
            </div>
          ))}
        </div>
      </div>
    </TileShell>
  );
}

/* ---------- SPORTS ---------- */
function TileSports() {
  const S = window.R1DATA.sports;
  if (!S || !S.board || !(S.board.games || []).length) return null; /* nothing on the board — stand down */
  return (
    <TileShell kicker="SPORTS" title="Scoreboard" accent="var(--signal-green)" right={<Eyebrow>{S.board.league}</Eyebrow>}>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 380px", gap: 24, height: "100%" }}>
        <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
          {S.board.games.map((g, i) => (
            <div key={i} style={{ flex: 1, display: "grid", gridTemplateColumns: "1fr auto auto auto 1fr 130px", alignItems: "center", gap: 18, padding: "0 22px", background: "rgba(15,24,38,0.7)", border: "1px solid var(--border-hairline)", borderLeft: `3px solid ${g.live ? "var(--signal-green)" : "var(--border-soft)"}`, borderRadius: "var(--sweep-md)", animation: `r1-enter-up 380ms var(--ease-broadcast) ${i * 60}ms both` }}>
              <div style={{ fontFamily: "var(--font-display)", fontWeight: 800, fontSize: 27, color: "var(--text-strong)", letterSpacing: "var(--ls-wide)" }}>{g.a}</div>
              <div style={{ fontFamily: "var(--font-display)", fontWeight: 900, fontSize: 33, color: g.as > g.bs ? "var(--text-strong)" : "var(--text-muted)", fontVariantNumeric: "tabular-nums" }}>{g.as}</div>
              <div style={{ color: "var(--text-faint)", fontWeight: "var(--fw-bold)", fontSize: 15 }}>–</div>
              <div style={{ fontFamily: "var(--font-display)", fontWeight: 900, fontSize: 33, color: g.bs > g.as ? "var(--text-strong)" : "var(--text-muted)", fontVariantNumeric: "tabular-nums" }}>{g.bs}</div>
              <div style={{ fontFamily: "var(--font-display)", fontWeight: 800, fontSize: 27, color: "var(--text-strong)", letterSpacing: "var(--ls-wide)" }}>{g.b}</div>
              <div style={{ display: "flex", alignItems: "center", gap: 8, justifyContent: "flex-end", color: g.live ? "var(--signal-green)" : "var(--text-muted)", fontWeight: "var(--fw-bold)", fontSize: 14, letterSpacing: "var(--ls-wider)", fontVariantNumeric: "tabular-nums" }}>
                {g.live && <LiveDot color="var(--signal-green)" size={7} />}{g.status}
              </div>
            </div>
          ))}
        </div>
        <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
          <Eyebrow color="var(--text-muted)">AROUND THE WORLD</Eyebrow>
          {S.fixtures.map((f, i) => (
            <div key={i} style={{ flex: 1, padding: "14px 18px", background: "rgba(15,24,38,0.7)", border: "1px solid var(--border-hairline)", borderLeft: "3px solid var(--signal-green)", borderRadius: "var(--sweep-md)", animation: `r1-enter-left 380ms var(--ease-broadcast) ${120 + i * 70}ms both` }}>
              <Eyebrow color="var(--signal-green)" style={{ fontSize: 11.5, marginBottom: 6 }}>{f.label}</Eyebrow>
              <div style={{ color: "var(--text-body)", fontWeight: "var(--fw-medium)", fontSize: 17.5, lineHeight: "var(--lh-normal)" }}>{f.text}</div>
            </div>
          ))}
        </div>
      </div>
    </TileShell>
  );
}

/* ---------- NEWS ---------- */
function TileNews() {
  const stories = window.R1DATA.stories || [];
  if (!stories.length) return null; /* nothing to report — stand down */
  const feat = stories[0], rest = stories.slice(1, 5);
  return (
    <TileShell kicker="TOP STORIES" title="This Hour" accent="var(--radar-red)">
      <div style={{ display: "grid", gridTemplateColumns: "480px 1fr", gap: 24, height: "100%" }}>
        <div style={{ position: "relative", border: "1px solid var(--border-soft)", borderRadius: "var(--sweep-lg)", overflow: "hidden" }}>
          <SimFeed scene="field" />
          <div style={{ position: "absolute", inset: 0, background: "var(--scrim-bottom)" }} />
          <div style={{ position: "absolute", left: 22, right: 22, bottom: 20 }}>
            <Eyebrow color="var(--signal-amber)" style={{ marginBottom: 8 }}>{feat.kicker}</Eyebrow>
            <div style={{ color: "var(--text-strong)", fontFamily: "var(--font-display)", fontWeight: 800, fontSize: 30, lineHeight: "var(--lh-snug)", letterSpacing: "var(--ls-tight)" }}>{feat.text}</div>
          </div>
        </div>
        <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
          {rest.map((s, i) => (
            <div key={i} style={{ flex: 1, display: "flex", alignItems: "center", gap: 20, padding: "0 22px", background: "rgba(15,24,38,0.7)", border: "1px solid var(--border-hairline)", borderLeft: `3px solid ${s.accent}`, borderRadius: "var(--sweep-md)", animation: `r1-enter-up 380ms var(--ease-broadcast) ${i * 70}ms both` }}>
              <div style={{ fontFamily: "var(--font-display)", fontWeight: 900, fontSize: 34, color: "var(--grey-600)", fontVariantNumeric: "tabular-nums" }}>{String(i + 2).padStart(2, "0")}</div>
              <div style={{ minWidth: 0 }}>
                <Eyebrow style={{ fontSize: 11.5, color: s.accent, marginBottom: 4 }}>{s.kicker} · {s.time}</Eyebrow>
                <div style={{ color: "var(--text-strong)", fontWeight: "var(--fw-semibold)", fontSize: 21, lineHeight: "var(--lh-snug)" }}>{s.text}</div>
              </div>
            </div>
          ))}
        </div>
      </div>
    </TileShell>
  );
}

/* ---------- STUDIO — ambient network scene, full frame ---------- */
/* No TileShell chrome and nothing overlaid: the scene carries its own faint
   RADAR1 wall brand, and HeroInset already draws the standard hairline. */
function TileStudio() {
  return <SimFeed scene="studio" />;
}

/* ---------- ELECTIONS ---------- */
function BoPBar({ label, d, r, und, forControl, total }) {
  const pct = (n) => (n / total) * 100;
  return (
    <div>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 8 }}>
        <Eyebrow color="var(--text-body)">{label}</Eyebrow>
        <span style={{ color: "var(--text-muted)", fontSize: 13, fontWeight: "var(--fw-semibold)", letterSpacing: "var(--ls-wider)" }}>{forControl} FOR CONTROL</span>
      </div>
      <div style={{ position: "relative", display: "flex", height: 42, borderRadius: "var(--sweep-sm)", overflow: "hidden", border: "1px solid var(--border-hairline)" }}>
        <div style={{ width: pct(d) + "%", background: "var(--signal-cyan)", display: "flex", alignItems: "center", paddingLeft: 12, color: "#04121C", fontFamily: "var(--font-display)", fontWeight: 900, fontSize: 22, fontVariantNumeric: "tabular-nums", transition: "width 600ms var(--ease-broadcast)" }}>{d}</div>
        <div style={{ width: pct(und) + "%", background: "var(--grey-700)" }} />
        <div style={{ width: pct(r) + "%", background: "var(--radar-red)", display: "flex", alignItems: "center", justifyContent: "flex-end", paddingRight: 12, color: "#fff", fontFamily: "var(--font-display)", fontWeight: 900, fontSize: 22, fontVariantNumeric: "tabular-nums", transition: "width 600ms var(--ease-broadcast)" }}>{r}</div>
        <div style={{ position: "absolute", left: "50%", top: 0, bottom: 0, width: 2, background: "rgba(255,255,255,0.85)" }} />
      </div>
      <div style={{ display: "flex", justifyContent: "space-between", marginTop: 6, fontSize: 12.5, fontWeight: "var(--fw-bold)", letterSpacing: "var(--ls-wider)" }}>
        <span style={{ color: "var(--signal-cyan)" }}>DEM</span>
        <span style={{ color: "var(--text-faint)" }}>{und} UNDECIDED</span>
        <span style={{ color: "var(--radar-red)" }}>GOP</span>
      </div>
    </div>
  );
}

function TileElections() {
  const E = window.R1DATA.election;
  if (!E || !(E.races || []).length) return null; /* no live election content */
  return (
    <TileShell kicker={"ELECTION NIGHT " + E.year} title="Balance of Power" accent={E.accent}
      right={<span style={{ display: "inline-flex", alignItems: "center", gap: 8 }}><LiveDot /><Eyebrow color="var(--radar-red)">LIVE RESULTS</Eyebrow></span>}>
      <div style={{ display: "grid", gridTemplateColumns: "460px 1fr", gap: 28, height: "100%" }}>
        <div style={{ display: "flex", flexDirection: "column", gap: 26, justifyContent: "center" }}>
          <BoPBar label="US SENATE" d={E.senate.d} r={E.senate.r} und={E.senate.und} forControl={E.senate.forControl} total={100} />
          <BoPBar label="US HOUSE" d={E.house.d} r={E.house.r} und={E.house.und} forControl={E.house.forControl} total={435} />
          <div style={{ color: "var(--text-faint)", fontSize: 12.5, fontWeight: "var(--fw-semibold)", letterSpacing: "var(--ls-wider)" }}>RADAR1 DECISION DESK · PROJECTIONS UPDATE AS VOTES COUNT</div>
        </div>
        <div style={{ display: "flex", flexDirection: "column", gap: 9 }}>
          <div style={{ display: "grid", gridTemplateColumns: "1.1fr 70px 1fr 1fr 86px", gap: 12, padding: "0 16px", color: "var(--text-faint)", fontSize: 11.5, fontWeight: "var(--fw-bold)", letterSpacing: "var(--ls-widest)" }}>
            <span>SENATE RACE</span><span>IN</span><span>DEM</span><span>GOP</span><span style={{ textAlign: "right" }}>CALL</span>
          </div>
          {E.races.map((r, i) => {
            const dLead = r.d.pct > r.r.pct;
            return (
              <div key={r.state} style={{ flex: 1, display: "grid", gridTemplateColumns: "1.1fr 70px 1fr 1fr 86px", gap: 12, alignItems: "center", padding: "0 16px", background: "rgba(15,24,38,0.7)", border: "1px solid var(--border-hairline)", borderLeft: `3px solid ${r.call === "D" ? "var(--signal-cyan)" : r.call === "R" ? "var(--radar-red)" : "var(--border-soft)"}`, borderRadius: "var(--sweep-md)", animation: `r1-enter-up 360ms var(--ease-broadcast) ${i * 55}ms both`, fontVariantNumeric: "tabular-nums" }}>
                <div style={{ color: "var(--text-strong)", fontWeight: "var(--fw-bold)", fontSize: 18.5 }}>{r.state}</div>
                <div style={{ color: "var(--text-muted)", fontWeight: "var(--fw-semibold)", fontSize: 14.5 }}>{r.pctIn}%</div>
                <div style={{ color: dLead ? "var(--signal-cyan)" : "var(--text-muted)", fontWeight: dLead ? "var(--fw-bold)" : "var(--fw-medium)", fontSize: 16.5 }}>{r.d.name} {r.d.pct}%</div>
                <div style={{ color: !dLead ? "var(--radar-red)" : "var(--text-muted)", fontWeight: !dLead ? "var(--fw-bold)" : "var(--fw-medium)", fontSize: 16.5 }}>{r.r.name} {r.r.pct}%</div>
                <div style={{ textAlign: "right" }}>
                  {r.call
                    ? <span style={{ padding: "3px 9px", background: r.call === "D" ? "var(--signal-cyan)" : "var(--radar-red)", color: r.call === "D" ? "#04121C" : "#fff", fontWeight: "var(--fw-black)", fontSize: 12, letterSpacing: "var(--ls-wider)", borderRadius: "0 4px 4px 0" }}>{r.call === "D" ? "DEM" : "GOP"}</span>
                    : <span style={{ color: "var(--text-faint)", fontSize: 12, fontWeight: "var(--fw-bold)", letterSpacing: "var(--ls-wider)" }}>—</span>}
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </TileShell>
  );
}

/* ---------- SOCIAL / VIEWER DESK ---------- */
function TileSocial() {
  const msgs = window.R1DATA.social;
  const shown = useCycle(1000, 5000);
  if (!msgs || !msgs.length) return null; /* guard AFTER hooks so the hook order stays stable */
  const list = Array.from({ length: 4 }, (_, k) => msgs[(shown + k) % msgs.length]);
  return (
    <TileShell kicker="VIEWER DESK" title="#Radar1Live" accent="var(--signal-violet)"
      right={<Eyebrow>MODERATED · GLOBAL FEED</Eyebrow>}>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, height: "100%", alignContent: "stretch" }}>
        {list.map((m, i) => (
          <div key={m.handle + shown} style={{ display: "flex", gap: 16, padding: "18px 20px", background: "rgba(15,24,38,0.7)", border: "1px solid var(--border-hairline)", borderLeft: "3px solid var(--signal-violet)", borderRadius: "var(--sweep-md)", animation: `r1-enter-up 420ms var(--ease-broadcast) ${i * 80}ms both` }}>
            <div style={{ width: 46, height: 46, flex: "none", display: "flex", alignItems: "center", justifyContent: "center", background: "var(--grey-700)", borderRadius: "0 8px 8px 0", fontFamily: "var(--font-display)", fontWeight: 900, fontSize: 20, color: "var(--text-body)" }}>
              {m.handle[1].toUpperCase()}
            </div>
            <div style={{ minWidth: 0 }}>
              <div style={{ display: "flex", alignItems: "baseline", gap: 10, marginBottom: 5 }}>
                <span style={{ color: "var(--text-strong)", fontWeight: "var(--fw-bold)", fontSize: 17 }}>{m.handle}</span>
                <span style={{ color: "var(--text-faint)", fontSize: 12.5, fontWeight: "var(--fw-semibold)", letterSpacing: "var(--ls-wider)" }}>{m.loc.toUpperCase()} · {m.time}</span>
              </div>
              <div style={{ color: "var(--text-body)", fontSize: 17.5, lineHeight: "var(--lh-normal)", fontWeight: "var(--fw-medium)" }}>{m.msg}</div>
            </div>
          </div>
        ))}
      </div>
    </TileShell>
  );
}

/* ---------- CUT SPOT — produced storyboard, plays window.R1DATA.cutspot ---------- */

const CUTSPOT_FRAME = {
  position: "absolute", inset: 0, overflow: "hidden",
  background: "radial-gradient(130% 120% at 22% 0%, #10233C 0%, #081120 70%)",
};

/* "41 CHICKS — a record season" -> { stat, unit, body } when the slide carries
   only `text`; explicit slide.stat / slide.unit always win */
function cutspotStatParts(slide) {
  if (slide.stat) return { stat: slide.stat, unit: slide.unit || "", body: slide.text || "" };
  const text = String(slide.text || "");
  const sep = text.search(/[—–:·]/);
  const head = (sep >= 0 ? text.slice(0, sep) : text).trim();
  const body = sep >= 0 ? text.slice(sep + 1).trim() : "";
  const m = head.match(/^([~$+\-]?[\d][\d.,]*%?)\s*([\s\S]*)$/);
  if (!m) return null;
  const unit = m[2].trim();
  if (unit.length > 24) return { stat: m[1], unit: "", body: body ? unit + " — " + body : unit };
  return { stat: m[1], unit, body };
}

function CutSpotSlide({ slide, accent }) {
  const type = slide.type;

  if (type === "image" && slide.src) {
    return (
      <div style={CUTSPOT_FRAME}>
        <div style={{ position: "absolute", inset: 0, background: `url("${slide.src}") center / cover no-repeat` }} />
        <div style={{ position: "absolute", left: 0, right: 0, bottom: 0, height: 200, background: "var(--scrim-bottom)" }} />
        {slide.text && (
          <div style={{ position: "absolute", left: 30, right: 30, bottom: slide.credit ? 46 : 24, color: "var(--text-strong)", fontFamily: "var(--font-display)", fontWeight: 800, fontSize: 31, lineHeight: "var(--lh-snug)", letterSpacing: "var(--ls-tight)" }}>{slide.text}</div>
        )}
        {slide.credit && (
          <div style={{ position: "absolute", left: 30, bottom: 18, color: "var(--text-faint)", fontWeight: "var(--fw-semibold)", fontSize: 12, letterSpacing: "var(--ls-wider)", textTransform: "uppercase" }}>{slide.credit}</div>
        )}
      </div>
    );
  }

  if (type === "stat") {
    const parts = cutspotStatParts(slide);
    if (parts) {
      return (
        <div style={{ ...CUTSPOT_FRAME, display: "flex", flexDirection: "column", justifyContent: "center", gap: 22, padding: "0 64px" }}>
          <div style={{ display: "flex", alignItems: "baseline", gap: 22 }}>
            <span style={{ fontFamily: "var(--font-display)", fontWeight: 900, fontSize: 150, lineHeight: 1, letterSpacing: "var(--ls-tightest)", color: accent, fontVariantNumeric: "tabular-nums" }}>{parts.stat}</span>
            {parts.unit && <span style={{ color: "var(--text-muted)", fontWeight: "var(--fw-black)", fontSize: 28, letterSpacing: "var(--ls-widest)", textTransform: "uppercase" }}>{parts.unit}</span>}
          </div>
          {parts.body && <div style={{ color: "var(--text-strong)", fontWeight: "var(--fw-semibold)", fontSize: 31, lineHeight: "var(--lh-snug)", letterSpacing: "var(--ls-tight)", maxWidth: 900 }}>{parts.body}</div>}
        </div>
      );
    }
    /* un-parseable stat — fall through to the title treatment */
  }

  if (type === "quote") {
    return (
      <div style={{ ...CUTSPOT_FRAME, display: "flex", flexDirection: "column", justifyContent: "center", gap: 26, padding: "0 72px" }}>
        <span aria-hidden="true" style={{ position: "absolute", right: 46, top: 4, fontFamily: "var(--font-display)", fontWeight: 900, fontSize: 260, lineHeight: 1, color: accent, opacity: 0.12 }}>”</span>
        <div style={{ color: "var(--text-strong)", fontWeight: "var(--fw-semibold)", fontSize: 42, lineHeight: "var(--lh-snug)", letterSpacing: "var(--ls-tight)", maxWidth: 980 }}>“{slide.text}”</div>
        {slide.credit && (
          <div style={{ color: accent, fontWeight: "var(--fw-bold)", fontSize: 21, letterSpacing: "var(--ls-wider)", textTransform: "uppercase" }}>{slide.credit}</div>
        )}
      </div>
    );
  }

  if (type === "list") {
    const items = (Array.isArray(slide.items) ? slide.items : String(slide.text || "").split(/\n+/))
      .map((s) => String(s).trim()).filter(Boolean).slice(0, 5);
    return (
      <div style={{ ...CUTSPOT_FRAME, display: "flex", flexDirection: "column", justifyContent: "center", gap: 14, padding: "0 56px" }}>
        {items.map((it, i) => (
          <div key={i} style={{ display: "flex", alignItems: "center", gap: 20, padding: "18px 24px", background: "rgba(15,24,38,0.7)", border: "1px solid var(--border-hairline)", borderLeft: `3px solid ${accent}`, borderRadius: "var(--sweep-md)", animation: `r1-enter-up 380ms var(--ease-broadcast) ${i * 70}ms both` }}>
            <span style={{ color: accent, fontSize: 13, flex: "none" }}>◆</span>
            <span style={{ color: "var(--text-strong)", fontWeight: "var(--fw-semibold)", fontSize: 27, lineHeight: "var(--lh-snug)" }}>{it}</span>
          </div>
        ))}
      </div>
    );
  }

  /* "title" and anything unrecognized: the big display card */
  return (
    <div style={{ ...CUTSPOT_FRAME, display: "flex", alignItems: "center", padding: "0 64px" }}>
      <div style={{ position: "absolute", left: 0, top: 0, bottom: 0, width: 5, background: accent }} />
      <div style={{ fontFamily: "var(--font-display)", fontWeight: 900, fontSize: 66, lineHeight: "var(--lh-tight)", letterSpacing: "var(--ls-tightest)", color: "var(--text-strong)", maxWidth: 1020 }}>{slide.text}</div>
    </div>
  );
}

function TileCutSpot() {
  const spot = window.R1DATA.cutspot;
  const slides = spot && Array.isArray(spot.slides) && spot.slides.length ? spot.slides : null;
  /* content signature, not object identity — SSE re-sends must not restart the deck */
  const sig = React.useMemo(() => { try { return spot ? JSON.stringify(spot) : ""; } catch (e) { return ""; } }, [spot]);
  const [idx, setIdx] = React.useState(0);
  React.useEffect(() => { setIdx(0); }, [sig]);
  React.useEffect(() => {
    const list = (window.R1DATA.cutspot || {}).slides;
    if (!Array.isArray(list) || !list.length) return;
    const cur = list[idx % list.length] || {};
    const ms = Math.max(2, Number(cur.duration_s) || 8) * 1000;
    const t = setTimeout(() => setIdx((i) => i + 1), ms); /* modulo below — the deck loops */
    return () => clearTimeout(t);
  }, [sig, idx]);
  if (!slides) return null;
  const shown = idx % slides.length;
  const slide = slides[shown];
  const accent = spot.accent || "var(--signal-cyan)";
  const sources = (spot.sources || []).map((s) => String(s).toUpperCase()).filter(Boolean);
  return (
    <TileShell kicker={spot.kicker || "SPOT"} title={spot.headline} accent={accent}
      right={<Eyebrow>SPOT · {shown + 1} / {slides.length}</Eyebrow>}>
      <div style={{ position: "relative", height: "100%", display: "flex", flexDirection: "column" }}>
        <div key={shown + "-" + sig.length} style={{ flex: 1, minHeight: 0, position: "relative", border: "1px solid var(--border-soft)", borderRadius: "var(--sweep-lg)", overflow: "hidden", animation: "r1-feedfade 450ms var(--ease-out) both" }}>
          <CutSpotSlide slide={slide} accent={accent} />
        </div>
        <div style={{ flex: "none", display: "flex", alignItems: "center", gap: 16, paddingTop: 14 }}>
          {sources.length > 0 && (
            <span style={{ color: "var(--text-faint)", fontWeight: "var(--fw-bold)", fontSize: 13, letterSpacing: "var(--ls-widest)", textTransform: "uppercase" }}>SOURCES · {sources.join(" · ")}</span>
          )}
          <div style={{ marginLeft: "auto", display: "flex", gap: 6 }}>
            {slides.map((_, i) => (
              <span key={i} style={{ width: i === shown ? 24 : 8, height: 4, borderRadius: "0 2px 2px 0", background: i === shown ? accent : "var(--grey-700)", transition: "width 320ms var(--ease-out)" }} />
            ))}
          </div>
        </div>
      </div>
    </TileShell>
  );
}

const TILES = {
  weather:   { label: "WEATHER",   comp: TileWeather },
  sports:    { label: "SPORTS",    comp: TileSports },
  news:      { label: "NEWS",      comp: TileNews },
  studio:    { label: "STUDIO",    comp: TileStudio },
  elections: { label: "ELECTIONS", comp: TileElections },
  social:    { label: "SOCIAL",    comp: TileSocial },
  cutspot:   { label: "SPOT",      comp: TileCutSpot },
};

Object.assign(window, { TILES, TileWeather, TileSports, TileNews, TileStudio, TileElections, TileSocial, TileCutSpot, BoPBar, TileShell });
