/* global React, GOLA, PinContext, EvBars, ObjRail, AskMakerLab, RealClaimCard */
// =========================================================
// Insights tab — Path-B master/detail (à la "Findings — Room to Breathe").
// LEFT  index: grouped by insight headline; each group lists the findings
//       linked to that headline (curated in evidence-data.jsx).
// RIGHT reading panel: scrolls. Each insight's headline + description is a
//       STICKY band that stays pinned while you scroll its findings. Every
//       finding renders as a full page with the SAME chart + description
//       used on the Findings tab (RealClaimCard).
// =========================================================

function rfIndex() {
  const RF = window.REAL_FINDINGS;
  if (!RF || !RF.CLAIMS) return {};
  const idx = {};
  RF.CLAIMS.forEach((f) => { idx[f.id] = f; });
  return idx;
}

function InsightsLoop() {
  const store = React.useContext(PinContext);
  const insights = store.insights;
  const RealCard = window.RealClaimCard;
  const { study, bench } = window.useFindingsStudyBench();

  const RF = React.useMemo(rfIndex, []);

  // Resolve each insight → its linked findings / evidence.
  const groups = React.useMemo(() => insights.map((ins) => {
    if (ins.source === "user") {
      const evCards = (ins.ev || []).map((id) => GOLA.EV_INDEX[id]).filter(Boolean);
      return { ins, kind: "user", evCards };
    }
    const findings = (ins.findings || []).map((id) => RF[id]).filter(Boolean);
    const exc = ins.findingsException && RF[ins.findingsException.id]
      ? { finding: RF[ins.findingsException.id], note: ins.findingsException.note }
      : null;
    const grp = ins.findingsGroup
      ? { label: ins.findingsGroup.label, sub: ins.findingsGroup.sub,
          findings: ins.findingsGroup.ids.map((id) => RF[id]).filter(Boolean) }
      : null;
    return { ins, kind: "curated", findings, exc, grp, note: ins.findingsNote };
  }), [insights, RF]);

  // user insights first, then curated (matches the numbering on the cards)
  const ordered = React.useMemo(() => {
    const u = groups.filter((g) => g.ins.source === "user");
    const c = groups.filter((g) => g.ins.source === "curated");
    return [...u, ...c];
  }, [groups]);
  const numOf = {};
  ordered.forEach((g, i) => { numOf[g.ins.id] = String(i + 1).padStart(2, "0"); });

  // key -> finding object, so the chat can follow the scrolled finding
  const keyToFinding = {};
  ordered.forEach((g) => {
    if (g.kind === "curated") {
      g.findings.forEach((f) => { keyToFinding[g.ins.id + "::" + f.id] = f; });
      if (g.exc) keyToFinding[g.ins.id + "::exc"] = g.exc.finding;
    }
  });

  const sectionRefs = React.useRef({});
  const findingRefs = React.useRef({});
  const readRef = React.useRef(null);
  const mlRef = React.useRef(null);
  const [activeKey, setActiveKey] = React.useState(null);
  const [activeIns, setActiveIns] = React.useState(ordered[0] ? ordered[0].ins.id : null);

  function stickyH(insId) {
    const sec = sectionRefs.current[insId];
    const h = sec && sec.querySelector(".gl-bx-sticky");
    return h ? h.offsetHeight : 130;
  }
  function scrollToFinding(insId, key) {
    const el = findingRefs.current[key];
    const sc = readRef.current;
    if (!el || !sc) return;
    const top = el.getBoundingClientRect().top - sc.getBoundingClientRect().top
      + sc.scrollTop - stickyH(insId) - 16;
    sc.scrollTo({ top, behavior: "smooth" });
    setActiveKey(key); setActiveIns(insId);
  }
  function scrollToSection(insId) {
    const el = sectionRefs.current[insId];
    const sc = readRef.current;
    if (!el || !sc) return;
    const top = el.getBoundingClientRect().top - sc.getBoundingClientRect().top + sc.scrollTop - 6;
    sc.scrollTo({ top, behavior: "smooth" });
    setActiveIns(insId); setActiveKey(null);
  }

  // scroll-spy via IntersectionObserver (fires on any scroll, height-robust):
  // a thin "active line" ~30% down the reading panel selects the section +
  // finding currently under the sticky head.
  React.useEffect(() => {
    const sc = readRef.current;
    if (!sc) return;
    const finds = new Map();
    const secs = new Map();
    const apply = () => {
      let topSec = null, topSecY = -Infinity;
      secs.forEach((y, id) => { if (y > topSecY) { topSecY = y; topSec = id; } });
      let topFind = null, topFindY = -Infinity;
      finds.forEach((y, key) => {
        if (topSec && key.indexOf(topSec + "::") !== 0) return;
        if (y > topFindY) { topFindY = y; topFind = key; }
      });
      if (topSec) setActiveIns(topSec);
      setActiveKey(topFind);
    };
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        const k = e.target.getAttribute("data-spy");
        const s = e.target.getAttribute("data-spysec");
        if (k != null) { e.isIntersecting ? finds.set(k, e.boundingClientRect.top) : finds.delete(k); }
        if (s != null) { e.isIntersecting ? secs.set(s, e.boundingClientRect.top) : secs.delete(s); }
      });
      apply();
    }, { root: sc, rootMargin: "-20% 0px -60% 0px", threshold: 0 });
    Object.values(sectionRefs.current).forEach((el) => el && io.observe(el));
    Object.values(findingRefs.current).forEach((el) => el && io.observe(el));
    return () => io.disconnect();
  }, [ordered]);

  // focus an insight from the pin toast
  React.useEffect(() => {
    if (!store.focusInsight) return;
    const id = store.focusInsight;
    const t = setTimeout(() => { scrollToSection(id); store.setFocusInsight(null); }, 90);
    return () => clearTimeout(t);
  }, [store.focusInsight]);

  const linkCount = (g) => g.kind === "curated"
    ? g.findings.length + (g.exc ? 1 : 0) + (g.grp ? 1 : 0)
    : g.evCards.length;
  const totalLinks = ordered.reduce((s, g) => s + linkCount(g), 0);

  return (
    <div className="gl gl-insx2">
      {/* ---------- Left index ---------- */}
      <aside className="gl-bx-index">
        <div className="gl-bx-index__scroll">
          {ordered.map((g) => (
            <div className="gl-bx-grp" key={g.ins.id}>
              <button
                className={"gl-bx-grp__head" + ((activeIns === g.ins.id && (!activeKey || activeKey.indexOf(g.ins.id + "::") !== 0)) ? " is-active" : "")}
                onClick={() => scrollToSection(g.ins.id)}
              >
                <span className="gl-bx-grp__num">{numOf[g.ins.id]}</span>
                <span className="gl-bx-grp__title">
                  {g.ins.source === "user" ? (g.ins.title || (g.ins.drafting ? "Drafting…" : "Untitled")) : (g.ins.nav || g.ins.title)}
                </span>
              </button>

              {g.kind === "curated" ? (
                <React.Fragment>
                  {g.findings.map((f) => {
                    const key = g.ins.id + "::" + f.id;
                    return (
                      <button key={key} className={"gl-bx-item" + (activeKey === key ? " is-sel" : "")} onClick={() => scrollToFinding(g.ins.id, key)}>
                        <span className={"gl-bx-item__dot gl-bx-item__dot--" + f.sig}></span>
                        <span className="gl-bx-item__txt">{f.statement}</span>
                        <span className="gl-bx-item__ask" role="button" tabIndex={0} title="Ask MakerLab about this finding"
                          onClick={(e) => { e.stopPropagation(); scrollToFinding(g.ins.id, key); mlRef.current && mlRef.current.openWith(f); }}>
                          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
                          Ask
                        </span>
                      </button>
                    );
                  })}
                  {g.exc && (() => {
                    const key = g.ins.id + "::exc";
                    return (
                      <button key={key} className={"gl-bx-item gl-bx-item--exc" + (activeKey === key ? " is-sel" : "")} onClick={() => scrollToFinding(g.ins.id, key)}>
                        <span className="gl-bx-item__exctag">EXC</span>
                        <span className="gl-bx-item__txt">{g.exc.finding.statement}</span>
                        <span className="gl-bx-item__ask" role="button" tabIndex={0} title="Ask MakerLab about this finding"
                          onClick={(e) => { e.stopPropagation(); scrollToFinding(g.ins.id, key); mlRef.current && mlRef.current.openWith(g.exc.finding); }}>
                          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
                          Ask
                        </span>
                      </button>
                    );
                  })()}
                  {g.grp && (() => {
                    const key = g.ins.id + "::grp";
                    return (
                      <button key={key} className={"gl-bx-item gl-bx-item--grp" + (activeKey === key ? " is-sel" : "")} onClick={() => scrollToFinding(g.ins.id, key)}>
                        <span className="gl-bx-item__dot gl-bx-item__dot--mid"></span>
                        <span className="gl-bx-item__txt">{g.grp.label} <b>· {g.grp.findings.length}</b></span>
                      </button>
                    );
                  })()}
                  {g.findings.length === 0 && !g.exc && !g.grp && (
                    <div className="gl-bx-item__empty">No linked findings</div>
                  )}
                </React.Fragment>
              ) : (
                g.evCards.length ? g.evCards.map((c) => {
                  const key = g.ins.id + "::" + c.id;
                  return (
                    <button key={key} className={"gl-bx-item" + (activeKey === key ? " is-sel" : "")} onClick={() => scrollToFinding(g.ins.id, key)}>
                      <span className={"gl-bx-item__dot gl-bx-item__dot--" + GOLA.evSig(c.id)}></span>
                      <span className="gl-bx-item__txt">{c.title}</span>
                    </button>
                  );
                }) : <div className="gl-bx-item__empty">No evidence pinned</div>
              )}
            </div>
          ))}
        </div>
      </aside>

      {/* ---------- Reading panel ---------- */}
      <div className="gl-bx-read" ref={readRef}>
        {ordered.map((g) => (
          <section className="gl-bx-sec" key={g.ins.id} data-spysec={g.ins.id} data-screen-label={"Insight " + numOf[g.ins.id]} ref={(el) => { sectionRefs.current[g.ins.id] = el; }}>
            <StickyInsightHead g={g} num={numOf[g.ins.id]} store={store} />
            <div className="gl-bx-sec__body">
              {g.ins.action && (
                <div className="gl-bx-action">
                  <span className="gl-bx-action__label">Recommendation</span>
                  <span className="gl-bx-action__txt" contentEditable={!store.readOnly} suppressContentEditableWarning>{g.ins.action}</span>
                </div>
              )}

              {g.kind === "curated" ? (
                <React.Fragment>
                  {g.findings.map((f) => (
                    <div className="gl-bx-page" key={g.ins.id + "::" + f.id} data-spy={g.ins.id + "::" + f.id} ref={(el) => { findingRefs.current[g.ins.id + "::" + f.id] = el; }}>
                      <RealCard c={f} objId={g.ins.objs ? g.ins.objs[0] : null} study={study} bench={bench} showVerbatim={false} />
                    </div>
                  ))}
                  {g.exc && (
                    <div className="gl-bx-page" data-spy={g.ins.id + "::exc"} ref={(el) => { findingRefs.current[g.ins.id + "::exc"] = el; }}>
                      <div className="gl-bx-exc">
                        <div className="gl-bx-exc__tag">
                          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/></svg>
                          Exception · qualifies the headline
                        </div>
                        <div className="gl-bx-exc__note">{g.exc.note}</div>
                      </div>
                      <RealCard c={g.exc.finding} objId={g.ins.objs ? g.ins.objs[0] : null} study={study} bench={bench} showVerbatim={false} />
                    </div>
                  )}
                  {g.grp && (
                    <div className="gl-bx-page" data-spy={g.ins.id + "::grp"} ref={(el) => { findingRefs.current[g.ins.id + "::grp"] = el; }}>
                      <ConsistentGroup group={g.grp} />
                    </div>
                  )}
                  {g.note && (
                    <div className="gl-bx-note">
                      <span className="gl-bx-note__badge">No linked findings in this batch</span>
                      <span className="gl-bx-note__txt">{g.note}</span>
                    </div>
                  )}
                </React.Fragment>
              ) : (
                <UserInsightBody g={g} store={store} findingRefs={findingRefs} />
              )}
            </div>
          </section>
        ))}
        <div className="gl-bx-read__end">End · {ordered.length} insight{ordered.length === 1 ? "" : "s"}</div>
      </div>

      <AskMakerLab
        ref={mlRef}
        greeting="Ask me what these insights mean for your work — what to buy, what to brief design on, or how to talk about it."
        presets={INSIGHT_PRESETS}
        onRun={handleInsightAsk}
        thinkingText="Reading the evidence against your objectives…"
        placeholder="Ask about these insights…"
        presetsFor={window.MakerLabCtx && window.MakerLabCtx.presetsFor}
        answerFor={window.MakerLabCtx && window.MakerLabCtx.answerFor}
        greetingFor={window.MakerLabCtx && window.MakerLabCtx.greetingFor}
        activeItem={activeKey ? keyToFinding[activeKey] : null}
      />
    </div>
  );
}

// ---- Sticky headline + description band (always visible while scrolling its findings) ----
function StickyInsightHead({ g, num, store }) {
  const ins = g.ins;
  const isUser = ins.source === "user";
  return (
    <div className="gl-bx-sticky">
      <div className="gl-bx-sticky__inner">
        <div className="gl-bx-sticky__top">
          <span className="gl-bx-sticky__num">{num}</span>
          <span className={"gl-srcbadge" + (isUser ? " gl-srcbadge--user" : "")}>{isUser ? "✦ Yours" : "Published"}</span>
          {ins.objs && ins.objs.length > 0 && (
            <span className="gl-bx-sticky__objs">
              {ins.objs.map((o) => <span key={o} className="gl-bx-objtag">O{o}</span>)}
            </span>
          )}
          <span className="gl-bx-sticky__spacer"></span>
          {isUser && !ins.drafting && (
            <button className="gl-publishbtn" onClick={() => store.publishInsight(ins.id)} title="Publish into the curated set">
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
              Publish
            </button>
          )}
        </div>
        <Headline insight={ins} store={store} />
        {ins.tagline && <div className="gl-bx-sticky__desc">{ins.tagline}</div>}
      </div>
    </div>
  );
}

// ---- Collapsed "consistent across segments" group ----
function ConsistentGroup({ group }) {
  const [open, setOpen] = React.useState(false);
  return (
    <div className={"gl-bx-grpcard" + (open ? " is-open" : "")}>
      <button className="gl-bx-grpcard__bar" onClick={() => setOpen((o) => !o)}>
        <span className={"gl-arg__caret" + (open ? " is-open" : "")}>▸</span>
        <span className="gl-bx-grpcard__label">{group.label}</span>
        <span className="gl-bx-grpcard__count">{group.findings.length} findings</span>
      </button>
      {group.sub && <div className="gl-bx-grpcard__sub">{group.sub}</div>}
      {open && (
        <div className="gl-bx-grpcard__list">
          {group.findings.map((f) => (
            <div className="gl-bx-grprow" key={f.id}>
              <span className={"gl-bx-item__dot gl-bx-item__dot--" + f.sig}></span>
              <span className="gl-bx-grprow__txt">{f.statement}</span>
              <span className="gl-bx-grprow__metric" title={f.metricLabel}>{f.metricVal}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ---- User-built insight: pinned evidence pages + optional synthesis ----
function UserInsightBody({ g, store, findingRefs }) {
  const ins = g.ins;
  return (
    <React.Fragment>
      {g.evCards.length === 0 ? (
        <div className="gl-bx-note"><span className="gl-bx-note__txt">No evidence pinned yet — add cuts from the Evidence tab.</span></div>
      ) : g.evCards.map((c) => (
        <div className="gl-bx-page gl-bx-page--ev" key={ins.id + "::" + c.id} data-spy={ins.id + "::" + c.id} ref={(el) => { findingRefs.current[ins.id + "::" + c.id] = el; }}>
          <EvBox card={c} store={store} onRemove={() => store.removeEvidence(ins.id, c.id)} expanded />
        </div>
      ))}
      {ins.structured && ins.structure && (
        <div className="gl-structure">
          <div className="gl-resolution">
            <div className="gl-resolution__label">Resolution</div>
            <div className="gl-resolution__text" contentEditable suppressContentEditableWarning>{ins.structure.resolution}</div>
          </div>
          {ins.structure.args.map((a) => <Argument key={a.id} arg={a} store={store} />)}
        </div>
      )}
    </React.Fragment>
  );
}

// ---- Headline editor (contentEditable, saves on blur) ----
function Headline({ insight, store }) {
  if (insight.drafting) {
    return (
      <div className="gl-story__headline is-drafting">
        <span className="gl-shimmer">✦</span> Drafting headline from your evidence…
      </div>
    );
  }
  return (
    <div
      className="gl-story__headline"
      contentEditable={!store.readOnly}
      suppressContentEditableWarning
      key={insight.id + "-ready"}
      onBlur={(e) => store.setHeadline(insight.id, e.currentTarget.textContent.trim())}
    >{insight.title || "Write a headline…"}</div>
  );
}

// ---- Collapsible argument (groups findings) ----
function Argument({ arg, store }) {
  const [open, setOpen] = React.useState(true);
  return (
    <div className="gl-arg">
      <div className="gl-arg__head" onClick={() => setOpen((o) => !o)}>
        <span className={"gl-arg__caret" + (open ? " is-open" : "")}>▸</span>
        <span className="gl-arg__id">{arg.id}</span>
        <span className="gl-arg__summary">{arg.summary}</span>
        <span className="gl-arg__nfind">{arg.findings.length} {arg.findings.length === 1 ? "finding" : "findings"}</span>
      </div>
      {open && (
        <div className="gl-arg__body">
          {arg.findings.map((f) => (
            <div className="gl-find" key={f.id}>
              <div className="gl-find__row">
                <span className="gl-find__dot"></span>
                <span className="gl-find__text">{f.text}</span>
              </div>
              <div className="gl-find__ev">
                {f.ev.map((e) => (
                  e.card
                    ? <EvBox key={e.id} card={e.card} store={store} />
                    : <div className="gl-find__ev-row" key={e.id}><span className="gl-find__ev-id">{e.id}</span></div>
                ))}
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ---- Insights-tab Ask MakerLab presets — framed for the brand-side people
// who act on these (merchants, designers, marketers), not researchers. ----
const INSIGHT_PRESETS = [
  { q: "Which styles should anchor each account's buy?",
    answer: "Lead every account with the same five — Hawk Twill, Championship, Sprinter Reflect, Delta Tonal and Elan. They land top-five with Anthropologie, Free People and department-store shoppers alike (the leader only moves about 4 points across accounts), so you can merchandise one core line everywhere and use the open slots for account-specific flavor.",
    confidence: "High", sources: ["Product ratings × retailer", "Overall sentiment ranking"] },
  { q: "What should I brief design to focus on for FW26?",
    answer: "Materialization, not new shapes. The same silhouette swings 12–19 points on its upper alone — Hawk in twill beats standard Hawk by 19 points, and mesh or heavy quilt consistently drags. Put development hours into fabric and upper treatment on the franchises you already have rather than briefing brand-new silhouettes.",
    confidence: "High", sources: ["Within-franchise ratings", "Product feedback"] },
  { q: "What message should marketing lead with?",
    answer: "Wardrobe-building, not durability. 59% buy for the design or to add a new style (only 16% to replace), 82% already own three or more pairs, and 80% wear them daily or weekly. Talk to shoppers like these are fashion pieces — that's what supports the premium price, not 'they last longer.'",
    confidence: "High", sources: ["Purchase reason", "Wear frequency", "Pairs owned"] },
  { q: "Do we need a separate range for aspirational shoppers?",
    answer: "No. The aspirational segment is half your sample and its top-five is identical to everyone else's — just +1 to +4 points higher on the same styles. Don't brief a separate line; the lift comes from shelf presence and visual merch at the premium retailers, so invest there instead.",
    confidence: "Moderate", sources: ["Aspirational segment vs overall", "Retailer overlap"] },
];
function handleInsightAsk(preset) {
  if (preset.free) {
    return {
      text: "Reading it against everything pinned, the most useful day-to-day takeaway is materialization: the same shape wins or loses on its upper, swinging sentiment 12–19 points. So when you're choosing what to push, get the fabric and upper right on the cores you already have before chasing new silhouettes.",
      confidence: "Moderate", sources: ["Within-franchise ratings"],
    };
  }
  return { text: preset.answer, confidence: preset.confidence, sources: preset.sources };
}

function famLabel(key) {
  const f = GOLA.FAMILIES.find((x) => x.key === key);
  return f ? f.title.replace(" comparison", "").replace(" drill-down", "").toUpperCase() : key.toUpperCase();
}

// ---- Reusable inline-expandable evidence row (shows the chart in place) ----
function EvBox({ card, store, onRemove, expanded }) {
  const [open, setOpen] = React.useState(!!expanded);
  const isHidden = store.hidden.includes(card.id);
  return (
    <div className={"gl-evbox" + (open ? " is-open" : "") + (isHidden ? " is-hidden" : "")}>
      <div className="gl-evbox__bar" onClick={() => setOpen((o) => !o)}>
        <span className={"gl-arg__caret" + (open ? " is-open" : "")}>▸</span>
        <span className="gl-evbox__title">{card.title}</span>
        {isHidden && <span className="gl-evbox__hiddentag">Hidden from external</span>}
        <span
          className={"gl-sigflag gl-sigflag--" + GOLA.evSig(card.id)}
          data-tip={GOLA.SIG_META[GOLA.evSig(card.id)].short + " significance — " + GOLA.SIG_META[GOLA.evSig(card.id)].tip}
        >
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"></path>
            <line x1="4" y1="22" x2="4" y2="15"></line>
          </svg>
        </span>
        <button
          className={"gl-evbox__hidebtn" + (isHidden ? " is-hidden" : "")}
          onClick={(e) => { e.stopPropagation(); isHidden ? store.unhide(card.id) : store.hide(card.id); }}
          title={isHidden ? "Hidden from external viewers — click to show" : "Hide this data from external viewers"}
        >
          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            {isHidden
              ? <React.Fragment><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path><line x1="1" y1="1" x2="23" y2="23"></line></React.Fragment>
              : <React.Fragment><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></React.Fragment>}
          </svg>
          {isHidden ? "Hidden" : "Hide"}
        </button>
        {onRemove && (
          <button
            className="gl-evbox__remove"
            onClick={(e) => { e.stopPropagation(); onRemove(); }}
            title="Remove from this insight"
            aria-label="Remove from this insight"
          >×</button>
        )}
      </div>
      {open && (
        <div className="gl-evbox__body">
          <div className="gl-ev__q">{card.q}</div>
          <EvBars bars={card.bars} unit={card.unit} />
          <div className="gl-evbox__n">n = {card.n.toLocaleString()}</div>
        </div>
      )}
    </div>
  );
}

window.InsightsLoop = InsightsLoop;
window.ConsistentGroup = ConsistentGroup;
