/* Daily Digest page - guide hero + News & Jobs from Supabase digest_posts -
   plus the article reader (kept for the home page's news line).

   digest_posts row conventions (engine job - see reports/JOB-PLAN-2-digest-agent.md):
     type 'news' : title, standfirst (the dek), source_links [{label:'dubaiweek.ae', url}]
     type 'job'  : title, standfirst (the dek), source_links
                   [{label:'View on LinkedIn', url}, {label:'Apply', url}, ...]
     Either type may carry one extra source_links entry {meta:{...}} for display fields
     the schema has no column for: category, tone (brand|neutral|amber), read ('2 min'),
     company, line (the mono job meta line), location, found.
   Until the engine publishes rows, the page shows DIGEST_FALLBACK - the approved
   batch from digest-mockup.html, every item linked to its real source. */

const newsToneCol = (t) => (t === "amber" ? "var(--tr-amber-700)" : t === "neutral" ? "var(--tr-ink-3)" : "var(--tr-brand-700)");
const NewsCat = ({ tone, children }) => {
  const cls = tone === "amber" ? "tr-pill-amber" : tone === "neutral" ? "tr-pill-neutral" : "tr-pill";
  return <span className={"tr-pill " + cls} style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: "0.04em" }}>{children}</span>;
};

const GUIDE_URL = "/guide/how-to-navigate-fitness-career-dubai/";

/* ── The free career guide hero - anchors the page, links to the real guide. ── */
const GUIDE_CHAPTERS = [
  { n: "01", t: "Get certified", d: "Level 2 gets you in the gym. Level 3 gets you paid." },
  { n: "02", t: "Find your specialty", d: "Specialists win, generalists struggle - the heart of the guide." },
  { n: "03", t: "Run it as a business", d: "Retention is your income. Keep clients, not just hours." },
  { n: "04", t: "Stay registered", d: "CPD never ends - use it to go deeper into your edge." },
];

function GuideHero() {
  // the whole card opens the guide - the button is just the visible affordance
  const openGuide = () => { window.location.href = GUIDE_URL; };
  return (
    <section onClick={openGuide} style={{ position: "relative", borderRadius: 20, overflow: "hidden", color: "#fff", minHeight: 440, display: "flex", flexDirection: "column", boxShadow: "var(--tr-shadow-md)", cursor: "pointer", maxWidth: 860, margin: "0 auto" }}>
      <img src="img/guide-dubai.png" alt="Trainers huddled together with the Burj Khalifa behind them, Dubai" draggable="false"
        style={{ position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", objectPosition: "center 38%", display: "block" }} />
      <div style={{ position: "absolute", inset: 0, background: "linear-gradient(98deg, rgba(5,40,31,0.97) 0%, rgba(5,40,31,0.88) 30%, rgba(5,40,31,0.55) 62%, rgba(5,40,31,0.25) 100%)" }}></div>
      <div style={{ position: "absolute", inset: 0, background: "linear-gradient(to top, rgba(5,40,31,0.92) 0%, rgba(5,40,31,0) 38%)" }}></div>

      <div className="digest-hero-pad" style={{ position: "relative", padding: "52px 48px 0", maxWidth: 580, flex: 1 }}>
        <div style={{ display: "inline-flex", alignItems: "center", gap: 9, fontFamily: "var(--tr-font-text)", fontSize: 12, fontWeight: 700, letterSpacing: "0.12em", textTransform: "uppercase", color: "var(--tr-amber-400)" }}>
          <Icon name="badge-check" size={15} /> Free guide · no email needed
        </div>
        <h2 className="digest-hero-h" style={{ fontFamily: "var(--tr-font-display)", fontWeight: 700, fontSize: 48, lineHeight: 1.02, letterSpacing: "-0.035em", margin: "18px 0 0", color: "#fff", textWrap: "balance" }}>
          How to navigate a fitness career in <span style={{ color: "var(--tr-amber-400)" }}>Dubai</span>
        </h2>
        <p style={{ fontSize: 17, lineHeight: 1.55, color: "rgba(255,255,255,0.86)", margin: "18px 0 0", maxWidth: 470, textWrap: "pretty" }}>
          The whole journey in plain English, from picking a path to building a career that lasts. Independent, and completely free.
        </p>
        <div style={{ display: "flex", alignItems: "center", gap: 16, marginTop: 28, flexWrap: "wrap" }}>
          <a className="tr-btn tr-btn-lg" href={GUIDE_URL} style={{ background: "var(--tr-amber-400)", color: "var(--tr-ink)", border: "none", fontWeight: 700, textDecoration: "none" }}>
            Start the guide <Icon name="arrow-right" size={17} />
          </a>
          <span style={{ fontSize: 13.5, color: "rgba(255,255,255,0.7)", fontFamily: "var(--tr-font-mono)" }}>12-min read · updated for 2026</span>
        </div>
      </div>

      {/* chapters - the table of contents, text only, along the bottom */}
      <div className="guide-toc" style={{ position: "relative", display: "grid", gridTemplateColumns: "repeat(4, 1fr)", padding: "0 48px 36px", marginTop: 38 }}>
        {GUIDE_CHAPTERS.map((c, i) => (
          <a key={c.n} href={GUIDE_URL} style={{ paddingLeft: i ? 22 : 0, paddingRight: 14, borderLeft: i ? "1px solid rgba(255,255,255,0.22)" : "none", display: "block", textDecoration: "none" }}>
            <span style={{ fontFamily: "var(--tr-font-mono)", fontSize: 12.5, color: "var(--tr-amber-400)" }}>{c.n}</span>
            <div style={{ fontFamily: "var(--tr-font-display)", fontWeight: 600, fontSize: 16, color: "#fff", letterSpacing: "-0.01em", marginTop: 6 }}>{c.t}</div>
          </a>
        ))}
      </div>
    </section>
  );
}

/* ── digest_posts → display items ── */
const DIGEST_SB_URL = "https://uyhnfysmsfiagotjfqjs.supabase.co";
const DIGEST_SB_ANON = "sb_publishable_MlNmf3BIRPXHqjxgyR7VdA_fZXiCMxp";

const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
function digestDate(iso) {
  if (!iso) return "";
  const d = new Date(iso);
  return isNaN(d) ? "" : d.getDate() + " " + MONTHS[d.getMonth()];
}
function readMinutes(row) {
  const words = ((row.body || "") + " " + (row.standfirst || "")).trim().split(/\s+/).length;
  return Math.max(1, Math.round(words / 200)) + " min";
}
function linkDomain(url) {
  try { return new URL(url).hostname.replace(/^www\./, ""); } catch (e) { return ""; }
}
/* honest relative time - the freshness signal. Never fabricated: it comes
   straight from the row's published_at, so cadence claims are real. */
function relTime(iso) {
  if (!iso) return "";
  const t = new Date(iso).getTime();
  if (isNaN(t)) return "";
  const mins = Math.round((Date.now() - t) / 60000);
  if (mins < 60) return Math.max(1, mins) + " min ago";
  const hrs = Math.round(mins / 60);
  if (hrs < 24) return hrs + "h ago";
  const days = Math.round(hrs / 24);
  if (days === 1) return "yesterday";
  if (days < 7) return days + " days ago";
  return digestDate(iso);
}
const TYPE_CAT_LABEL = { pt: "Careers", "reps-process": "Registration", pilates: "Pilates", article: "Industry News", news: "News" };
function rowToItem(row) {
  const all = Array.isArray(row.source_links) ? row.source_links : [];
  const meta = (all.find((l) => l && l.meta) || {}).meta || {};
  const links = all.filter((l) => l && l.url);
  const type = (row.type || "").toLowerCase();
  if (type === "brief") return null; // the morning brief is a separate surface, not a feed row
  if (type === "job" || type === "jobs") {
    return {
      kind: "job", id: row.slug,
      time: relTime(row.published_at) || "today",
      location: meta.location || "UAE",
      company: meta.company || "", title: row.title, line: meta.line || "",
      dek: row.standfirst || "", links: links,
    };
  }
  // Every other published row is a real guide/news article (type PT, REPs-process,
  // pilates, article, news, ...) - all of it goes in the feed and opens our real,
  // live, SEO-indexed /insights/<slug> page (the source is cited inside that page).
  const src = links[0] || {};
  return {
    kind: "news", id: row.slug,
    time: relTime(row.published_at),
    category: meta.category || TYPE_CAT_LABEL[type] || "Guide", tone: meta.tone || "brand",
    title: row.title, dek: row.standfirst || "",
    url: "/insights/" + row.slug, internal: true,
    source: src.label || linkDomain(src.url),
  };
}

/* The approved batch (digest-mockup.html, runs of 2026-06-11) - shown until the
   engine publishes rows. Real Exa data; every item links to its real source. */
const DIGEST_FALLBACK = {
  stream: [
    { kind: "job", id: "f-j1", time: "today", location: "Dubai", company: "Optimum Strength",
      title: "Personal Trainer", line: "Full-time · strength-focused studio",
      dek: "A strength-led private studio hiring trainers who specialise rather than generalise. Needs REPS UAE Level 3.",
      links: [{ label: "View on LinkedIn", url: "https://ae.linkedin.com/jobs/view/personal-trainer-at-optimum-strength-4094504135" }] },
    { kind: "news", id: "f-b1", time: "yesterday", category: "Retention", tone: "amber",
      title: "Client dropout crisis: what it means for Dubai personal trainers",
      dek: "Most clients quit within months. A platform just rebuilt its product around that fact. Here's why it matters for your income.",
      url: "digest-article-mockup.html", internal: true },
    { kind: "news", id: "f-n1", time: "yesterday", category: "Regulation", tone: "neutral",
      title: "Dubai establishes a Longevity Authority to regulate the wellness sector",
      dek: "Wellness services now have their own regulator under Law No. 17 of 2026. Worth watching if you train here.",
      url: "digest-article-mockup.html", internal: true },
    { kind: "news", id: "f-n5", time: "yesterday", category: "Youth sport", tone: "brand",
      title: "Iniesta launches his academy in Dubai after taking over at Gulf United",
      dek: "Youth sport keeps expanding in the UAE, and growing academies hire coaches.",
      url: "digest-article-mockup.html", internal: true },
    { kind: "job", id: "f-j2", time: "yesterday", location: "UAE", company: "Jumeirah Group",
      title: "Fitness Instructor - J Club", line: "Full-time · hotel package + benefits",
      dek: "Instructor role at Jumeirah's members' wellness facility: a stable, benefits-backed route in a major hospitality group.",
      links: [{ label: "View on LinkedIn", url: "https://www.linkedin.com/jobs/view/4343586562/" }] },
    { kind: "news", id: "f-b2", time: "2 days ago", category: "Pilates", tone: "brand",
      title: "Peloton signals a move into reformer Pilates with its Skop acquisition",
      dek: "The biggest home-fitness brand wants into reformer. The specialism keeps gaining value.",
      url: "digest-article-mockup.html", internal: true },
    { kind: "job", id: "f-j3", time: "2 days ago", location: "Dubai", company: "Fitness First Middle East",
      title: "Personal Trainer - Dubai", line: "Full-time · AED 4,000-6,000/mo + commission · closes 18 Jun",
      dek: "Build and retain a client base across Fitness First's Dubai clubs. Needs REPS UAE Level 3, floor experience, a sales mindset.",
      links: [{ label: "View on LinkedIn", url: "https://www.linkedin.com/jobs/view/4351788622/" }, { label: "Apply", url: "https://www.leisurejobs.com/job/4879080/personal-trainer-dubai-uae/" }] },
    { kind: "news", id: "f-n2", time: "3 days ago", category: "New gym", tone: "brand",
      title: "FITCODE opens a flagship premium health club at Q-East, Al Quoz",
      dek: "24,000 sq ft, four studios, a HYROX centre and the brand's first dedicated reformer studio. New floors need new trainers.",
      url: "digest-article-mockup.html", internal: true },
    { kind: "news", id: "f-n3", time: "3 days ago", category: "City", tone: "brand",
      title: "Dubai Sports Council launches Active Summer, running to 31 August",
      dek: "A citywide events calendar to keep people training through the heat. Demand for coaches in the slow season.",
      url: "digest-article-mockup.html", internal: true },
    { kind: "job", id: "f-j4", time: "3 days ago", location: "Dubai", company: "Paris Group International",
      title: "Fitness Trainer", line: "Full-time · corporate group",
      dek: "A corporate-backed role with structure and stability, suited to a trainer who values a steady base over commission-only work.",
      links: [{ label: "View on LinkedIn", url: "https://www.linkedin.com/jobs/view/4346982528/" }] },
    { kind: "news", id: "f-c1", time: "4 days ago", category: "New course", tone: "brand",
      title: "On the register: Focus Awards Level 3 Diploma Practitioner in Personal Training",
      dek: "A REPS-recognised Level 3 route. Compare it against the other Level 3 diplomas before you commit.",
      url: "compare.html", internal: true },
    { kind: "news", id: "f-a1", time: "5 days ago", category: "Awarding body", tone: "neutral",
      title: "Active IQ awards three of the Level 3 routes on the UAE register",
      dek: "What the awarding body's name on your certificate actually guarantees, sourced to the register.",
      url: "compare.html", internal: true },
  ],
};

/* the agent's morning brief - engine writes one row: type 'brief', newest wins */
const DIGEST_BRIEF_FALLBACK = "The industry is fighting client dropout, which makes retention the most valuable skill a trainer has. Reformer Pilates keeps gaining value, with Peloton buying in and PILAT3S expanding to Dubai. Two new premium clubs just opened, and 4 trainer jobs are live below.";

function useDigest() {
  const [feed, setFeed] = React.useState({ ...DIGEST_FALLBACK, brief: DIGEST_BRIEF_FALLBACK });
  React.useEffect(() => {
    // reuse the auth.js client (avoids another GoTrueClient on the same storage key)
    const sb = (window.TRAuth && window.TRAuth.client) ||
      (window.supabase && window.supabase.createClient && window.supabase.createClient(DIGEST_SB_URL, DIGEST_SB_ANON));
    if (!sb) return; // supabase.js never loaded - the approved batch stays up
    sb.from("digest_posts").select("*").eq("status", "published")
      .order("published_at", { ascending: false }).limit(500)
      .then((res) => {
        const rows = (res && res.data) || [];
        const briefRow = rows.find((r) => (r.type || "").toLowerCase() === "brief");
        // rows arrive newest-first; jobs and articles stay mixed - that IS the feed
        const stream = rows.map(rowToItem).filter(Boolean);
        if (stream.length || briefRow) {
          setFeed({
            stream: stream.length ? stream : DIGEST_FALLBACK.stream,
            brief: (briefRow && (briefRow.standfirst || briefRow.body)) || DIGEST_BRIEF_FALLBACK,
          });
        }
      })
      .catch((err) => { console.error("[useDigest] fetch failed, showing fallback:", err); });
  }, []);
  return feed;
}

/* one news row - text only; internal rows open our /digest/ page, the rest link out */
function DigestNewsRow({ n }) {
  const ext = !n.internal;
  return (
    <a className="news-row digest-row" href={n.url} target={ext ? "_blank" : undefined} rel={ext ? "noopener noreferrer" : undefined}
      style={{ display: "flex", gap: 24, alignItems: "baseline", padding: "20px 0", borderBottom: "1px solid var(--tr-line-2)", textDecoration: "none" }}>
      <div className="digest-row-l" style={{ width: 96, flexShrink: 0, fontFamily: "var(--tr-font-mono)", fontSize: 12, lineHeight: 1.5, paddingTop: 3 }}>
        <div style={{ color: "var(--tr-ink-3)" }}>{n.time}</div>
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ marginBottom: 7 }}><NewsCat tone={n.tone}>{n.category}</NewsCat></div>
        <h3 style={{ fontFamily: "var(--tr-font-display)", fontWeight: 700, fontSize: 20, lineHeight: 1.18, letterSpacing: "-0.015em", margin: 0, color: "var(--tr-ink)", textWrap: "balance" }}>{n.title}</h3>
        <p style={{ fontSize: 14.5, lineHeight: 1.5, color: "var(--tr-fg-2)", margin: "7px 0 0", textWrap: "pretty", maxWidth: 620 }}>{n.dek}</p>
      </div>
      <span className="news-go" style={{ flexShrink: 0, color: "var(--tr-fg-faint)", alignSelf: "center" }}><Icon name={ext ? "arrow-up-right" : "arrow-right"} size={18} /></span>
    </a>
  );
}

/* one job row - same aesthetic, richer middle, LinkedIn + Apply links */
function DigestJobRow({ j }) {
  return (
    <article className="news-row digest-row" style={{ display: "flex", gap: 24, alignItems: "baseline", padding: "20px 0", borderBottom: "1px solid var(--tr-line-2)" }}>
      <div className="digest-row-l" style={{ width: 96, flexShrink: 0, fontFamily: "var(--tr-font-mono)", fontSize: 12, lineHeight: 1.5, paddingTop: 3 }}>
        <div style={{ color: "var(--tr-ink-3)" }}>{j.time}</div>
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ marginBottom: 7 }}><NewsCat tone="brand">Job · {j.location}</NewsCat></div>
        <h3 style={{ fontFamily: "var(--tr-font-display)", fontWeight: 700, fontSize: 20, lineHeight: 1.18, letterSpacing: "-0.015em", margin: 0, color: "var(--tr-ink)", textWrap: "balance" }}>{j.title}</h3>
        <div style={{ fontFamily: "var(--tr-font-mono)", fontSize: 12, color: "var(--tr-ink-3)", marginTop: 8 }}>{j.company}{j.line ? " · " + j.line : ""}</div>
        <p style={{ fontSize: 14.5, lineHeight: 1.5, color: "var(--tr-fg-2)", margin: "11px 0 0", textWrap: "pretty", maxWidth: 620 }}>{j.dek}</p>
        <div style={{ display: "flex", gap: 18, marginTop: 11, flexWrap: "wrap" }}>
          {j.links.map((l) => (
            <a key={l.url} href={l.url} target="_blank" rel="noopener noreferrer"
              style={{ fontSize: 14, fontWeight: 600, color: "var(--tr-brand-700)", display: "inline-flex", alignItems: "center", gap: 5 }}>
              {l.label} <Icon name="arrow-up-right" size={14} />
            </a>
          ))}
        </div>
      </div>
    </article>
  );
}

function NewsPage() {
  const feed = useDigest();

  // signed-out visitors see one job in the stream; sign in (free) unlocks the rest
  const [user, setUser] = React.useState(window.TRAuth ? window.TRAuth.user : null);
  React.useEffect(() => {
    if (!window.TRAuth) return;
    return window.TRAuth.onChange(setUser);
  }, []);

  // one continuous feed, newest first, jobs mixed in between - every published
  // article stays on the page, paged 10-per-page rather than capped at 10 total.
  const full = [];
  let jobCount = 0, jobsHidden = 0;
  for (const it of feed.stream) {
    if (it.kind === "news") {
      full.push(it);
    } else if (user || jobCount < 1) { full.push(it); jobCount++; }
    else jobsHidden++;
  }

  const PAGE_SIZE = 10;
  const pageCount = Math.max(1, Math.ceil(full.length / PAGE_SIZE));
  const [page, setPage] = React.useState(0);
  React.useEffect(() => { if (page > pageCount - 1) setPage(0); }, [pageCount]); // feed refresh landed - stay in range
  const display = full.slice(page * PAGE_SIZE, page * PAGE_SIZE + PAGE_SIZE);
  const goToPage = (p) => { setPage(p); window.scrollTo({ top: 0, behavior: "smooth" }); };

  const today = new Date().toLocaleDateString("en-GB", { weekday: "long", day: "numeric", month: "long", year: "numeric" }).replace(",", "");

  return (
    <main style={{ background: "#fff", minHeight: "70vh" }}>
      {/* mobile: compact rows, smaller masthead/hero type */}
      <style>{`
        @media (max-width: 640px) {
          .digest-row { gap: 12px !important; padding: 14px 0 !important; }
          .digest-row-l { width: 52px !important; font-size: 11px !important; }
          .digest-row-s { display: none; }
          .digest-row h3 { font-size: 16.5px !important; }
          .digest-row p { font-size: 13px !important; margin-top: 4px !important; }
          .digest-row .news-go { display: none; }
          .digest-mast-h1 { font-size: 32px !important; }
          .digest-hero-h { font-size: 32px !important; }
          .digest-hero-pad { padding: 30px 22px 0 !important; }
          .digest-glance { padding: 18px !important; }
          .digest-glance p { font-size: 16px !important; }
        }
      `}</style>

      {/* masthead - deep evergreen, same treatment as the compare page head */}
      <div style={{ background: "var(--tr-brand-900)" }}>
        <div className="container" style={{ padding: "34px var(--tr-gutter) 32px" }}>
          <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: 24, flexWrap: "wrap" }}>
            <div>
              <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: "0.12em", textTransform: "uppercase", color: "var(--tr-brand-300)" }}>{today} · Dubai</div>
              <h1 className="digest-mast-h1" style={{ fontFamily: "var(--tr-font-display)", fontWeight: 800, fontSize: 46, lineHeight: 1.02, letterSpacing: "-0.035em", margin: "13px 0 0", color: "#fff" }}>The Daily Digest</h1>
              <p style={{ fontSize: 15.5, color: "rgba(255,255,255,0.58)", margin: "10px 0 0", maxWidth: 600, lineHeight: 1.6, textWrap: "pretty" }}>UAE fitness in one glance: news, jobs, provider reviews, AI and the business of training.</p>
            </div>
            <a className="tr-btn tr-btn-sm" href="https://wa.me/971541771709?text=Hi%2C%20send%20me%20the%20daily%20digest%20every%20morning." target="_blank" rel="noopener noreferrer" style={{ background: "#fff", color: "var(--tr-ink)", border: "none", fontWeight: 700, textDecoration: "none", flexShrink: 0 }}>
              Get the morning edition in your chat →
            </a>
          </div>
        </div>
      </div>

      <div className="container" style={{ padding: "32px var(--tr-gutter) 80px" }}>
        <GuideHero />

        {/* the agent's morning brief - read this and you're done; ask for more in chat */}
        <div className="tr-card digest-glance" style={{ margin: "28px auto 0", maxWidth: 860, padding: "24px 26px", borderColor: "var(--tr-brand-200)", background: "var(--tr-brand-50)" }}>
          <div className="tr-eyebrow-row" style={{ color: "var(--tr-brand-700)", marginBottom: 10 }}><span className="live-dot" style={{ width: 7, height: 7 }}></span> Today in 20 seconds</div>
          <p style={{ fontFamily: "var(--tr-font-display)", fontWeight: 500, fontSize: 19, lineHeight: 1.5, letterSpacing: "-0.01em", color: "var(--tr-ink)", margin: 0, maxWidth: 760, textWrap: "pretty" }}>{feed.brief}</p>
          <div style={{ display: "flex", alignItems: "center", gap: 14, marginTop: 18, flexWrap: "wrap" }}>
            <a className="tr-btn tr-btn-primary tr-btn-sm" href="https://wa.me/971541771709?text=Hi%2C%20I%20have%20a%20question%20about%20today%27s%20digest%3A%20" target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none" }}>Ask the agent about any of this</a>
            <span style={{ fontSize: 12.5, color: "var(--tr-fg-muted)", fontFamily: "var(--tr-font-mono)" }}>answers come from our archive + the verified registry, not a web search</span>
          </div>
        </div>

        {/* single column - one continuous stream, newest first, centered */}
        <div style={{ maxWidth: 860, margin: "40px auto 0" }}>
          <div>
            <div className="tr-eyebrow-row" style={{ marginBottom: 14, display: "flex", alignItems: "baseline", gap: 10 }}>
              Live · updated through the day
              <span style={{ fontFamily: "var(--tr-font-mono)", fontSize: 11, fontWeight: 500, letterSpacing: 0, textTransform: "none", color: "var(--tr-fg-faint)" }}>
                {pageCount > 1 ? `page ${page + 1} of ${pageCount} · ${full.length} total` : "every published article"}
              </span>
            </div>
            {display.map((it) => it.kind === "job" ? <DigestJobRow key={it.id} j={it} /> : <DigestNewsRow key={it.id} n={it} />)}

            {jobsHidden > 0 && (
              <div style={{ background: "var(--tr-paper-2)", border: "1px solid var(--tr-line)", borderRadius: 14, padding: "20px 22px", marginTop: 18, display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16, flexWrap: "wrap" }}>
                <div>
                  <div style={{ fontFamily: "var(--tr-font-display)", fontWeight: 700, fontSize: 17, color: "var(--tr-ink)" }}>{jobsHidden} more live {jobsHidden === 1 ? "job" : "jobs"} in the feed</div>
                  <div style={{ fontSize: 14, color: "var(--tr-fg-2)", marginTop: 2 }}>Sign in to see every live job as it lands. Free, takes a minute.</div>
                </div>
                <button className="tr-btn tr-btn-primary" onClick={() => { if (window.TRAuth) window.TRAuth.openModal(); }}>Sign in free</button>
              </div>
            )}

            {pageCount > 1 && (
              <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16, marginTop: 26, paddingTop: 22, borderTop: "1px solid var(--tr-line-2)" }}>
                <button className="tr-btn tr-btn-sm" disabled={page === 0} onClick={() => goToPage(page - 1)} style={{ opacity: page === 0 ? 0.4 : 1 }}>
                  <Icon name="arrow-left" size={15} /> Prev
                </button>
                <span style={{ fontSize: 13, fontFamily: "var(--tr-font-mono)", color: "var(--tr-fg-muted)" }}>Page {page + 1} of {pageCount}</span>
                <button className="tr-btn tr-btn-sm" disabled={page >= pageCount - 1} onClick={() => goToPage(page + 1)} style={{ opacity: page >= pageCount - 1 ? 0.4 : 1 }}>
                  Next <Icon name="arrow-right" size={15} />
                </button>
              </div>
            )}

            <div style={{ display: "flex", alignItems: "center", gap: 9, marginTop: 26, paddingTop: 22, borderTop: "1px solid var(--tr-line-2)", fontSize: 13.5, color: "var(--tr-fg-muted)" }}>
              <span className="live-dot" style={{ width: 7, height: 7 }}></span> You're all caught up · refreshed this morning, every item linked to its source
            </div>
          </div>

        </div>
      </div>
    </main>
  );
}

function Article({ id, onBack, onReadNews }) {
  const { ARTICLES } = window.TR_CONTENT;
  const a = ARTICLES.find((x) => x.id === id) || ARTICLES[0];
  const more = ARTICLES.filter((x) => x.id !== a.id).slice(0, 3);
  return (
    <main style={{ background: "#fff", minHeight: "70vh" }}>
      <div className="container-narrow" style={{ padding: "24px var(--tr-gutter) 72px" }}>
        <button className="tr-btn tr-btn-ghost tr-btn-sm" onClick={onBack} style={{ padding: "6px 0", color: "var(--tr-fg-muted)", marginBottom: 8 }}><Icon name="arrow-left" size={16} /> All news</button>
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 14 }}><NewsCat tone={a.tone}>{a.category}</NewsCat><span style={{ fontSize: 13.5, color: "var(--tr-fg-muted)" }}>{a.date} · {a.read} read</span></div>
        <h1 style={{ fontFamily: "var(--tr-font-display)", fontWeight: 700, fontSize: 40, lineHeight: 1.08, letterSpacing: "-0.03em", margin: 0, color: "var(--tr-ink)", textWrap: "balance" }}>{a.title}</h1>
        <p style={{ fontSize: 20, lineHeight: 1.5, color: "var(--tr-fg-2)", margin: "18px 0 0", textWrap: "pretty" }}>{a.dek}</p>
        <hr style={{ border: "none", borderTop: "1px solid var(--tr-line)", margin: "28px 0" }} />
        <div style={{ display: "flex", flexDirection: "column", gap: 18 }}>
          {a.body.map((b, i) => {
            if (b && b.h) return <h2 key={i} className="art-h">{b.h}</h2>;
            if (b && b.q) return <blockquote key={i} className="art-q">{b.q}</blockquote>;
            return <p key={i} className="art-p">{b}</p>;
          })}
        </div>

        {a.takeaway && (
          <div className="art-takeaway">
            <div className="art-takeaway-label">The bottom line</div>
            <p style={{ margin: "6px 0 0", fontFamily: "var(--tr-font-display)", fontWeight: 500, fontSize: 19, lineHeight: 1.45, color: "var(--tr-ink)", letterSpacing: "-0.01em", textWrap: "pretty" }}>{a.takeaway}</p>
          </div>
        )}

        <div style={{ background: "var(--tr-brand-50)", border: "1px solid var(--tr-brand-100)", borderRadius: 14, padding: "20px 22px", marginTop: 30, display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16, flexWrap: "wrap" }}>
          <div>
            <div style={{ fontFamily: "var(--tr-font-display)", fontWeight: 700, fontSize: 17, color: "var(--tr-ink)" }}>Not sure what this means for you?</div>
            <div style={{ fontSize: 14, color: "var(--tr-fg-2)", marginTop: 2 }}>Ask the guide and we'll map it to your goal, for free.</div>
          </div>
          <button className="tr-btn tr-btn-primary">Ask the guide</button>
        </div>

        {a.sources && (
          <div style={{ marginTop: 40 }}>
            <div className="tr-eyebrow-row" style={{ marginBottom: 14 }}>Sources &amp; further reading</div>
            <ol className="art-sources">
              {a.sources.map((sr, i) => (
                <li key={i}>
                  <span className="art-src-n">{i + 1}</span>
                  <span style={{ flex: 1 }}>{sr.t}</span>
                  <span className="art-src-k">{sr.k}</span>
                </li>
              ))}
            </ol>
            <p style={{ fontSize: 12.5, color: "var(--tr-fg-faint)", margin: "12px 0 0", lineHeight: 1.5 }}>Figures are illustrative and compiled for this demo; verify against the named sources before relying on them.</p>
          </div>
        )}

        <hr style={{ border: "none", borderTop: "1px solid var(--tr-line)", margin: "40px 0 22px" }} />
        <div style={{ fontFamily: "var(--tr-font-display)", fontWeight: 700, fontSize: 18, color: "var(--tr-ink)", marginBottom: 8 }}>More from the brief</div>
        <div style={{ display: "flex", flexDirection: "column" }}>
          {more.map((m, i) => (
            <button key={m.id} onClick={() => onReadNews(m.id)} className="art-more-row" style={{ borderTop: i ? "1px solid var(--tr-line-2)" : "none" }}>
              <div style={{ flex: 1 }}>
                <div style={{ marginBottom: 5 }}><NewsCat tone={m.tone}>{m.category}</NewsCat></div>
                <div style={{ fontFamily: "var(--tr-font-display)", fontWeight: 600, fontSize: 16, color: "var(--tr-ink)" }}>{m.title}</div>
              </div>
              <span className="art-more-go" aria-hidden="true">→</span>
            </button>
          ))}
        </div>
      </div>
    </main>
  );
}

window.NewsPage = NewsPage;
window.Article = Article;
