/* RADAR1 — rails, info bar, ticker stacks (the L-shaped furniture) */
const DSr = window.Radar1DesignSystem_1f4661;

/* ================= SIDE RAIL — editorial card deck ================= */

/* abstract still-image placeholders (gradient washes, no illustration) */
const RAIL_ART = {
  lunar:     { base: "linear-gradient(150deg, #1A2436 0%, #0B1220 60%, #060C17 100%)", glow: "radial-gradient(circle at 72% 30%, rgba(203,211,222,0.32), rgba(203,211,222,0.05) 45%, transparent 62%)" },
  heat:      { base: "linear-gradient(150deg, #3A1420 0%, #1A0F1A 55%, #0A0A14 100%)", glow: "radial-gradient(circle at 30% 35%, rgba(255,140,0,0.4), rgba(228,3,46,0.12) 50%, transparent 68%)" },
  harbor:    { base: "linear-gradient(170deg, #0E2233 0%, #0A1524 55%, #071019 100%)", glow: "radial-gradient(ellipse at 50% 78%, rgba(31,168,224,0.28), transparent 60%)" },
  community: { base: "linear-gradient(150deg, #2A2314 0%, #171507 55%, #0C0B06 100%)", glow: "radial-gradient(circle at 65% 40%, rgba(255,176,0,0.28), transparent 58%)" },
  wildlife:  { base: "linear-gradient(160deg, #10281E 0%, #0A1A14 55%, #060F0B 100%)", glow: "radial-gradient(circle at 40% 45%, rgba(23,178,106,0.26), transparent 60%)" },
};

const railInk = (accent) => (/cyan|amber|green/.test(accent) ? "#04121C" : "#fff");

function CardKicker({ text, accent }) {
  return (
    <span style={{ alignSelf: "flex-start", padding: "5px 14px", background: accent, color: railInk(accent), fontWeight: "var(--fw-black)", fontSize: 15, letterSpacing: "var(--ls-widest)", textTransform: "uppercase", borderRadius: "var(--sweep-sm)", whiteSpace: "nowrap" }}>{text}</span>
  );
}

function RailPhotoCard({ card }) {
  const art = RAIL_ART[card.art] || RAIL_ART.harbor;
  /* real image (live /media/ asset) when present + loadable; gradient art otherwise */
  const [imgOk, setImgOk] = React.useState(true);
  React.useEffect(() => {
    setImgOk(true);
    if (!card.image) return;
    const probe = new Image();
    probe.onerror = () => setImgOk(false);
    probe.src = card.image;
    return () => { probe.onerror = null; };
  }, [card.image]);
  const photo = !!card.image && imgOk;
  return (
    <React.Fragment>
      <div style={{ flex: 1.1, position: "relative", minHeight: 0, background: photo ? `url("${card.image}") center / cover no-repeat, ${art.base}` : art.base }}>
        {!photo && <div style={{ position: "absolute", inset: 0, background: art.glow }} />}
        <div style={{ position: "absolute", left: 0, right: 0, bottom: 0, height: 70, background: "var(--scrim-bottom)" }} />
        <div style={{ position: "absolute", left: 18, top: 18 }}><CardKicker text={card.kicker} accent={card.accent} /></div>
        {card.credit && (
          <span style={{ position: "absolute", left: 16, bottom: 12, maxWidth: card.loc ? "52%" : "85%", color: "var(--text-faint)", fontWeight: "var(--fw-semibold)", fontSize: 11, letterSpacing: "var(--ls-wider)", textTransform: "uppercase", overflow: "hidden", whiteSpace: "nowrap", textOverflow: "ellipsis" }}>{card.credit}</span>
        )}
        {card.loc && (
          <span style={{ position: "absolute", right: 16, bottom: 12, color: "var(--text-muted)", fontWeight: "var(--fw-bold)", fontSize: 14.5, letterSpacing: "var(--ls-wider)", textTransform: "uppercase" }}>▸ {card.loc}</span>
        )}
      </div>
      <div style={{ padding: "20px 22px 22px", display: "flex", flexDirection: "column", gap: 12 }}>
        <div style={{ color: "var(--text-strong)", fontWeight: "var(--fw-bold)", fontSize: 30, lineHeight: "var(--lh-tight)", letterSpacing: "var(--ls-tight)", display: "-webkit-box", WebkitBoxOrient: "vertical", WebkitLineClamp: 3, overflow: "hidden" }}>{card.headline}</div>
        <div style={{ color: "var(--text-muted)", fontWeight: "var(--fw-medium)", fontSize: 19.5, lineHeight: "var(--lh-snug)", display: "-webkit-box", WebkitBoxOrient: "vertical", WebkitLineClamp: 4, overflow: "hidden" }}>{card.dek}</div>
      </div>
    </React.Fragment>
  );
}

function RailChartCard({ card }) {
  const pts = card.chart.points;
  const max = Math.max(...pts.map((p) => p.v));
  return (
    <div style={{ flex: 1, minHeight: 0, display: "flex", flexDirection: "column", gap: 14, padding: "20px 22px" }}>
      <CardKicker text={card.kicker} accent={card.accent} />
      <div style={{ color: "var(--text-strong)", fontWeight: "var(--fw-bold)", fontSize: 29, lineHeight: "var(--lh-tight)", letterSpacing: "var(--ls-tight)" }}>{card.headline}</div>
      <div style={{ color: "var(--text-faint)", fontWeight: "var(--fw-semibold)", fontSize: 15.5, letterSpacing: "var(--ls-wide)" }}>{card.sub}</div>
      <div style={{ flex: 1, minHeight: 0, display: "flex", alignItems: "stretch", gap: 14, paddingTop: 8 }}>
        {pts.map((p, i) => {
          const hot = i === pts.length - 1;
          return (
            <div key={i} style={{ flex: 1, display: "flex", flexDirection: "column", justifyContent: "flex-end", gap: 8 }}>
              <span style={{ textAlign: "center", fontWeight: "var(--fw-bold)", fontSize: 17, fontVariantNumeric: "tabular-nums", color: hot ? card.accent : "var(--text-muted)" }}>{p.v}</span>
              <div style={{ height: `${Math.round((p.v / max) * 78)}%`, background: hot ? card.accent : "var(--grey-600)", borderRadius: "0 4px 4px 0", boxShadow: hot ? "var(--shadow-md)" : "none", transition: "height 400ms var(--ease-out)" }} />
              <span style={{ textAlign: "center", color: "var(--text-faint)", fontWeight: "var(--fw-bold)", fontSize: 14.5, letterSpacing: "var(--ls-wide)" }}>{p.l}</span>
            </div>
          );
        })}
      </div>
      <div style={{ textAlign: "right", color: "var(--text-faint)", fontWeight: "var(--fw-semibold)", fontSize: 13, letterSpacing: "var(--ls-wider)" }}>{card.source}</div>
    </div>
  );
}

function RailQuoteCard({ card }) {
  return (
    <div style={{ flex: 1, minHeight: 0, display: "flex", flexDirection: "column", gap: 16, padding: "20px 22px", position: "relative" }}>
      <CardKicker text={card.kicker} accent={card.accent} />
      <span aria-hidden="true" style={{ position: "absolute", right: 22, top: 8, fontFamily: "var(--font-display)", fontWeight: 900, fontSize: 170, lineHeight: 1, color: card.accent, opacity: 0.14 }}>”</span>
      <div style={{ flex: 1, display: "flex", alignItems: "center" }}>
        <div style={{ color: "var(--text-strong)", fontWeight: "var(--fw-semibold)", fontSize: 31, lineHeight: "var(--lh-snug)", letterSpacing: "var(--ls-tight)" }}>“{card.quote}”</div>
      </div>
      <div style={{ display: "flex", alignItems: "baseline", gap: 12 }}>
        <span style={{ color: card.accent, fontWeight: "var(--fw-bold)", fontSize: 20 }}>{card.name}</span>
        <span style={{ color: "var(--text-faint)", fontWeight: "var(--fw-semibold)", fontSize: 16.5, letterSpacing: "var(--ls-wide)", textTransform: "uppercase" }}>{card.loc}</span>
      </div>
    </div>
  );
}

function RailStatCard({ card }) {
  return (
    <div style={{ flex: 1, minHeight: 0, display: "flex", flexDirection: "column", gap: 12, padding: "20px 22px" }}>
      <CardKicker text={card.kicker} accent={card.accent} />
      <div style={{ flex: 1, display: "flex", flexDirection: "column", justifyContent: "center", gap: 14 }}>
        <div style={{ display: "flex", alignItems: "baseline", gap: 14 }}>
          <span style={{ fontFamily: "var(--font-display)", fontWeight: 900, fontSize: 96, lineHeight: 1, letterSpacing: "var(--ls-tightest)", color: card.accent, fontVariantNumeric: "tabular-nums" }}>{card.stat}</span>
          <span style={{ color: "var(--text-muted)", fontWeight: "var(--fw-black)", fontSize: 21, letterSpacing: "var(--ls-widest)" }}>{card.unit}</span>
        </div>
        <div style={{ color: "var(--text-strong)", fontWeight: "var(--fw-bold)", fontSize: 28, lineHeight: "var(--lh-tight)", letterSpacing: "var(--ls-tight)" }}>{card.headline}</div>
      </div>
      <div style={{ color: "var(--text-faint)", fontWeight: "var(--fw-semibold)", fontSize: 17, letterSpacing: "var(--ls-wide)" }}>{card.sub}</div>
    </div>
  );
}

const RAIL_CARD_TYPES = { photo: RailPhotoCard, chart: RailChartCard, quote: RailQuoteCard, stat: RailStatCard };

function RailCard({ card }) {
  const Body = RAIL_CARD_TYPES[card.type] || RailPhotoCard;
  return (
    <div style={{ position: "relative", height: "100%", display: "flex", flexDirection: "column", background: "rgba(24,33,46,0.75)", border: "1px solid var(--border-hairline)", borderLeft: `4px solid ${card.accent}`, borderRadius: "var(--sweep-md)", overflow: "hidden", boxShadow: "var(--shadow-md)" }}>
      <Body card={card} />
    </div>
  );
}

/* ---- Live cams: rail card holding 1–2 remote feeds (severe weather / watch-and-wait) ---- */
function CamCell({ scene, h24, showTime = true }) {
  const def = window.HERO_SCENES[scene] || {};
  const now = useNow();
  const W = 466, H = Math.round(W * 9 / 16);
  return (
    <div style={{ flex: "none", borderRadius: "var(--sweep-md)", overflow: "hidden", border: "1px solid var(--border-soft)", background: "var(--radar-ink-900)", boxShadow: "var(--shadow-md)" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 10, height: 38, padding: "0 14px", background: "rgba(13,21,36,0.95)", borderBottom: "1px solid var(--border-hairline)" }}>
        <span style={{ flex: 1, minWidth: 0, color: "var(--text-strong)", fontWeight: "var(--fw-bold)", fontSize: 14, letterSpacing: "var(--ls-wider)", textTransform: "uppercase", overflow: "hidden", whiteSpace: "nowrap", textOverflow: "ellipsis", textAlign: "left" }}>{def.locator}</span>
        {showTime && <span style={{ flex: "none", fontFamily: "var(--font-display)", fontWeight: 800, fontSize: 16, color: "var(--text-body)", fontVariantNumeric: "tabular-nums" }}>{fmtTime(now, def.tz, h24)}</span>}
      </div>
      {/* clean chroma hole — cam video keyed in downstream (magenta, same as hero) */}
      <div style={{ position: "relative", width: W, height: H, overflow: "hidden", background: "#FF00FF" }}></div>
    </div>
  );
}

function RailCamsCard({ cams, h24, camTimes = {} }) {
  return (
    <div style={{ position: "absolute", inset: 0, fontFamily: "var(--font-ui)", animation: "r1-enter-right 520ms var(--ease-broadcast) both" }}>
      <div style={{ height: "100%", display: "flex", flexDirection: "column", gap: 16, padding: 16, background: "rgba(24,33,46,0.75)", border: "1px solid var(--border-hairline)", borderLeft: "4px solid var(--signal-cyan)", borderRadius: "var(--sweep-md)", overflow: "hidden", boxShadow: "var(--shadow-md)" }}>
        <span style={{ alignSelf: "flex-start", display: "inline-flex", alignItems: "center", gap: 9, padding: "5px 14px", background: "var(--radar-red)", color: "#fff", fontWeight: "var(--fw-black)", fontSize: 15, letterSpacing: "var(--ls-widest)", textTransform: "uppercase", borderRadius: "var(--sweep-sm)", whiteSpace: "nowrap" }}>
          <LiveDot color="#fff" size={8} />LIVE
        </span>
        <div style={{ flex: 1, minHeight: 0, display: "flex", flexDirection: "column", justifyContent: "center", gap: 18 }}>
          {cams.map((c) => <CamCell key={c} scene={c} h24={h24} showTime={camTimes[c] !== false} />)}
        </div>
      </div>
    </div>
  );
}

/* ---- Rail countdown: card addendum — the card appears to extend down past its divider ---- */
function RailCountdown({ label = "TOP OF THE HOUR", target = null, accent = "var(--signal-cyan)" }) {
  const now = useNow(500);
  /* director-supplied absolute target (RFC3339); default: top of the hour */
  let tgt = null;
  if (target) {
    const d = new Date(target);
    if (!isNaN(d.getTime())) tgt = d;
  }
  if (!tgt) { tgt = new Date(now); tgt.setMinutes(60, 0, 0); }
  const s = Math.max(0, Math.floor((tgt - now) / 1000));
  const pad2 = (n) => String(n).padStart(2, "0");
  const disp = s >= 172800
    ? `${Math.floor(s / 86400)}D ${pad2(Math.floor((s % 86400) / 3600))}H` /* long-horizon (elections) */
    : s >= 3600
    ? `${pad2(Math.floor(s / 3600))}:${pad2(Math.floor((s % 3600) / 60))}:${pad2(s % 60)}`
    : `${pad2(Math.floor(s / 60))}:${pad2(s % 60)}`;
  /* flush under the card: shares the card's bottom hairline, paints over its shadow */
  return (
    <div style={{ position: "absolute", left: 0, right: 0, top: "100%", marginTop: -1, height: 54,
      display: "flex", alignItems: "center", gap: 14, padding: "0 20px 0 18px",
      background: "rgba(24,33,46,0.75)", border: "1px solid var(--border-hairline)",
      borderLeft: `4px solid ${accent}`, borderRadius: "0 0 var(--radius-md) 0",
      fontFamily: "var(--font-ui)",
      animation: "r1-in-fade 500ms var(--ease-out) both" }}>
      <span style={{ color: "var(--text-strong)", fontWeight: "var(--fw-black)", fontSize: 17, letterSpacing: "var(--ls-widest)", textTransform: "uppercase", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{label}</span>
      <span style={{ marginLeft: "auto", fontFamily: "var(--font-display)", fontWeight: 800, fontSize: 25, color: "var(--text-strong)", fontVariantNumeric: "tabular-nums", letterSpacing: "0.03em", whiteSpace: "nowrap" }}>{disp}</span>
    </div>
  );
}

function SideRail({ mode, h24, dwell = 18000, cams = [], camTimes }) {
  const cards = window.R1DATA.railCards;
  const idx = useCycle(cards.length, dwell, mode === "situation" || cams.length > 0);
  if (cams.length > 0) return <RailCamsCard cams={cams} h24={h24} camTimes={camTimes} />;
  if (mode === "situation") return <SituationRail />;
  if (!cards.length) return null; /* empty rail — show nothing, never crash */
  const card = cards[idx % cards.length];
  return (
    <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", fontFamily: "var(--font-ui)" }}>
      <div key={card.id} style={{ flex: 1, minHeight: 0, animation: "r1-enter-right 520ms var(--ease-broadcast) both" }}><RailCard card={card} /></div>
    </div>
  );
}

/* ---- Situation rail: one static card — the timeline of what we know ---- */
function SituationRail() {
  const C = window.R1DATA.situation;
  if (!C || !(C.timeline || []).length) return null; /* no live situation content */
  return (
    <div style={{ position: "absolute", inset: 0, fontFamily: "var(--font-ui)", animation: "r1-enter-right 520ms var(--ease-broadcast) both" }}>
      <div style={{ height: "100%", display: "flex", flexDirection: "column", background: "color-mix(in srgb, var(--radar-red) 5%, rgba(24,33,46,0.8))", border: "1px solid var(--border-hairline)", borderLeft: "4px solid var(--radar-red)", borderRadius: "var(--sweep-md)", overflow: "hidden", boxShadow: "var(--shadow-md)", padding: "20px 22px" }}>
        <div style={{ display: "flex", alignItems: "center" }}>
          <span style={{ color: "var(--text-faint)", fontWeight: "var(--fw-bold)", fontSize: 13.5, letterSpacing: "var(--ls-widest)" }}>DEVELOPING</span>
        </div>
        <div style={{ fontFamily: "var(--font-display)", fontWeight: 900, fontSize: 40, lineHeight: "var(--lh-tight)", letterSpacing: "var(--ls-tightest)", color: "var(--text-strong)", margin: "10px 0 4px" }}>{C.topic}</div>
        <div style={{ color: "var(--text-muted)", fontWeight: "var(--fw-semibold)", fontSize: 15, letterSpacing: "var(--ls-widest)", padding: "14px 0 6px", borderBottom: "1px solid var(--border-hairline)" }}>WHAT WE KNOW · TIMELINE</div>
        <div style={{ flex: 1, minHeight: 0, display: "flex", flexDirection: "column" }}>
          {C.timeline.map((t, i) => (
            <div key={i} style={{ flex: 1, display: "flex", gap: 14, alignItems: "center", borderBottom: i < C.timeline.length - 1 ? "1px solid var(--border-hairline)" : "none" }}>
              <span style={{ fontFamily: "var(--font-display)", fontWeight: 800, fontSize: 21, color: i === 0 ? "var(--radar-red)" : "var(--text-muted)", fontVariantNumeric: "tabular-nums", flex: "none", width: 68 }}>{t.t}</span>
              <span style={{ color: i === 0 ? "var(--text-strong)" : "var(--text-body)", fontWeight: i === 0 ? "var(--fw-semibold)" : "var(--fw-medium)", fontSize: 20, lineHeight: "var(--lh-snug)" }}>{t.text}</span>
            </div>
          ))}
        </div>
        <div style={{ color: "var(--text-faint)", fontWeight: "var(--fw-semibold)", fontSize: 12.5, letterSpacing: "var(--ls-wider)", textAlign: "right", paddingTop: 10 }}>RADAR1 VERIFIED · ALL TIMES GMT</div>
      </div>
    </div>
  );
}

/* ================= INFO BAR — Now/Next · world city feature bar · animated logo ================= */

/* weather-type ambient animations — miniature broadcast weather FX, behind cell content */
function wxSeed(str) { let s = 0; for (let i = 0; i < str.length; i++) s = (s * 31 + str.charCodeAt(i)) % 2147483647; return s || 7; }
function wxRand(seed) { let s = seed % 2147483647; if (s <= 0) s += 2147483646; return () => (s = (s * 16807) % 2147483647) / 2147483647; }

function WxAnim({ type, seed = 7 }) {
  const layers = { position: "absolute", inset: 0, pointerEvents: "none" };
  const vMask = { WebkitMaskImage: "linear-gradient(to bottom, transparent 0, black 18%, black 80%, transparent 100%)", maskImage: "linear-gradient(to bottom, transparent 0, black 18%, black 80%, transparent 100%)" };
  const hMask = { WebkitMaskImage: "linear-gradient(to right, transparent 0, black 12%, black 88%, transparent 100%)", maskImage: "linear-gradient(to right, transparent 0, black 12%, black 88%, transparent 100%)" };
  /* deterministic per-city randomization (no re-roll on re-render) */
  const R = React.useMemo(() => {
    const r = wxRand(seed * 7919 + 13);
    return {
      drops: Array.from({ length: 16 }, () => ({ left: 4 + r() * 92, dur: 0.65 + r() * 0.75, delay: -r() * 2.2, op: 0.30 + r() * 0.38, h: 12 + r() * 12 })),
      clouds: Array.from({ length: 3 }, () => ({ top: 4 + r() * 30, dur: 26 + r() * 22, delay: -r() * 40, sc: 0.8 + r() * 0.7, op: 0.10 + r() * 0.08 })),
    };
  }, [seed]);

  if (type === "sun") return (
    <span aria-hidden="true" style={{ ...layers, overflow: "hidden" }}>
      {/* rotating rays, radially faded — centered, subdued */}
      <span style={{ position: "absolute", left: "50%", top: "50%", width: 110, height: 110, margin: "-55px 0 0 -55px", borderRadius: "50%",
        background: "repeating-conic-gradient(from 0deg, rgba(255,176,0,0.11) 0deg 8deg, transparent 8deg 30deg)",
        WebkitMaskImage: "radial-gradient(circle, black 0%, rgba(0,0,0,0.35) 40%, transparent 64%)",
        maskImage: "radial-gradient(circle, black 0%, rgba(0,0,0,0.35) 40%, transparent 64%)",
        animation: "r1-rotate 40s linear infinite" }} />
      {/* glowing core */}
      <span style={{ position: "absolute", left: "50%", top: "50%", width: 24, height: 24, margin: "-12px 0 0 -12px", borderRadius: "50%",
        background: "radial-gradient(circle at 36% 32%, rgba(255,222,140,0.42), rgba(255,176,0,0.26) 55%, rgba(255,140,0,0.08) 100%)",
        boxShadow: "0 0 16px rgba(255,176,0,0.22), 0 0 34px rgba(255,176,0,0.09)",
        animation: "r1-sunbreathe 5s ease-in-out infinite alternate" }} />
    </span>
  );

  if (type === "cloud" || type === "fog") {
    const tint = type === "fog" ? "203,211,222" : "166,177,194";
    return (
      <span aria-hidden="true" style={{ ...layers, overflow: "hidden", ...hMask }}>
        {R.clouds.map((c, i) => (
          <span key={i} style={{ position: "absolute", left: -90, top: c.top, width: 54, height: 15, borderRadius: 15,
            background: `rgba(${tint},${c.op + 0.06})`,
            boxShadow: `16px -8px 0 3px rgba(${tint},${c.op}), 34px -3px 0 1px rgba(${tint},${c.op - 0.03})`,
            filter: type === "fog" ? "blur(5px)" : "blur(1.5px)",
            transform: `scale(${c.sc})`,
            animation: `r1-cloudcross ${type === "fog" ? c.dur * 1.6 : c.dur}s linear ${c.delay}s infinite` }} />
        ))}
        {type === "fog" && <span style={{ position: "absolute", left: "-10%", right: "-10%", bottom: 4, height: 14, borderRadius: 14, background: `rgba(${tint},0.10)`, filter: "blur(6px)", animation: "r1-fogroll 18s ease-in-out infinite alternate" }} />}
      </span>
    );
  }

  if (type === "rain" || type === "storm") {
    const col = type === "storm" ? "150,130,255" : "92,196,240";
    const drops = type === "storm" ? R.drops.slice(0, 12) : R.drops;
    return (
      <span aria-hidden="true" style={{ ...layers, overflow: "hidden" }}>
        {/* falling stems, per-drop randomized (speed / phase / opacity / length) */}
        <span style={{ ...layers, ...vMask }}>
          {drops.map((d, i) => (
            <span key={i} style={{ position: "absolute", left: d.left + "%", top: -26, width: type === "storm" ? 2 : 1.5, height: d.h,
              background: `linear-gradient(to bottom, rgba(${col},0), rgba(${col},${d.op}))`,
              animation: `r1-dropfall ${d.dur}s linear ${d.delay}s infinite` }} />
          ))}
        </span>
        {type === "storm" && (
          <React.Fragment>
            {/* brooding cloud mass along the top */}
            <span style={{ position: "absolute", left: -10, right: -10, top: -14, height: 26, borderRadius: 20, background: "rgba(96,86,160,0.22)", filter: "blur(7px)" }} />
            {/* lightning bolt + cell flash */}
            <span style={{ position: "absolute", left: "38%", top: 8, width: 15, height: 26,
              clipPath: "polygon(42% 0, 72% 0, 52% 36%, 74% 36%, 30% 100%, 44% 52%, 24% 52%)",
              background: "linear-gradient(to bottom, #FFE9B0, var(--signal-amber))",
              filter: "drop-shadow(0 0 6px rgba(255,214,120,0.9))",
              opacity: 0, animation: "r1-boltflash 7s linear infinite" }} />
            <span style={{ ...layers, background: "radial-gradient(ellipse at 42% 0%, rgba(200,190,255,0.5), rgba(123,97,255,0.12) 70%)", opacity: 0, animation: "r1-wxflash 7s linear infinite" }} />
          </React.Fragment>
        )}
      </span>
    );
  }
  return null;
}

/* cycling fixed clock: GMT + US zones, 20s dwell, crossfade */
const CLOCK_ZONES = [
  { l: "GMT", tz: "Etc/GMT" },
  { l: "ET",  tz: "America/New_York" },
  { l: "CT",  tz: "America/Chicago" },
  { l: "MT",  tz: "America/Denver" },
  { l: "PT",  tz: "America/Los_Angeles" },
];
/* live 3-letter abbreviation (EDT/EST etc. — DST-aware) */
function tzAbbr(now, tz, fallback) {
  try {
    const p = new Intl.DateTimeFormat("en-US", { timeZone: tz, timeZoneName: "short" }).formatToParts(now);
    const v = (p.find((x) => x.type === "timeZoneName") || {}).value || fallback;
    return /^[A-Z]{2,4}$/.test(v) ? v : fallback;
  } catch (e) { return fallback; }
}
function CycleClock({ now }) {
  const i = useCycle(CLOCK_ZONES.length, 20000);
  const cycled = React.useRef(false);
  React.useEffect(() => { if (i > 0) cycled.current = true; }, [i]);
  const prev = (i + CLOCK_ZONES.length - 1) % CLOCK_ZONES.length;
  const cell = (z) => {
    let parts;
    try { parts = new Intl.DateTimeFormat("en-US", { hour: "numeric", minute: "2-digit", hour12: true, timeZone: z.tz }).formatToParts(now); } catch (e) { parts = null; }
    const get = (t) => (parts ? (parts.find((p) => p.type === t) || {}).value || "" : "");
    return (
      <div style={{ display: "flex", flexDirection: "column", alignItems: "flex-start", justifyContent: "center", height: "100%", gap: 3 }}>
        <span style={{ fontFamily: "var(--font-display)", fontWeight: 900, fontSize: 21, color: "var(--text-strong)", fontVariantNumeric: "tabular-nums", lineHeight: 1, whiteSpace: "nowrap" }}>{parts ? get("hour") + ":" + get("minute") : "--:--"}</span>
        <span style={{ fontWeight: "var(--fw-bold)", fontSize: 15, letterSpacing: "var(--ls-wider)", color: "var(--text-muted)", lineHeight: 1, whiteSpace: "nowrap" }}>{get("dayPeriod").toUpperCase()} {tzAbbr(now, z.tz, z.l)}</span>
      </div>
    );
  };
  return (
    <div style={{ position: "relative", flex: "none", width: 84 }}>
      <div key={"c" + i} style={{ position: "absolute", inset: 0, animation: "r1-in-fade 700ms var(--ease-out) both" }}>{cell(CLOCK_ZONES[i])}</div>
      {cycled.current && (
        <div key={"p" + i} style={{ position: "absolute", inset: 0, animation: "r1-out-fade 700ms var(--ease-out) both", pointerEvents: "none" }}>{cell(CLOCK_ZONES[prev])}</div>
      )}
    </div>
  );
}

/* fixed-width clock: identical box in 12h and 24h modes (AM/PM as small suffix) */
function CityClock({ now, tz, h24 }) {
  const parts = React.useMemo(() => {
    try {
      return new Intl.DateTimeFormat("en-US", { hour: "2-digit", minute: "2-digit", hour12: !h24, timeZone: tz }).formatToParts(now);
    } catch (e) { return null; }
  }, [now, tz, h24]);
  const get = (t) => (parts ? (parts.find((p) => p.type === t) || {}).value || "" : "");
  return (
    <span style={{ flex: "none", width: 96, display: "inline-flex", alignItems: "baseline", justifyContent: "flex-end", gap: 4, whiteSpace: "nowrap" }}>
      <span style={{ fontFamily: "var(--font-display)", fontWeight: 800, fontSize: 22, color: "var(--text-strong)", fontVariantNumeric: "tabular-nums" }}>
        {parts ? get("hour") + ":" + get("minute") : "--:--"}
      </span>
      {!h24 && <span style={{ fontWeight: "var(--fw-bold)", fontSize: 12, letterSpacing: "var(--ls-wide)", color: "var(--text-faint)" }}>{get("dayPeriod").toUpperCase()}</span>}
    </span>
  );
}

/* ---- brand cell with promo rotation: every 5 min the logo fades out for a 10s promo ---- */
const PROMO_MSGS = [
  { pre: "WATCH ONLINE AT", brand: "RADAR1.COM" },
  { pre: "NOW ON MOBILE", brand: "RADAR1.APP" },
];
function BrandPromoCell({ now, size = 26 }) {
  /* wall-clock driven so every instance agrees: last 10s of each 5-minute block */
  const sec = Math.floor(now.getTime() / 1000);
  const promo = sec % 300 >= 290;
  const m = PROMO_MSGS[Math.floor(sec / 300) % PROMO_MSGS.length];
  return (
    <div style={{ position: "relative", display: "flex", alignItems: "center" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 16, opacity: promo ? 0 : 1, transition: "opacity 900ms var(--ease-out)" }}>
        <DSr.Wordmark size={size} animated />
        <CycleClock now={now} />
      </div>
      <div aria-hidden={!promo} style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", justifyContent: "center", gap: 4, opacity: promo ? 1 : 0, transition: "opacity 900ms var(--ease-out)", pointerEvents: "none", fontFamily: "var(--font-ui)" }}>
        <span style={{ color: "var(--text-muted)", fontWeight: "var(--fw-bold)", fontSize: 13, letterSpacing: "var(--ls-widest)", whiteSpace: "nowrap" }}>{m.pre}</span>
        <span style={{ fontFamily: "var(--font-display)", fontWeight: 900, fontSize: 21, letterSpacing: "var(--ls-tight)", color: "var(--text-strong)", whiteSpace: "nowrap" }}>{m.brand}</span>
      </div>
    </div>
  );
}

function InfoBar({ h24, mode }) {
  const now = useNow();
  const prog = window.R1DATA.programs[0];
  const regions = window.R1DATA.regions;
  /* 3 cities per page; a region shows all its pages before the next region flips in */
  const pages = React.useMemo(() => {
    const out = [];
    regions.forEach((r) => {
      for (let i = 0; i < r.cities.length; i += 3) {
        out.push({ region: r, cities: r.cities.slice(i, i + 3), page: i / 3, pages: Math.ceil(r.cities.length / 3) });
      }
    });
    return out;
  }, [regions]);
  const pi = useCycle(pages.length, 7000);
  const cur = pages[pi];
  const region = cur.region;
  const cycledRef = React.useRef(false);
  React.useEffect(() => { if (pi > 0) cycledRef.current = true; }, [pi]);
  const prevCities = cycledRef.current || pi > 0 ? pages[(pi + pages.length - 1) % pages.length].cities : null;
  useLucide([pi]);
  const minsToNext = 60 - now.getMinutes();
  return (
    <div style={{ position: "absolute", inset: 0, display: "flex", alignItems: "stretch", fontFamily: "var(--font-ui)", background: "var(--surface-bar)", borderTop: "1px solid var(--border-hairline)", borderBottom: "1px solid var(--border-hairline)" }}>
      {/* left: programming — subtle progress fill tracks the hour */}
      <div style={{ position: "relative", overflow: "hidden", display: "flex", alignItems: "center", gap: 14, padding: "0 24px", borderRight: "1px solid var(--border-hairline)", whiteSpace: "nowrap" }}>
        <span aria-hidden="true" style={{ position: "absolute", left: 0, top: 0, bottom: 0, width: `${Math.round((now.getMinutes() / 60) * 100)}%`, background: "rgba(255,255,255,0.05)", borderRight: "1px solid rgba(255,255,255,0.08)", transition: "width 1s linear", pointerEvents: "none" }} />
        <span style={{ padding: "4px 10px", background: "var(--radar-red)", color: "#fff", fontWeight: "var(--fw-black)", fontSize: 13.5, letterSpacing: "var(--ls-widest)", borderRadius: "var(--sweep-sm)" }}>NOW</span>
        <span style={{ color: "var(--text-strong)", fontWeight: "var(--fw-bold)", fontSize: 18, letterSpacing: "var(--ls-wide)", textTransform: "uppercase" }}>{prog.now}</span>
        <span style={{ width: 1, height: 26, background: "var(--border-hairline)" }} />
        <span style={{ padding: "4px 10px", background: "var(--grey-700)", color: "var(--text-body)", fontWeight: "var(--fw-black)", fontSize: 13.5, letterSpacing: "var(--ls-widest)", borderRadius: "var(--sweep-sm)" }}>NEXT</span>
        <span style={{ color: "var(--text-muted)", fontWeight: "var(--fw-bold)", fontSize: 18, letterSpacing: "var(--ls-wide)", textTransform: "uppercase" }}>{prog.next}</span>
        <span style={{ color: "var(--text-faint)", fontWeight: "var(--fw-semibold)", fontSize: 15.5, letterSpacing: "var(--ls-wider)", fontVariantNumeric: "tabular-nums" }}>IN {minsToNext} MIN</span>
      </div>

      {/* middle: world city feature bar — 4 fixed segments (region + 3 cities) */}
      <div style={{ flex: 1, minWidth: 0, display: "flex", alignItems: "stretch", overflow: "hidden" }}>
        {/* segment 1: region header — fixed width, never reflows */}
        <div key={"rh-" + region.name} style={{ flex: "none", width: 212, display: "flex", alignItems: "center", gap: 10, padding: "0 16px", borderLeft: "3px solid var(--signal-cyan)", background: "rgba(31,168,224,0.06)", whiteSpace: "nowrap", overflow: "hidden", animation: "r1-enter-up 420ms var(--ease-broadcast) both" }}>
          <i data-lucide={region.icon} style={{ width: 19, height: 19, color: "var(--signal-cyan)", flex: "none", opacity: 0.85 }} />
          <span style={{ color: "var(--text-muted)", fontWeight: "var(--fw-black)", fontSize: 12.5, letterSpacing: "var(--ls-widest)", overflow: "hidden", textOverflow: "ellipsis" }}>{region.name}</span>
        </div>
        {/* segments 2–4: equal-width city cells fill the remaining bar; staggered crossfade per cell */}
        <div style={{ flex: 1, minWidth: 0, display: "flex", alignItems: "stretch" }}>
          {cur.cities.map((c, ci) => (
            <div key={ci} style={{ position: "relative", flex: "1 1 0", minWidth: 0, borderRight: "1px solid var(--border-hairline)" }}>
              <div key={c.city} style={{ position: "absolute", inset: 0, overflow: "hidden", display: "flex", alignItems: "center", gap: 10, padding: "0 18px", animation: `r1-in-fade 600ms var(--ease-out) ${ci * 500}ms both` }}>
                <WxAnim type={c.type} seed={wxSeed(c.city)} />
                <span style={{ position: "relative", flex: "none", width: 104, color: "var(--text-body)", fontWeight: "var(--fw-bold)", fontSize: 14, letterSpacing: "var(--ls-wider)", overflow: "hidden", whiteSpace: "nowrap", textOverflow: "ellipsis" }}>{c.city}</span>
                <CityClock now={now} tz={c.tz} h24={h24} />
                <span style={{ marginLeft: "auto", flex: "none", width: 42, paddingRight: 6, display: "flex", flexDirection: "column", alignItems: "flex-end", lineHeight: 1.15, fontVariantNumeric: "tabular-nums" }}>
                  <span style={{ color: "var(--status-down)", fontWeight: "var(--fw-bold)", fontSize: 15.5 }}>{c.hi}°</span>
                  <span style={{ color: "var(--signal-cyan)", fontWeight: "var(--fw-bold)", fontSize: 15.5 }}>{c.lo}°</span>
                </span>
              </div>
              {prevCities && prevCities[ci] && prevCities[ci].city !== c.city && (
                <div key={"p-" + prevCities[ci].city + pi} style={{ position: "absolute", inset: 0, overflow: "hidden", display: "flex", alignItems: "center", gap: 10, padding: "0 18px", pointerEvents: "none", animation: `r1-out-fade 600ms var(--ease-out) ${ci * 500}ms both` }}>
                  <WxAnim type={prevCities[ci].type} seed={wxSeed(prevCities[ci].city)} />
                  <span style={{ position: "relative", flex: "none", width: 104, color: "var(--text-body)", fontWeight: "var(--fw-bold)", fontSize: 14, letterSpacing: "var(--ls-wider)", overflow: "hidden", whiteSpace: "nowrap", textOverflow: "ellipsis" }}>{prevCities[ci].city}</span>
                  <CityClock now={now} tz={prevCities[ci].tz} h24={h24} />
                  <span style={{ marginLeft: "auto", flex: "none", width: 42, paddingRight: 6, display: "flex", flexDirection: "column", alignItems: "flex-end", lineHeight: 1.15, fontVariantNumeric: "tabular-nums" }}>
                    <span style={{ color: "var(--status-down)", fontWeight: "var(--fw-bold)", fontSize: 15.5 }}>{prevCities[ci].hi}°</span>
                    <span style={{ color: "var(--signal-cyan)", fontWeight: "var(--fw-bold)", fontSize: 15.5 }}>{prevCities[ci].lo}°</span>
                  </span>
                </div>
              )}
            </div>
          ))}
        </div>
      </div>

      {/* right: animated network logo + cycling zone clock as one unit (with promo rotation) */}
      <div style={{ display: "flex", alignItems: "center", padding: "0 22px", borderLeft: "1px solid var(--border-hairline)" }}>
        <BrandPromoCell now={now} size={26} />
      </div>
    </div>
  );
}

/* ================= TICKERS ================= */

const TICKER_LABEL_W = 178;

function TickerLabel({ label, accent, ink }) {
  return (
    <div style={{ width: TICKER_LABEL_W, flex: "none", display: "flex", alignItems: "center", justifyContent: "center", padding: "0 12px", background: `color-mix(in srgb, ${accent} 90%, transparent)`, color: ink || "#fff", fontWeight: "var(--fw-black)", fontSize: 16, letterSpacing: "var(--ls-widest)", textTransform: "uppercase", whiteSpace: "nowrap", overflow: "hidden", borderRadius: "var(--sweep-md)", boxShadow: "var(--shadow-md)" }}>{label}</div>
  );
}

/* Rotating crawl bar:
   - fixed-width label with sweep (right-edge) rounding; unique accent per ticker
   - accent also tints the bar itself
   - rotation choreography: old content drains off left → beat of empty bar →
     incoming label travels across with its content trailing → docks over the old label */
function RotatingCrawl({ items, dur = 46, height = 52, rotateMs = 150000, offsetMs = 0, tint = true }) {
  const [active, setActive] = React.useState(0);
  const [phase, setPhase] = React.useState("loop"); /* loop → drain → gap → chip → loop */
  const barRef = React.useRef(null);
  const stripRef = React.useRef(null);
  const chipRef = React.useRef(null);
  /* items can shrink between renders when a live category empties — stay in range */
  const act = items.length ? active % items.length : 0;
  const incoming = (act + 1) % items.length;
  const cur = items[act], inc = items[incoming];
  const curDur = cur.dur || dur;

  /* rotate timer */
  React.useEffect(() => {
    if (items.length < 2) return;
    let t;
    const arm = (ms) => { t = setTimeout(() => { setPhase("drain"); arm(rotateMs); }, ms); };
    arm(rotateMs + offsetMs);
    return () => clearTimeout(t);
  }, [items.length, rotateMs, offsetMs]);

  /* drain: time's up — cut the old ticker with a quick clean fade */
  React.useEffect(() => {
    if (phase !== "drain") return;
    const strip = stripRef.current;
    if (!strip) { setPhase("gap"); return; }
    const anim = strip.animate([{ opacity: 1 }, { opacity: 0 }], { duration: 350, easing: "ease-out", fill: "forwards" });
    anim.onfinish = () => setPhase("gap");
    return () => anim.cancel();
  }, [phase]);

  /* gap: a beat of empty bar */
  React.useEffect(() => {
    if (phase !== "gap") return;
    const t = setTimeout(() => setPhase("chip"), 900);
    return () => clearTimeout(t);
  }, [phase]);

  /* chip: incoming label crosses the bar (content trailing) and docks over the old label */
  React.useEffect(() => {
    if (phase !== "chip") return;
    const bar = barRef.current, chip = chipRef.current;
    if (!bar || !chip) { setActive(incoming); setPhase("loop"); return; }
    const BW = bar.offsetWidth;
    const speed = 260; /* px/s — label glides across, readable but not sluggish */
    const anim = chip.animate(
      [{ transform: `translateX(${BW}px)` }, { transform: "translateX(0px)" }],
      { duration: (BW / speed) * 1000, easing: "linear", fill: "forwards" }
    );
    anim.onfinish = () => { setActive(incoming); setPhase("loop"); };
    return () => anim.cancel();
  }, [phase, incoming]);

  return (
    <div ref={barRef} style={{ position: "relative", display: "flex", alignItems: "stretch", width: "100%", height, overflow: "hidden", fontFamily: "var(--font-ui)", borderTop: "1px solid var(--border-hairline)", background: tint ? `color-mix(in srgb, ${cur.accent} 8%, var(--surface-bar))` : "var(--surface-bar)", transition: "background 600ms var(--ease-out)" }}>
      {/* crawl layer — spans the FULL bar so items slide beneath the translucent label as they exit */}
      <div style={{ position: "absolute", inset: 0, overflow: "hidden" }}>
        {phase !== "chip" && (
          <div key={"strip-" + cur.key} ref={stripRef} style={{ display: "inline-flex", alignItems: "center", height: "100%", whiteSpace: "nowrap", animation: `r1-crawl ${curDur}s linear infinite`, willChange: "transform" }}>
            <span style={{ width: TICKER_LABEL_W, flex: "none" }} />{cur.children}
            <span style={{ width: TICKER_LABEL_W, flex: "none" }} />{cur.children}
          </div>
        )}
      </div>
      {/* fixed label — translucent so the crawl fades behind it */}
      <div style={{ position: "relative", zIndex: 1, display: "flex", alignItems: "stretch" }}>
        <TickerLabel label={cur.label} accent={cur.accent} ink={cur.ink} />
      </div>
      {/* traveling label chip + trailing content (covers the whole bar, slides over the old label) */}
      {phase === "chip" && (
        <div ref={chipRef} style={{ position: "absolute", inset: 0, zIndex: 2, display: "flex", alignItems: "stretch", transform: "translateX(200%)", willChange: "transform" }}>
          <TickerLabel label={inc.label} accent={inc.accent} ink={inc.ink} />
          <div style={{ display: "inline-flex", alignItems: "center", height: "100%", whiteSpace: "nowrap" }}>{inc.children}</div>
        </div>
      )}
    </div>
  );
}

/* ---- ticker content segments ---- */
function diamondSegs(texts, accent, strong) {
  return (
    <React.Fragment>
      {texts.map((t, i) => (
        <span key={i} style={{ display: "inline-flex", alignItems: "center", color: strong ? "var(--text-strong)" : "var(--text-body)", fontWeight: strong ? "var(--fw-semibold)" : "var(--fw-medium)", fontSize: 20, letterSpacing: "var(--ls-wide)", padding: "0 24px" }}>
          <span style={{ color: accent, marginRight: 24, fontSize: 12 }}>◆</span>{t}
        </span>
      ))}
    </React.Fragment>
  );
}

function marketSegs(data) {
  return (
    <React.Fragment>
      {data.map((m, i) => (
        <span key={i} style={{ display: "inline-flex", alignItems: "center", gap: 12, padding: "0 28px", fontSize: 20, fontVariantNumeric: "tabular-nums" }}>
          <span style={{ color: "var(--text-strong)", fontWeight: "var(--fw-bold)", letterSpacing: "var(--ls-wide)" }}>{m.sym}</span>
          <span style={{ color: "var(--text-body)", fontWeight: "var(--fw-medium)" }}>{m.px}</span>
          <Delta v={m.chg} />
        </span>
      ))}
    </React.Fragment>
  );
}

/* Situation: double-height breaking flipper — right column stacks one city cell with the (navy) logo box */
function HeadlineFlipper({ edge = "bottom", h24 = true }) {
  const C = window.R1DATA.situation || { topic: "", headlines: [""] };
  const i = useCycle(C.headlines.length, 8000);
  const now = useNow();
  const cityCell = <SituationCityCell h24={h24} />;
  const logoCell = (
    <div style={{ flex: 1, minHeight: 0, display: "flex", alignItems: "center", justifyContent: "center", background: "var(--surface-bar)" }}>
      <BrandPromoCell now={now} size={24} />
    </div>
  );
  return (
    <div style={{ display: "flex", alignItems: "stretch", width: "100%", height: 116, fontFamily: "var(--font-ui)", background: "color-mix(in srgb, var(--radar-red) 8%, var(--surface-bar))", borderTop: "1px solid var(--border-hairline)", overflow: "hidden" }}>
      <div style={{ flex: "none", maxWidth: 470, display: "flex", alignItems: "center", padding: "0 30px", background: "var(--radar-red)", borderRadius: "var(--sweep-md)", boxShadow: "var(--shadow-md)" }}>
        <span style={{ fontFamily: "var(--font-display)", color: "#fff", fontWeight: 900, fontSize: 30, letterSpacing: "var(--ls-tight)", lineHeight: 1.08, textTransform: "uppercase" }}>{C.topic}</span>
      </div>
      <div style={{ flex: 1, minWidth: 0, display: "flex", alignItems: "center", padding: "0 28px" }}>
        <div key={i} style={{ minWidth: 0, animation: "r1-enter-up 420ms var(--ease-broadcast) both" }}>
          <div style={{ color: "var(--text-strong)", fontWeight: "var(--fw-semibold)", fontSize: 30, lineHeight: "var(--lh-snug)", letterSpacing: "var(--ls-tight)", display: "-webkit-box", WebkitBoxOrient: "vertical", WebkitLineClamp: 2, overflow: "hidden" }}>{C.headlines[i]}</div>
        </div>
      </div>
      {/* right column: logo keeps its normal-mode position (nearest the hero); the city cell takes the outer slot */}
      <div style={{ width: 330, flex: "none", display: "flex", flexDirection: "column", borderLeft: "1px solid var(--border-hairline)" }}>
        {edge === "bottom" ? (
          <React.Fragment>
            {logoCell}
            <div style={{ flex: 1, minHeight: 0, borderTop: "1px solid var(--border-hairline)" }}>{cityCell}</div>
          </React.Fragment>
        ) : (
          <React.Fragment>
            <div style={{ flex: 1, minHeight: 0, borderBottom: "1px solid var(--border-hairline)" }}>{cityCell}</div>
            {logoCell}
          </React.Fragment>
        )}
      </div>
    </div>
  );
}

/* Situation: single city cell — one city at a time, fixed box, crossfade */
function SituationCityCell({ h24 }) {
  const cities = React.useMemo(() => window.R1DATA.regions.flatMap((r) => r.cities), []);
  const i = useCycle(cities.length, 8000);
  const now = useNow();
  const prev = (i + cities.length - 1) % cities.length;
  const cycled = React.useRef(false);
  React.useEffect(() => { if (i > 0) cycled.current = true; }, [i]);
  const cell = (c) => (
    <div style={{ display: "flex", alignItems: "center", gap: 12, height: "100%", padding: "0 18px", background: "var(--surface-bar)" }}>
      <span style={{ flex: 1, minWidth: 0, color: "var(--text-body)", fontWeight: "var(--fw-bold)", fontSize: 15, letterSpacing: "var(--ls-wider)", overflow: "hidden", whiteSpace: "nowrap", textOverflow: "ellipsis" }}>{c.city}</span>
      <CityClock now={now} tz={c.tz} h24={h24} />
      <span style={{ flex: "none", width: 42, display: "flex", flexDirection: "column", alignItems: "flex-end", lineHeight: 1.15, fontVariantNumeric: "tabular-nums" }}>
        <span style={{ color: "var(--status-down)", fontWeight: "var(--fw-bold)", fontSize: 14.5 }}>{c.hi}°</span>
        <span style={{ color: "var(--signal-cyan)", fontWeight: "var(--fw-bold)", fontSize: 14.5 }}>{c.lo}°</span>
      </span>
    </div>
  );
  return (
    <div style={{ position: "relative", width: "100%", height: "100%", overflow: "hidden" }}>
      <div key={"c" + i} style={{ position: "absolute", inset: 0, animation: "r1-in-fade 700ms var(--ease-out) both" }}>{cell(cities[i])}</div>
      {cycled.current && (
        <div key={"p" + i} style={{ position: "absolute", inset: 0, pointerEvents: "none", animation: "r1-out-fade 700ms var(--ease-out) both" }}>{cell(cities[prev])}</div>
      )}
    </div>
  );
}

function TickerStack({ mode, dur = 46, edge = "bottom", h24 = true }) {
  const D = window.R1DATA;
  const sportsCrawl = (D.sports && D.sports.crawl) || [];
  /* top rail: editorial categories */
  const newsItem = { key: "news", label: "NEWS", accent: "var(--radar-red)", src: D.tickerNews || [], children: diamondSegs(D.tickerNews || [], "var(--radar-red)"), dur };
  const topAll = [
    newsItem,
    { key: "sports", label: "SPORTS",        accent: "var(--signal-cyan)",   ink: "#04121C", src: sportsCrawl, children: diamondSegs(sportsCrawl, "var(--signal-cyan)"), dur: Math.round(dur * 0.9) },
    { key: "ent",   label: "ENTERTAINMENT", accent: "var(--signal-violet)", src: D.entertainment || [], children: diamondSegs(D.entertainment || [], "var(--signal-violet)"), dur: Math.round(dur * 0.95) },
    { key: "biz",   label: "BUSINESS",      accent: "var(--signal-green)",  ink: "#03130A", src: D.business || [], children: diamondSegs(D.business || [], "var(--signal-green)"), dur },
    { key: "tech",  label: "TECHNOLOGY",    accent: "var(--signal-amber)",  ink: "#1F1300", src: D.technology || [], children: diamondSegs(D.technology || [], "var(--signal-amber)"), dur: Math.round(dur * 0.95) },
  ];
  /* bottom rail: finance boards */
  const cryptoItem = { key: "crypto", label: "CRYPTO", accent: "var(--signal-violet)", src: D.crypto || [], children: marketSegs(D.crypto || []), dur };
  const botAll = [
    { key: "stocks", label: "STOCKS",      accent: "var(--signal-green)",  ink: "#03130A", src: D.markets || [], children: marketSegs((D.markets || []).slice(0, 6)), dur: Math.round(dur * 1.15) },
    cryptoItem,
    { key: "forex",  label: "FOREX",       accent: "var(--signal-cyan)",   ink: "#04121C", src: D.forex || [], children: marketSegs(D.forex || []), dur },
    { key: "cmdty",  label: "COMMODITIES", accent: "var(--signal-amber)",  ink: "#1F1300", src: D.commodities || [], children: marketSegs(D.commodities || []), dur: Math.round(dur * 1.1) },
    { key: "rates",  label: "RATES",       accent: "var(--grey-500)",      ink: "#04121C", src: D.rates || [], children: marketSegs(D.rates || []), dur: Math.round(dur * 0.9) },
  ];
  /* empty categories drop out of rotation; each bar always keeps its anchor item */
  let topItems = topAll.filter((it) => it.src.length > 0);
  if (!topItems.length) topItems = [newsItem];
  let botItems = botAll.filter((it) => it.src.length > 0);
  if (!botItems.length) botItems = [cryptoItem];
  const rows = mode === "situation"
    /* crisis: the full category rotation runs on the OUTER bar; the inner bar is a static headline flipper.
       NEWS trades its red for cyan so red stays reserved for the breaking treatment */
    ? [<HeadlineFlipper key="hf" edge={edge} h24={h24} />, <RotatingCrawl key="all" items={[{ ...newsItem, accent: "var(--signal-cyan)", ink: "#04121C", children: diamondSegs(D.tickerNews || [], "var(--signal-cyan)") }, ...topItems.filter((it) => it.key !== "news"), ...botItems]} dur={dur} rotateMs={120000} tint={false} />]
    : [<RotatingCrawl key="n" items={topItems} dur={dur} rotateMs={150000} />, <RotatingCrawl key="m" items={botItems} dur={dur} rotateMs={150000} offsetMs={75000} />];
  if (edge === "top") rows.reverse();
  return (
    <div style={{ position: "absolute", left: 0, right: 0, [edge]: 0, display: "flex", flexDirection: "column", boxShadow: edge === "bottom" ? "var(--shadow-chyron)" : "var(--shadow-md)", background: mode === "situation" ? "var(--radar-ink)" : "none" }}>
      {rows}
    </div>
  );
}

Object.assign(window, { SideRail, SituationRail, InfoBar, TickerStack, RotatingCrawl, HeadlineFlipper, SituationCityCell, RailCamsCard, RailCountdown });
