/* ── Acasă / Calendarul meu / Plan strategic (credinte + valori) ────────────────
   Au pornit pe 24.07 ca variante PARALELE („(nou)"), langa paginile de atunci, ca sa
   aleaga Petre. Pe 25.07 Marian a ales: paginile vechi (AcasaView / CalendarView /
   PlanStrategicView) au fost STERSE din app.jsx, iar astea le-au luat locul si slugurile
   („acasa" / „calendar" / „plan"); store-urile salvate cu slugurile „-nou" se traduc la
   incarcare (REDENUMIRI in loadStore).
   Fiecare pagina arata cele doua fluxuri in BLOCURI SEPARATE, nu amestecate.
   Unirea: itemii credintelor (planEntries) + itemii valorilor (window.valItemiPlan -
   rutinele Z/S/L + angajamentul din Codul meu), bifati in ACELASI store.log; cheile
   valorilor au prefixul "val|" si nu se ciocnesc cu ale credintelor.
   Helperii vin din window.PRP_UTIL (ancora aditiva din app.jsx). */

function hibridItemi(store) {
  const U = window.PRP_UTIL;
  const cred = U.planEntries(store.plan);
  const val = (window.valItemiPlan ? window.valItemiPlan(store) : []);
  return { cred, val, toate: [...cred, ...val] };
}
/* sub-eticheta sursei pe un item, indiferent de flux */
function hibridSub(e) {
  if (e.cred) return e.cred.nume + " · Aria " + String(e.aria.nr).padStart(2, "0");
  return e.valoare.nume + " · " + e.eticheta.toLowerCase();
}
function hibridToggleLog(set) {
  return (key, pk) => set((s) => {
    const forKey = { ...(s.log[key] || {}) };
    if (forKey[pk]) delete forKey[pk]; else forKey[pk] = true;
    return { ...s, log: { ...s.log, [key]: forKey } };
  });
}

/* ── Acasă (nou) ── */
function AcasaHibridView({ store, set }) {
  const U = window.PRP_UTIL;
  const azi = new Date(); azi.setHours(0, 0, 0, 0);
  const aziISO = U.fmtISO(azi);
  const { cred, val, toate } = hibridItemi(store);
  /* zilnicele se numara PE FLUX (benzile saptamanii + cifra zilei sunt despartite);
     agregat raman doar conformarea pe 7 zile si sirul de zile complete, care masoara procesul intreg */
  const sumOA = cred.reduce((n, e) => n + (e.oa || 0), 0);
  const sumOR = cred.reduce((n, e) => n + (e.orNr || 0), 0);
  /* conformarea si sirul de zile NU se mai calculeaza pe tot: fiecare flux le are pe ale lui,
     langa statisticile lui (vezi grupurile de mai jos) */
  const nume = store.consola.nume.trim();
  const prenume = nume.split(/\s+/)[0] || "";
  const dataRo = azi.toLocaleDateString("ro-RO", { weekday: "long", day: "numeric", month: "long", year: "numeric" });
  let ziProces = null;
  if (store.consola.dataStart) {
    const start = new Date(store.consola.dataStart + "T00:00:00");
    if (!isNaN(start) && start <= azi) ziProces = Math.floor((azi - start) / 86400000) + 1;
  }
  const toggleLog = hibridToggleLog(set);
  const monday = U.startOfWeek(azi);
  const days = Array.from({ length: 7 }, (_, i) => U.addDays(monday, i));
  const saptPk = U.periodKey("Săptămânal", azi);
  const goto = (view) => () => set((s) => ({ ...s, view }));
  /* DOUA definitii ale zilei, alaturi (cerinta Marian 24.07 seara): una din dictionarul
     credintelor + una din dictionarul valorilor - fiecare cu rotatia ei pe ziua anului */
  const dictValori = React.useMemo(() => {
    const din = [];
    const pv = window.PRP_VALORI || {};
    (pv.concepte || []).forEach((c) => din.push({ termen: c.nume, definitie: c.definitie }));
    (pv.categorii || []).forEach((c) => c.valori.forEach((v) => din.push({ termen: v.nume, definitie: v.definitie })));
    return din;
  }, []);

  const randAzi = (e, pk) => {
    const on = U.isLogged(store.log, e.key, pk);
    return (
      <div className="today-row" key={e.key}>
        <button className={"check-btn lg" + (on ? " on" : "")}
          title={on ? "Îndeplinită - apasă pentru a anula" : "Marchează îndeplinită"}
          onClick={() => toggleLog(e.key, pk)}>
          <U.IcCheck size={15} />
        </button>
        <span className={"today-text" + (on ? " done" : "")}>
          {e.text}
          <span className="today-sub">{hibridSub(e)}</span>
        </span>
      </div>
    );
  };

  return (
    <div className="page">
      <div className="panel hero-panel">
        <div>
          <h1 className="hero-salut">{prenume ? `Bună, ${prenume}` : "Bună"}</h1>
          <p className="hero-data">{dataRo}{ziProces ? ` · ziua ${ziProces} din proces` : ""}</p>
          {(() => {
            const zi = Math.floor((azi - new Date(azi.getFullYear(), 0, 0)) / 86400000);
            const dc = U.DICTIONAR || [];
            const rand = (t, tag) => t ? (
              <button key={tag} className="hero-def" style={{ display: "block" }}
                title={"Deschide dicționarul - " + tag} onClick={goto("dictionar")}>
                <span className="hero-def-termen">{t.termen}</span> - {(t.definitie || "").split("\n")[0]}
              </button>
            ) : null;
            return (
              <React.Fragment>
                {rand(dc.length ? dc[zi % dc.length] : null, "credințe")}
                {rand(dictValori.length ? dictValori[zi % dictValori.length] : null, "valori")}
              </React.Fragment>
            );
          })()}
        </div>
      </div>

      {toate.length === 0 ? (
        <div className="panel empty mt-18">
          <div className="empty-title">Procesul nu a început încă</div>
          <div className="small" style={{ maxWidth: 520 }}>
            Alege acțiuni concrete din credințe și valorile care te definesc - împreună formează
            planul tău de implementare, urmărit aici zi de zi.
          </div>
          <div className="row" style={{ gap: 8 }}>
            <button className="btn btn-primary btn-sm" onClick={goto("credinte")}>
              Deschide credințele <U.IcArrow size={13} />
            </button>
            <button className="btn btn-ghost btn-sm" onClick={goto("valori")}>
              Deschide valorile <U.IcArrow size={13} />
            </button>
          </div>
        </div>
      ) : (
        <React.Fragment>
          <div className="grid-2 mt-18" style={{ alignItems: "start" }}>
            {/* cele doua fluxuri, in BLOCURI SEPARATE pe aceeasi pagina (cerinta Marian 25.07):
                fiecare bloc isi tine propriile zilnice / saptamanale / angajamente, ca sa se
                vada dintr-o privire ce vine din credinte si ce din valori */}
            <div className="stack-14">
              {[
                { id: "cred", titlu: "Credințele mele", lista: cred, view: "credinte", buton: "Credințele",
                  gol: "Nicio acțiune aleasă din credințe - deschide o credință și alege acțiunile concrete." },
                { id: "val", titlu: "Valorile mele", lista: val, view: "valori", buton: "Valorile",
                  gol: "Nicio rutină sau angajament din valori - completează-ți Codul meu." },
              ].map((b) => {
                const bDaily = b.lista.filter((e) => e.frecventa === "Zilnic");
                const bSapt = b.lista.filter((e) => e.frecventa === "Săptămânal");
                const bAngaj = b.lista.filter((e) => e.frecventa === "Angajament (la nevoie)");
                return (
                  <div className="panel" key={b.id}>
                    <div className="row" style={{ marginBottom: 4 }}>
                      <p className="sect-label" style={{ margin: 0 }}>{b.titlu} - astăzi</p>
                      <button className="btn btn-ghost btn-sm" style={{ marginLeft: "auto" }} onClick={goto(b.view)}>
                        {b.buton} <U.IcArrow size={12} />
                      </button>
                    </div>
                    {b.lista.length === 0 && <p className="small muted" style={{ margin: "8px 0 0" }}>{b.gol}</p>}
                    {b.lista.length > 0 && bDaily.length === 0 && (
                      <p className="small muted" style={{ margin: "8px 0 0" }}>Nimic zilnic - urmărirea se face pe perioadele alese.</p>
                    )}
                    {bDaily.map((e) => randAzi(e, aziISO))}
                    {bSapt.length > 0 && (
                      <React.Fragment>
                        <p className="sect-label" style={{ marginTop: 16 }}>Săptămâna asta</p>
                        {bSapt.map((e) => randAzi(e, saptPk))}
                      </React.Fragment>
                    )}
                    {bAngaj.length > 0 && (
                      <React.Fragment>
                        <p className="sect-label" style={{ marginTop: 16 }}>Angajamente (la nevoie)</p>
                        {bAngaj.map((e) => randAzi(e, aziISO))}
                      </React.Fragment>
                    )}
                  </div>
                );
              })}
            </div>

            <div className="stack-14">
              <div className="panel">
                <div className="row" style={{ marginBottom: 4 }}>
                  <p className="sect-label" style={{ margin: 0 }}>Săptămâna mea</p>
                  <button className="btn btn-ghost btn-sm" style={{ marginLeft: "auto" }} onClick={goto("calendar")}>
                    Calendarul <U.IcArrow size={12} />
                  </button>
                </div>
                {/* o banda de saptamana pentru FIECARE flux, una sub alta si aliniate pe zile
                    (cerinta Marian 25.07) - se citesc si separat, si comparativ pe aceeasi zi */}
                {[
                  { id: "cred", eticheta: "Credințe", lista: cred.filter((e) => e.frecventa === "Zilnic") },
                  { id: "val", eticheta: "Valori", lista: val.filter((e) => e.frecventa === "Zilnic") },
                ].map((b, idx) => (
                  <React.Fragment key={b.id}>
                    <p className="sect-label" style={{ margin: idx === 0 ? "6px 0 6px" : "14px 0 6px", opacity: .85 }}>
                      {b.eticheta}
                    </p>
                    {b.lista.length === 0 ? (
                      <p className="small muted" style={{ margin: 0 }}>
                        {b.id === "cred" ? "Nicio acțiune zilnică din credințe." : "Nicio rutină zilnică din valori."}
                      </p>
                    ) : (
                      <div className="mini-week">
                        {days.map((d, i) => {
                          const iso = U.fmtISO(d);
                          const viitor = d > azi;
                          const done = viitor ? 0 : b.lista.filter((e) => U.isLogged(store.log, e.key, iso)).length;
                          const plin = !viitor && done === b.lista.length;
                          return (
                            <button key={i}
                              className={"mini-day" + (plin ? " plin" : "") + (U.sameDay(d, azi) ? " azi" : "")}
                              disabled={viitor}
                              title={viitor ? "zi viitoare" : `${done}/${b.lista.length} zilnice - ${b.eticheta.toLowerCase()}`}
                              onClick={goto("calendar")}>
                              <span className="dow">{U.ZILE_RO[i]}</span>
                              <span className="frac">{viitor ? "·" : `${done}/${b.lista.length}`}</span>
                            </button>
                          );
                        })}
                      </div>
                    )}
                  </React.Fragment>
                ))}
              </div>
              {/* statisticile, tot pe fluxuri (cerinta Marian 25.07: „totul trebuie sa fie
                  despartit"): fiecare grup isi masoara PROPRIILE bife - conformarea si sirul
                  de zile complete se calculeaza pe lista fluxului, nu pe amandoua la un loc,
                  deci un flux neglijat nu mai poate fi ascuns de celalalt */}
              {[
                { id: "cred", eticheta: "Credințe", lista: cred },
                { id: "val", eticheta: "Valori", lista: val },
              ].filter((b) => b.lista.length > 0).map((b) => {
                const zilnice = b.lista.filter((e) => e.frecventa === "Zilnic");
                const doneAzi = zilnice.filter((e) => U.isLogged(store.log, e.key, aziISO)).length;
                const c7b = U.conform7(b.lista, store.log, azi);
                const streakB = U.streakZile(b.lista, store.log, azi);
                return (
                  <React.Fragment key={b.id}>
                    <p className="sect-label" style={{ margin: "2px 0 -4px", opacity: .85 }}>{b.eticheta}</p>
                    <div className="stat-row">
                      {zilnice.length > 0 && (
                        <div className="panel stat">
                          <div className="stat-val">{doneAzi} / {zilnice.length}</div>
                          <div className="stat-lbl">zilnice bifate azi</div>
                          <div className="progress" style={{ marginTop: 8 }}><div className="progress-fill good" style={{ width: (doneAzi / zilnice.length) * 100 + "%" }} /></div>
                        </div>
                      )}
                      {b.id === "cred" && sumOA > 0 && (
                        <div className="panel stat">
                          {/* aceeasi ordine ca in Plan si in Consola: alocat intai, realizat dupa
                              (auditul 25.07 a gasit aceeasi masura citita invers pe doua ecrane) */}
                          <div className="stat-val">{sumOA} / {sumOR}</div>
                          <div className="stat-lbl">obiective alocate / realizate</div>
                        </div>
                      )}
                      {b.id === "val" && (
                        <div className="panel stat">
                          <div className="stat-val">{val.length}</div>
                          <div className="stat-lbl">rutine și angajamente asumate</div>
                        </div>
                      )}
                      {c7b && (
                        <div className="panel stat">
                          <div className="stat-val">{c7b.pct}%</div>
                          <div className="stat-lbl">conformare ultimele 7 zile ({c7b.done}/{c7b.expected})</div>
                        </div>
                      )}
                      <div className="panel stat">
                        <div className="stat-val">{streakB}</div>
                        <div className="stat-lbl">{streakB === 1 ? "zi completă la rând" : "zile complete la rând"}</div>
                      </div>
                    </div>
                  </React.Fragment>
                );
              })}
            </div>
          </div>
        </React.Fragment>
      )}
    </div>
  );
}
window.AcasaHibridView = AcasaHibridView;

/* ── Calendarul (nou) ── */
function CalendarHibridView({ store, set }) {
  const U = window.PRP_UTIL;
  const azi = new Date(); azi.setHours(0, 0, 0, 0);
  const [monday, setMonday] = React.useState(() => U.startOfWeek(azi));
  const { cred, val, toate } = hibridItemi(store);
  const days = Array.from({ length: 7 }, (_, i) => U.addDays(monday, i));
  const toggleLog = hibridToggleLog(set);
  const weekLabel = `${days[0].getDate()} ${days[0].toLocaleDateString("ro-RO", { month: "short" })} - ${days[6].getDate()} ${days[6].toLocaleDateString("ro-RO", { month: "short" })} ${days[6].getFullYear()}`;
  const grupuri = [
    ["Zilnic", "Acțiunile și rutinele zilnice - se bifează doar ziua de azi; trecutul rămâne evidență"],
    ["Săptămânal", "Săptămânale - o bifă pe săptămână"],
    ["Lunar", "Lunare"],
    ["Trimestrial", "Trimestriale"],
    ["Anual", "Anuale"],
  ];

  if (toate.length === 0) {
    return (
      <div className="page">
        <div className="panel empty">
          <div className="empty-title">Nimic de urmărit încă</div>
          <div className="small" style={{ maxWidth: 460 }}>
            Alege acțiuni din credințe și completează-ți rutinele valorilor în Codul meu -
            toate apar aici, pe zile.
          </div>
          <div className="row" style={{ gap: 8 }}>
            <button className="btn btn-primary btn-sm" onClick={() => set((s) => ({ ...s, view: "credinte" }))}>
              Credințele <U.IcArrow size={13} />
            </button>
            <button className="btn btn-ghost btn-sm" onClick={() => set((s) => ({ ...s, view: "valori" }))}>
              Valorile <U.IcArrow size={13} />
            </button>
          </div>
        </div>
      </div>
    );
  }

  /* panourile de frecventa ale UNUI flux (credinte sau valori) - aceeasi mecanica pentru
     ambele, doar lista difera; angajamentele intra tot aici, ca sa ramana in blocul sursei */
  const panouriFlux = (lista) => {
    const angajamente = lista.filter((e) => e.frecventa === "Angajament (la nevoie)");
    /* angajamentele se inregistreaza pe ZIUA DE AZI; cat timp te uiti la alta saptamana, butonul
       ar scrie in alta parte decat arata ecranul, deci se blocheaza (audit 25.07) */
    const saptCurenta = days[0] <= azi && azi <= days[6];
    return (
      <React.Fragment>
        {grupuri.map(([frecv, titlu]) => {
          const group = lista.filter((e) => e.frecventa === frecv);
          if (!group.length) return null;
          if (frecv === "Zilnic") {
            return (
              <div className="panel mt-14" key={frecv} style={{ overflowX: "auto" }}>
                <p className="sect-label">{titlu}</p>
                <div className="cal-grid" style={{ minWidth: 560 }}>
                  <span />
                  {days.map((d, i) => (
                    <span className={"cal-dow" + (U.sameDay(d, azi) ? " azi" : "")} key={i}>
                      {U.ZILE_RO[i]}<span className="nr">{d.getDate()}</span>
                    </span>
                  ))}
                  {group.map((e) => (
                    <React.Fragment key={e.key}>
                      <span className="cal-act">
                        {e.text}
                        <span className="sub">{hibridSub(e)}</span>
                      </span>
                      {days.map((d, i) => {
                        const pk = U.fmtISO(d);
                        const on = U.isLogged(store.log, e.key, pk);
                        const eAzi = U.sameDay(d, azi);
                        return (
                          <button key={i}
                            className={"cal-cell" + (on ? " on" : "") + (eAzi ? " azi" : "")}
                            disabled={!eAzi}
                            title={eAzi ? `${e.text} - azi` : (d < azi ? "evidență - doar ziua de azi se poate bifa" : "zi viitoare")}
                            aria-label={`${e.text}, ${pk}: ${on ? "îndeplinită" : "neîndeplinită"}`}
                            onClick={() => toggleLog(e.key, pk)}>
                            <U.IcCheck size={15} />
                          </button>
                        );
                      })}
                    </React.Fragment>
                  ))}
                </div>
              </div>
            );
          }
          const refD = days[0] <= azi && azi <= days[6] ? azi : days[0];
          const pk = U.periodKey(frecv, refD);
          const editabil = pk === U.periodKey(frecv, azi);
          return (
            <div className="panel mt-14" key={frecv}>
              <p className="sect-label">{titlu}</p>
              <div className="cal-grid single">
                {group.map((e) => {
                  const on = U.isLogged(store.log, e.key, pk);
                  return (
                    <React.Fragment key={e.key}>
                      <span className="cal-act">
                        {e.text}
                        <span className="sub">{hibridSub(e)} · {U.periodLabel(frecv, refD)}</span>
                      </span>
                      <button className={"cal-cell cal-wide" + (on ? " on" : "")}
                        disabled={!editabil}
                        title={editabil ? `${e.text} - ${U.periodLabel(frecv, refD)}` : "doar perioada curentă se poate bifa"}
                        onClick={() => toggleLog(e.key, pk)}>
                        <U.IcCheck size={15} />
                      </button>
                    </React.Fragment>
                  );
                })}
              </div>
            </div>
          );
        })}
        {angajamente.length > 0 && (
          <div className="panel mt-14">
            <p className="sect-label">Angajamente (la nevoie) - se înregistrează când situația a apărut</p>
            <div className="cal-grid single">
              {angajamente.map((e) => {
                const pk = U.fmtISO(azi);
                const on = U.isLogged(store.log, e.key, pk);
                return (
                  <React.Fragment key={e.key}>
                    <span className="cal-act">
                      {e.text}
                      <span className="sub">{hibridSub(e)}</span>
                    </span>
                    <button className={"cal-cell cal-wide" + (on ? " on" : "")}
                      disabled={!saptCurenta}
                      title={saptCurenta ? "Marchează pentru azi" : "Angajamentele se marchează pe ziua de azi - întoarce-te la săptămâna curentă"}
                      onClick={() => toggleLog(e.key, pk)}>
                      <U.IcCheck size={15} />
                    </button>
                  </React.Fragment>
                );
              })}
            </div>
          </div>
        )}
      </React.Fragment>
    );
  };

  return (
    <div className="page">
      <div className="week-nav" style={{ marginBottom: 14 }}>
        <button className="btn btn-ghost btn-sm" onClick={() => setMonday(U.addDays(monday, -7))} title="Săptămâna anterioară"><U.IcChevL size={14} /></button>
        <span className="week-label">{weekLabel}</span>
        <button className="btn btn-ghost btn-sm" onClick={() => setMonday(U.addDays(monday, 7))} title="Săptămâna următoare"><U.IcChev size={14} /></button>
        {!U.sameDay(monday, U.startOfWeek(azi)) && (
          <button className="btn btn-ghost btn-sm" onClick={() => setMonday(U.startOfWeek(azi))}>Azi</button>
        )}
      </div>

      {/* doua SECTIUNI pe aceeasi pagina - credintele si valorile, fiecare cu grupurile ei
          de frecventa si cu angajamentele ei (cerinta Marian 25.07). Sectiunea unui flux
          fara itemi nu se afiseaza; daca ambele sunt goale, ecranul gol de mai sus preia. */}
      {[
        { id: "cred", titlu: "Credințele mele", lista: cred },
        { id: "val", titlu: "Valorile mele", lista: val },
      ].map((b) => b.lista.length === 0 ? null : (
        <div key={b.id} style={{ marginTop: 22 }}>
          <p className="sect-label" style={{ margin: "0 0 -4px", fontSize: "var(--m-fs-2)" }}>{b.titlu}</p>
          {panouriFlux(b.lista)}
        </div>
      ))}
    </div>
  );
}
window.CalendarHibridView = CalendarHibridView;

/* ── Plan strategic (nou) ── */
function PlanHibridView({ store, set }) {
  const U = window.PRP_UTIL;
  const azi = new Date(); azi.setHours(0, 0, 0, 0);
  const { cred, val } = hibridItemi(store);
  const goto = (view) => () => set((s) => ({ ...s, view }));

  if (cred.length === 0 && val.length === 0) {
    return (
      <div className="page">
        <div className="panel empty">
          <div className="empty-title">Planul strategic pornește din credințe și valori</div>
          <div className="small" style={{ maxWidth: 500 }}>
            Asumă-ți acțiuni din mecanismele credințelor și completează-ți rutinele valorilor
            în Codul meu - progresul lor se urmărește aici, pe orizonturi de 30 / 60 / 90 / 120 de zile.
          </div>
          <div className="row" style={{ gap: 8 }}>
            <button className="btn btn-primary btn-sm" onClick={goto("credinte")}>Credințele <U.IcArrow size={13} /></button>
            <button className="btn btn-ghost btn-sm" onClick={goto("valori")}>Valorile <U.IcArrow size={13} /></button>
          </div>
        </div>
      </div>
    );
  }

  const start = store.consola.dataStart ? new Date(store.consola.dataStart + "T00:00:00") : null;
  const startOk = !!start && !isNaN(start) && start <= azi;
  const ziProces = startOk ? Math.floor((azi - start) / 86400000) + 1 : null;
  const ziuaData = (n) => U.addDays(start, n - 1);
  const ORIZONT = store.planOrizont;

  const bifeInFereastra = (list, de, pana) => {
    let n = 0;
    list.forEach((e) => {
      Object.keys(store.log[e.key] || {}).forEach((pk) => {
        const d = U.pkToDate(pk);
        if (d && d >= de && d <= pana) n += 1;
      });
    });
    return n;
  };
  /* Data asumarii vine acum si de la valori (`adaugat`, scris de valori.jsx la completarea
     campului), deci ambele fluxuri se judeca la fel. Inainte, valorile se judecau de la PRIMA
     BIFA, iar o valoare asumata si niciodata bifata - exact cazul pe care rosul trebuie sa-l
     prinda - nu devenea rosie niciodata (audit 25.07). */

  const CREDINTE = U.CREDINTE;
  const credActive = (CREDINTE || []).filter((c) => cred.some((e) => e.slug === c.slug));
  const valActive = [];
  val.forEach((e) => {
    const g = valActive.find((x) => x.id === e.valoare.id);
    if (g) g.itemi.push(e); else valActive.push({ id: e.valoare.id, nume: e.valoare.nume, itemi: [e] });
  });

  const credRanduri = credActive.map((c) => {
    const ale = cred.filter((e) => e.slug === c.slug);
    const oa = ale.reduce((n, e) => n + (e.oa || 0), 0);
    const or = ale.reduce((n, e) => n + (e.orNr || 0), 0);
    const impliniri = startOk ? bifeInFereastra(ale, ziuaData(1), ziuaData(ORIZONT)) : null;
    return { nume: c.nume, itemi: ale, oa, or, rest: oa - or, impliniri };
  });
  const valRanduri = valActive.map((v) => ({
    nume: v.nume, itemi: v.itemi,
    impliniri: startOk ? bifeInFereastra(v.itemi, ziuaData(1), ziuaData(ORIZONT)) : null,
  }));

  const totCred = credRanduri.reduce((t, r) => ({ oa: t.oa + r.oa, or: t.or + r.or, imp: t.imp + (r.impliniri || 0) }), { oa: 0, or: 0, imp: 0 });
  const totValImp = valRanduri.reduce((n, r) => n + (r.impliniri || 0), 0);

  const nSapt = Math.ceil(ORIZONT / 7);
  const saptamani = startOk ? Array.from({ length: nSapt }, (_, i) => {
    const de = ziuaData(i * 7 + 1);
    const panaBruta = ziuaData((i + 1) * 7);
    const limita = ziuaData(ORIZONT);
    const pana = panaBruta < limita ? panaBruta : limita;
    return { nr: i + 1, de, pana, trecuta: pana < azi, viitoare: de > azi };
  }) : [];

  const [desfacute, setDesfacute] = React.useState([]);
  const toggleRand = (id) => setDesfacute(desfacute.includes(id) ? desfacute.filter((x) => x !== id) : [...desfacute, id]);

  /* Derulare orizontala COMUNA intre cele doua sectiuni: de la 90 de zile in sus tabelele nu mai
     incap in panou, iar daca fiecare deruleaza singur, saptamana S12 a credintelor ajunge langa
     S3 a valorilor - adica exact comparatia pe care alinierea trebuia s-o asigure. */
  const panouri = React.useRef([]);
  const sincronScroll = (i) => (ev) => {
    const alt = panouri.current[1 - i];
    const x = ev.currentTarget.scrollLeft;
    if (alt && Math.abs(alt.scrollLeft - x) > 1) alt.scrollLeft = x;
  };

  /* cele DOUA sectiuni ale planului (cerinta Marian 25.07): fiecare cu cifrele ei deasupra
     si cu benzile ei dedesubt. Latimea primei coloane e comuna, ca saptamanile sa fie aliniate
     intre sectiuni; e singura constanta de geometrie de care depinde citirea comparativa. */
  const LATIME_NUME = 268;
  const LATIME_SAPT = 34;   /* = .sapt-cell din index.html */
  const SPATIU = 3;         /* = border-spacing al .sapt-tbl */
  /* Latimea TABELULUI, calculata, nu lasata pe seama continutului: cu `width:auto`, tabelul se
     stramtoreaza cand depaseste panoul si comprima prima coloana ALTFEL in fiecare dintre cele
     doua sectiuni (masurat la auditul din 25.07: 268/268 la 30 de zile, dar 144/185 la 120,
     deci saptamanile nu mai cadeau una sub alta). Cu latime explicita, geometria e identica. */
  const latimeTabel = LATIME_NUME + saptamani.length * LATIME_SAPT + (saptamani.length + 2) * SPATIU;
  const sectiuni = [
    {
      tip: "cred", titlu: "Credințele mele", subtitlu: "Credințele în timp", randuri: credRanduri,
      statistici: [
        { val: (<React.Fragment><b className="pv-oa" style={{ color: "var(--m-navy)" }}>{totCred.oa}</b> / <b style={{ color: "var(--m-good)" }}>{totCred.or}</b></React.Fragment>),
          lbl: "obiective alocate / realizate" },
        ...(startOk ? [{ val: totCred.imp, lbl: "împliniri în orizont" }] : []),
      ],
    },
    {
      tip: "val", titlu: "Valorile mele", subtitlu: "Valorile în timp", randuri: valRanduri,
      statistici: [
        { val: val.length, lbl: "rutine și angajamente asumate" },
        ...(startOk ? [{ val: totValImp, lbl: "împliniri în orizont" }] : []),
      ],
    },
  ];

  /* celula agregata a unei benzi - regula echitabila: rosu doar pe o saptamana trecuta in
     care itemii erau deja asumati (credinte: campul adaugat; valori: de la prima bifa) */
  const celulaBanda = (list, s) => {
    if (s.viitoare) return <td key={s.nr} className="sapt-cell viitor" />;
    const active = list.filter((e) => {
      const ad = e.adaugat ? new Date(e.adaugat + "T00:00:00") : start;
      return ad && !isNaN(ad) && ad <= s.pana;
    });
    if (active.length === 0) return <td key={s.nr} className="sapt-cell viitor" />;
    const bife = bifeInFereastra(active, s.de, s.pana);
    const cls = s.trecuta ? (bife > 0 ? "ok" : "rosu") : (bife > 0 ? "ok curenta" : "curenta");
    return <td key={s.nr} className={"sapt-cell " + cls} title={`S${s.nr}: ${bife} ${bife === 1 ? "împlinire" : "împliniri"}`}>{bife > 0 ? bife : ""}</td>;
  };

  const banda = (r, tip) => {
    const id = tip + "|" + r.nume;
    const deschis = desfacute.includes(id);
    return (
      <React.Fragment key={id}>
        <tr className="sapt-rand-cred" onClick={() => toggleRand(id)}>
          <td className="sapt-cred">
            <span className={"sapt-chev" + (deschis ? " deschis" : "")}><U.IcChev size={13} /></span>
            {/* fara sufixul „· valoare": sectiunea in care sta banda spune deja de unde vine */}
            <span className="sapt-banda">{r.nume}</span>
          </td>
          {saptamani.map((s) => celulaBanda(r.itemi, s))}
        </tr>
        {deschis && r.itemi.map((e) => (
          <tr key={e.key}>
            <td className="sapt-act sub" title={e.text}>{e.text}</td>
            {saptamani.map((s) => {
              if (s.viitoare) return <td key={s.nr} className="sapt-cell viitor" />;
              const bife = bifeInFereastra([e], s.de, s.pana);
              const cls = s.trecuta ? (bife > 0 ? "ok" : "") : (bife > 0 ? "ok curenta" : "curenta");
              return <td key={s.nr} className={"sapt-cell " + cls}>{bife > 0 ? bife : ""}</td>;
            })}
          </tr>
        ))}
      </React.Fragment>
    );
  };

  return (
    <div className="page">
      <div className="row" style={{ marginBottom: 14, flexWrap: "wrap", gap: 8 }}>
        <p className="plan-info" style={{ margin: 0 }}>
          Ce fac în primele {ORIZONT} de zile{ziProces ? ` · ziua ${Math.min(ziProces, ORIZONT)} din ${ORIZONT}` : ""}
        </p>
        <div className="mod-tabs" role="tablist" aria-label="Orizontul planului" style={{ marginLeft: "auto" }}>
          {[30, 60, 90, 120].map((o) => (
            <button key={o} role="tab" aria-selected={ORIZONT === o}
              className={"mod-tab" + (ORIZONT === o ? " activ" : "")}
              onClick={() => set((s) => ({ ...s, planOrizont: o }))}>{o}</button>
          ))}
        </div>
      </div>

      {!startOk ? (
        <div className="panel empty">
          <div className="empty-title">Lipsește data de start a procesului</div>
          <div className="small" style={{ maxWidth: 440 }}>
            Săptămânile orizontului se calculează de la data de start - seteaz-o în consolă.
          </div>
          <button className="btn btn-primary btn-sm" onClick={goto("consola")}>Deschide consola <U.IcArrow size={13} /></button>
        </div>
      ) : (
        <React.Fragment>
          {sectiuni.map((sec, idxSec) => sec.randuri.length === 0 ? null : (
            <div key={sec.tip} style={{ marginTop: 4, marginBottom: 18 }}>
              <p className="sect-label" style={{ margin: "0 0 10px", fontSize: "var(--m-fs-2)" }}>{sec.titlu}</p>
              <div className="stat-row" style={{ marginBottom: 12 }}>
                {sec.statistici.map((s, i) => (
                  <div className="panel stat" key={i}>
                    <div className="stat-val">{s.val}</div>
                    <div className="stat-lbl">{s.lbl}</div>
                  </div>
                ))}
              </div>
              <div className="panel" style={{ overflowX: "auto" }}
                ref={(el) => { panouri.current[idxSec] = el; }} onScroll={sincronScroll(idxSec)}>
                <p className="sect-label">{sec.subtitlu} - săptămânile orizontului de {ORIZONT} de zile</p>
                {/* latime NUMITA pe prima coloana, identica in ambele tabele: altfel fiecare
                    tabel si-ar lua latimea din cel mai lung nume propriu si saptamanile S1..Sn
                    ale celor doua sectiuni n-ar mai cadea una sub alta */}
                <table className="sapt-tbl" style={{ tableLayout: "fixed", width: latimeTabel }}>
                  <thead>
                    <tr>
                      <th style={{ textAlign: "left", width: LATIME_NUME }} />
                      {saptamani.map((s) => (
                        <th key={s.nr} title={`${U.fmtISO(s.de)} - ${U.fmtISO(s.pana)}`} style={{ width: LATIME_SAPT }}
                          className={!s.trecuta && !s.viitoare ? "sapt-global" : undefined}>S{s.nr}</th>
                      ))}
                    </tr>
                  </thead>
                  <tbody>
                    {sec.randuri.map((r) => banda(r, sec.tip))}
                  </tbody>
                </table>
              </div>
            </div>
          ))}
          <p className="small muted" style={{ margin: "2px 0 0" }}>
            Verde = săptămână cu împliniri · roșu = săptămână trecută fără nicio împlinire (doar după asumare) ·
            contur = săptămâna curentă. Apasă pe un rând pentru detalierea pe acțiuni.
          </p>
        </React.Fragment>
      )}
    </div>
  );
}
window.PlanHibridView = PlanHibridView;
