/* ============================================================
   Public site — the 3-window domain page + menu.
   ============================================================ */
const { useState: uS, useEffect: uE, useRef: uR, useMemo: uM } = React;

/* ------- tiny i18n (chrome only; content English in phase 1) ------- */
const T = {
  en: { about:"About", contact:"Contact me", feedback:"Give feedback", networks:"Networks",
        apps:"Apps", navigation:"Navigation", menu:"Menu", home:"Home", scroll:"scroll",
        readMore:"Read", watch:"Watch", featured:"Featured", topPicks:"Top picks",
        sectionsLabel:"Sections", domainsLabel:"Domains", filterDomain:"Domain", filterTag:"Tag",
        all:"All", results:"results", backToSite:"Back to site", aboutMe:"About me", secret:"the secrets of Cannelle — locked", secretOpen:"the secrets of Cannelle — open" },
  fr: { about:"À propos", contact:"Me contacter", feedback:"Donner un feedback", networks:"Réseaux",
        apps:"Apps", navigation:"Navigation", menu:"Menu", home:"Accueil", scroll:"défiler",
        readMore:"Lire", watch:"Voir", featured:"À la une", topPicks:"Sélection",
        sectionsLabel:"Sections", domainsLabel:"Domaines", filterDomain:"Domaine", filterTag:"Tag",
        all:"Tous", results:"résultats", backToSite:"Retour au site", aboutMe:"À propos de moi", secret:"les secrets de Cannelle — verrouillé", secretOpen:"les secrets de Cannelle — ouvert" },
};
function useT() { const { lang } = useNav(); return T[lang] || T.en; }

/* ===================== BRAND (flips to the site QR code on click) ===================== */
function BrandFlip({ dark }) {
  const { db } = useStore();
  const p = db.profile;
  const [flipped, setFlipped] = uS(false);
  const tRef = uR(null);
  const clear = () => { if (tRef.current) { clearTimeout(tRef.current); tRef.current = null; } };
  const toggle = (e) => {
    if (e) { e.preventDefault(); e.stopPropagation(); }
    setFlipped(f => {
      const nf = !f; clear();
      if (nf) tRef.current = setTimeout(() => setFlipped(false), 5000);
      return nf;
    });
  };
  uE(() => () => clear(), []);
  const qr = window.seedQrDataUrl ? window.seedQrDataUrl() : "";   // front end auto-generates from the site address
  return (
    <div className={"brandflip" + (flipped ? " flipped" : "")} onClick={toggle} role="button" tabIndex={0} title="Scan to open on your phone"
      onKeyDown={e => { if (e.key === "Enter" || e.key === " ") toggle(e); }}>
      <div className="bf-inner">
        <span className="brand bf-front">
          <span className="b1" style={dark ? { color: "#faf7f1" } : null}>{p.first}</span>
          <span className="b2">{p.last}<span className="dot">.</span></span>
        </span>
        <span className="bf-back">
          {qr ? <img src={qr} alt="QR code — scan to open this site on your phone" /> : null}
          <span className="bf-cap">scan me</span>
        </span>
      </div>
    </div>
  );
}

/* ===================== TOP BAR ===================== */
function TopBar({ domain, onMenu, dark }) {
  const { db } = useStore();
  const { go, lang, setLang } = useNav();
  const p = db.profile;
  return (
    <header className={"topbar" + (dark ? " on-dark" : "")}>
      <div className="brand-wrap">
        <BrandFlip dark={dark} />
      </div>
      <div className="topright">
        <div className="lang">
          <button className={lang === "en" ? "on" : ""} onClick={() => setLang("en")}>EN</button>
          <button className={lang === "fr" ? "on" : ""} onClick={() => setLang("fr")}>FR</button>
        </div>
        <button className="menubtn" onClick={onMenu} aria-label="menu"><Icon name="menu" size={24} /></button>
        <IdentityControl dark={dark} />
      </div>
    </header>
  );
}

/* ===================== DOMAIN SWITCHER ===================== */
function DomainSwitcher({ domain }) {
  const { db } = useStore();
  const { go } = useNav();
  const others = db.domains.filter(d => d.display);
  return (
    <div className="dom-switch">
      {others.map((d, i) => (
        <React.Fragment key={d.id}>
          <span className={"ds" + (d.id === domain.id ? " cur" : "")} onClick={() => go(d.route)}>{d.label}</span>
        </React.Fragment>
      ))}
    </div>
  );
}

/* ===================== MENU PANEL ===================== */
function MenuPanel({ open, onClose, domain, goWindow, editMode }) {
  const { db } = useStore();
  const { go, lang, session, isAdmin, role, edit, reviewMode } = useNav();
  const t = useT();
  const fr = lang === "fr";
  const sections = db.sections.filter(s => s.inMenu);
  // admin in edit mode also sees hidden (not-in-menu) sections so they can manage them
  const menuSections = editMode && isAdmin ? db.sections : sections;
  const allGrants = userGrants(db, session);
  const sharedGrants = allGrants.filter(g => g.kind !== "section");      // pages / content shared
  const grantedSections = allGrants.filter(g => g.kind === "section");   // hidden sections this user may access
  const collabMode = (role === "editor" && edit) || !!reviewMode;
  const status = reviewMode ? "review" : "draft";
  // counts mirror the section-list pool exactly (per piece, any language, private gated)
  const cnt = (kind) => db.content.filter(c => c.type === kind && itemActionable(db, c, status, session, role, false)).length;
  const otherCnt = (db.otherPages || []).filter(p => p.kind !== "secret" && itemActionable(db, p, status, session, role, false)).length;
  // logged-in read-only collaborators get a single "shared pages" entry + any hidden sections shared with them
  const showShared = !!session && !isAdmin && !collabMode && (sharedGrants.length > 0 || grantedSections.length > 0);
  const close = (fn) => { onClose(); setTimeout(fn, 240); };
  const iconFor = (k) => window.iconForKind(k);
  const grantIcon = (k) => k === "video" ? "play" : k === "secret" ? "lock" : k === "section" ? "compass" : "edit";
  const sharedLabel = fr ? "Contenus partagés" : "Shared content";
  return (
    <>
      <div className={"menu-scrim" + (open ? " show" : "")} style={{ pointerEvents: open ? "auto" : "none" }} onClick={onClose} />
      <nav className={"menu-panel" + (open ? " show" : "")}>
        <div className="row between" style={{ marginBottom: 18 }}>
          <span className="mono dim" style={{ fontSize: 12, letterSpacing: ".14em" }}>{t.menu.toUpperCase()}</span>
          <button className="menubtn" onClick={onClose} style={{ width: 40, height: 40 }}><Icon name="close" size={20} /></button>
        </div>
        <div className="col grow hide-scroll" style={{ overflowY: "auto" }}>
          {collabMode ? (
            <>
              {/* Home — back to the workspace dashboard reached via "Enter … mode" */}
              <a className="menu-link" onClick={() => close(() => go(db.profile.defaultRoute))}>
                <span className="mi"><Icon name="home" size={20} /></span><span>{t.home}</span>
                <span className="grow" /><span className="ml-go"><Icon name="right" size={20} /></span>
              </a>
              {/* restricted menu — only the sections this collaborator can act on, + Other */}
              <div className="menu-cap mode-cap"><Icon name={reviewMode ? "check" : "edit"} size={13} /> {reviewMode ? (fr ? "À relire" : "To review") : (fr ? "À éditer" : "To edit")}</div>
              {db.sections.filter(s => sectionEditorialAccess(db, s, session, role)).map(s => (
                <a className="menu-link mode-hot" key={s.id} onClick={() => close(() => go(s.route))}>
                  <span className="mi">{s.logo ? <img src={s.logo} alt="" style={{ width: 24, height: 24, borderRadius: 6, objectFit: "cover" }} /> : <Icon name={iconFor(s.kind)} size={20} />}</span>
                  <span>{secLabel(s, lang)}</span>
                  <span className="grow" />
                  {cnt(s.kind) > 0 && <span className="ml-count">{cnt(s.kind)}</span>}
                  <span className="ml-go"><Icon name="right" size={20} /></span>
                </a>
              ))}
              {(role === "admin" || (db.otherPages || []).some(p => p.kind !== "secret" && hasEditorialAccess(db, p, session, role))) && (
                <a className="menu-link mode-hot" onClick={() => close(() => go("/other"))}>
                  <span className="mi"><Icon name="compass" size={20} /></span><span>Other</span>
                  <span className="grow" />
                  {otherCnt > 0 && <span className="ml-count">{otherCnt}</span>}
                  <span className="ml-go"><Icon name="right" size={20} /></span>
                </a>
              )}
            </>
          ) : (
            <>
              <a className="menu-link" onClick={() => close(() => goWindow(1))}><span className="mi"><Icon name="home" size={20} /></span><span>{t.home}</span><span className="grow" /><span className="ml-go"><Icon name="right" size={20} /></span></a>
              <a className="menu-link" onClick={() => close(() => go("/about"))}><span className="mi"><Icon name="user" size={20} /></span><span>{t.aboutMe}</span><span className="grow" /><span className="ml-go"><Icon name="right" size={20} /></span></a>
              {menuSections.map(s => (
                <a className="menu-link" key={s.id} onClick={() => close(() => go(s.route))}>
                  <span className="mi">{s.logo ? <img src={s.logo} alt="" style={{ width: 24, height: 24, borderRadius: 6, objectFit: "cover" }} /> : <Icon name={iconFor(s.kind)} size={20} />}</span><span>{secLabel(s, lang)}</span>
                  <span className="grow" />
                  {editMode && !s.inMenu && <span className="ml-edit-tag">{fr ? "caché" : "hidden"}</span>}
                  <span className="ml-go"><Icon name="right" size={20} /></span>
                </a>
              ))}
              <a className="menu-link" onClick={() => close(() => goWindow(3))}><span className="mi"><Icon name="compass" size={20} /></span><span>{t.apps}</span><span className="grow" /><span className="ml-go"><Icon name="right" size={20} /></span></a>
              <a className="menu-link" onClick={() => close(() => go("/contact"))}><span className="mi"><Icon name="msg" size={20} /></span><span>{t.contact}</span><span className="grow" /><span className="ml-go"><Icon name="right" size={20} /></span></a>
              <a className="menu-link" onClick={() => close(() => go("/feedback"))}><span className="mi"><Icon name="star" size={20} /></span><span>{t.feedback}</span><span className="grow" /><span className="ml-go"><Icon name="right" size={20} /></span></a>
              {editMode && (
                <a className="menu-link other-edit" onClick={() => close(() => go("/other"))}>
                  <span className="mi"><Icon name="compass" size={20} /></span><span>Other</span>
                  <span className="grow" /><span className="ml-edit-tag">edit</span><span className="ml-go"><Icon name="right" size={20} /></span>
                </a>
              )}
              {showShared && (
                <>
                  <div className="menu-cap shared-cap">{sharedLabel}</div>
                  {/* shared content — shown as the SECTIONS it lives in, like the public section tabs.
                      Opening one lands on "<Section> — shared" with only the shared pieces. */}
                  {(() => {
                    const groups = [];
                    sharedGrants.forEach(g => {
                      const key = g.sectionId || "other";
                      let grp = groups.find(x => x.key === key);
                      if (!grp) {
                        const sec = db.sections.find(s => s.id === g.sectionId);
                        grp = { key, label: sec ? secLabel(sec, lang) : (g.sectionLabel || "Other"), logo: sec ? sec.logo : (g.logo || null), kind: sec ? sec.kind : g.sectionKind, items: [] };
                        groups.push(grp);
                      }
                      grp.items.push(g);
                    });
                    return groups.map(grp => (
                      <a key={grp.key} className="menu-link shared-sub" onClick={() => close(() => go("/shared/" + grp.key))}>
                        <span className="mi">{grp.logo ? <img src={grp.logo} alt="" style={{ width: 24, height: 24, borderRadius: 6, objectFit: "cover" }} /> : <Icon name={iconFor(grp.kind || "blog")} size={20} />}</span>
                        <span>{grp.label}</span>
                        <span className="grow" />
                        <span className="ml-count">{grp.items.length}</span>
                        <span className="ml-go"><Icon name="right" size={20} /></span>
                      </a>
                    ));
                  })()}
                  {/* whole hidden sections granted (view-only) */}
                  {grantedSections.map(g => {
                    const sec = db.sections.find(x => x.id === g.id);
                    return (
                      <a key={g.id} className="menu-link" onClick={() => close(() => go(g.route))}>
                        <span className="mi">{g.logo ? <img src={g.logo} alt="" style={{ width: 24, height: 24, borderRadius: 6, objectFit: "cover" }} /> : <Icon name={window.iconForKind(sec ? sec.kind : "blog")} size={20} />}</span>
                        <span>{sec ? secLabel(sec, lang) : g.label}</span>
                        <span className="grow" />{!sec?.inMenu && <span className="ml-edit-tag">{fr ? "section" : "section"}</span>}<span className="ml-go"><Icon name="right" size={20} /></span>
                      </a>
                    );
                  })}
                </>
              )}
            </>
          )}
        </div>
        <div className="row gap-8 wrap" style={{ paddingTop: 14, borderTop: "1px dashed var(--line)" }}>
          {domainSocials(db, domain.id).map(s => <SocialChip key={s.id} s={s} size={36} />)}
        </div>
      </nav>
    </>
  );
}

/* ===================== WINDOW 1 — HERO ===================== */
function HeroWindow({ domain, goWindow }) {
  const { db, update } = useStore();
  const { go, lang } = useNav();
  const t = useT();
  const dl = domLangFields(domain, lang);
  const solved = !!db.secretUnlocked;
  const solve = () => update(d => { d.secretUnlocked = true; });
  const socials = domainSocials(db, domain.id);
  const sections = db.domains.find(d => d.id === domain.id)?.sections || [];
  const secObjs = sections.map(id => db.sections.find(s => s.id === id)).filter(Boolean);
  const iconFor = (k) => k === "video" ? "play" : k === "podcast" ? "msg" : "edit";

  const bubbles = [
    ...((domain.extraPages || []).map(pid => db.otherPages.find(p => p.id === pid)).filter(Boolean).map(p => ({
      key: p.id, label: p.label, tag: "on request", icon: "user", onClick: () => go(p.route)
    }))),
    ...secObjs.map(s => ({ key: s.id, label: s.label, tag: domain.id, icon: iconFor(s.kind), onClick: () => go(`${s.route}?domain=${domain.id}`) })),
    { key: "apps", label: t.apps, tag: "tools", icon: "compass", onClick: () => goWindow(3) },
  ];

  return (
    <section className="window">
      <div className="win-inner" style={{ paddingTop: "clamp(82px,12vh,118px)" }}>
        {/* title block */}
        <div className="editable" data-edit-label="title + route">
          <h1 className="hero-title">
            <span className="lead">{db.profile.tagline} …</span>
            <span className="accent">{dl.title}</span>
          </h1>
          <div style={{ marginTop: 16 }} className="editable" data-edit-label="domains">
            <DomainSwitcher domain={domain} />
          </div>
        </div>

        {/* stage: art (socials live in the persistent left rail) — transparent, integrates with the page */}
        <div className="hero-stage solo">
          <div className="hero-art">
            <div className="hero-artframe editable" data-edit-label="illustration">
              {domain.appEmbed
                ? <iframe className="hero-embed" src={domain.appEmbed} title="illustration" loading="lazy" />
                : <DomainArt kind={domain.art} accent="var(--accent)" />}
            </div>
          </div>
        </div>

        {/* link bubbles */}
        <div className="bubbles editable" data-edit-label="sections" style={{ marginBottom: 10 }}>
          {bubbles.map(b => (
            <div className="bubble" key={b.key} onClick={b.onClick}>
              <span className="ring"><Icon name={b.icon} size={24} /></span>
              <span className="bl">{b.label}</span>
              {b.tag && <span className="bt">{b.tag}</span>}
            </div>
          ))}
        </div>
        <div className="scroll-cue" onClick={() => goWindow(2)} style={{ position: "static", margin: "4px auto 18px", cursor: "pointer" }}>
          <span>{t.scroll}</span><Icon name="down" size={18} />
        </div>
      </div>
    </section>
  );
}

/* ===================== content card ===================== */
function ContentCard({ item }) {
  const { db } = useStore();
  const { go, lang } = useNav();
  const t = useT();
  const cl = wholeLang(item, publicDisplayLang(item, lang));
  const sec = db.sections.find(s => s.kind === item.type);
  const open = () => go(`${sec ? sec.route : (item.type === "video" ? "/videos" : "/blog")}/${item.ref}`);
  const arts = domainsOf(item).map(id => { const d = db.domains.find(x => x.id === id); return { kind: (d && d.art) || (id === "human" ? "human" : "network"), accent: accentFor(id).accent }; });
  return (
    <article className="ccard" onClick={open}>
      <div className="thumb">
        <LangChips has={(l) => langExists(item, l) && publishedInLang(item, l)} className="on-thumb" />
        {item.cover
          ? <img src={item.cover} alt="" style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
          : <div className="thumb-art" style={{ width: "100%", height: "100%", background: arts.length > 1 ? "linear-gradient(135deg,#eaf0fb,#ffffff 72%)" : accentFor(primaryDomain(item)).wash }}><MultiDomainArt arts={arts} animate={false} /></div>}
        {item.type !== "blog" && item.type !== "document" && <span style={{ position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", color: "var(--accent)" }}><Icon name={window.iconForKind(item.type)} size={42} /></span>}
      </div>
      <div className="cc-body">
        <div className="cc-title">{cl.title}</div>
        <div className="cc-desc">{cl.description}</div>
        <div className="cc-meta">
          <Stars n={item.rating} size={12} />
          <span>·</span><span>{fmtDate(item.createdAt)}</span>
          {item.tags.slice(0, 2).map(tg => <span key={tg}>#{tg}</span>)}
        </div>
      </div>
    </article>
  );
}

/* ===================== WINDOW 2 — ABOUT ===================== */
function AboutWindow({ domain, goWindow }) {
  const { db } = useStore();
  const { lang } = useNav();
  const t = useT();
  const dl = domLangFields(domain, lang);
  const items = uM(() => {
    const picks = (domain.featured && domain.featured[lang]) || [];
    if (picks.length) {
      return picks.map(id => db.content.find(c => c.id === id)).filter(Boolean)
        .filter(c => publishedInLang(c, lang) && langExists(c, lang)).slice(0, 4);
    }
    // default: the 4 best-rated pieces that are published AND really exist in this language
    return db.content
      .filter(c => domainsOf(c).includes(domain.id) && publishedInLang(c, lang) && langExists(c, lang))
      .sort((a, b) => (b.rating || 0) - (a.rating || 0))
      .slice(0, 4);
  }, [db.content, domain, lang]);

  return (
    <section className="window">
      <div className="win-inner">
        <div className="about-grid" style={{ flex: 1 }}>
          {/* left: quote + top picks */}
          <div className="editable" data-edit-label="quote + top picks">
            <div className="row gap-8" style={{ marginBottom: 10 }}>
              <span className="mono dim" style={{ letterSpacing: ".14em", fontSize: 13 }}>{t.about.toUpperCase()}</span>
              <hr className="grow sep" />
            </div>
            <p className="quote">{dl.quote}</p>
            <div className="row between" style={{ marginTop: 30, marginBottom: 6 }}>
              <span className="mono dim" style={{ fontSize: 12, letterSpacing: ".1em" }}>{t.topPicks.toUpperCase()} · {domain.label}</span>
            </div>
            <div className="top-grid">
              {items.map(it => <ContentCard key={it.id} item={it} />)}
            </div>
          </div>

          {/* right: presentation */}
          <div className="about-pres editable" data-edit-label="presentation">
            <h3><span className="nm">{db.profile.first} {db.profile.last},</span><br />{dl.presTitle}</h3>
            <RichText text={dl.presBody} style={{ marginTop: 16, fontSize: 16, color: "var(--ink-soft)", lineHeight: 1.6 }} />
            <div style={{ marginTop: 20 }}>
              {domain.presImage
                ? <img src={domain.presImage} alt="" style={{ width: "100%", aspectRatio: "16/9", objectFit: "cover", borderRadius: "var(--r-md)", border: "1.5px solid var(--accent)" }} />
                : <Placeholder label="optional portrait / photo / short video" tag="optional" style={{ width: "100%", aspectRatio: "16/9" }} />}
            </div>
          </div>
        </div>
        <div className="scroll-cue" onClick={() => goWindow(3)} style={{ position: "static", margin: "0 auto 22px", cursor: "pointer" }}>
          <Icon name="down" size={18} />
        </div>
      </div>
    </section>
  );
}

/* ===================== PERSISTENT SOCIAL RAIL ===================== */
function InlineSocials({ domain }) {
  const { db } = useStore();
  const socials = domainSocials(db, domain.id);
  return (
    <div className="inline-socials">
      <span className="is-wave" aria-hidden="true" />
      {socials.map(s => <SocialChip key={s.id} s={s} size={34} />)}
      <span className="mono dim" style={{ fontSize: 11, letterSpacing: ".08em" }}>@{db.profile.first.toLowerCase()}richter</span>
    </div>
  );
}

function SocialRail({ domain, opacity = 1 }) {
  const { db } = useStore();
  const { go } = useNav();
  const socials = domainSocials(db, domain.id);
  const hidden = opacity <= 0.02;
  return (
    <div className="social-rail" style={{ opacity, pointerEvents: hidden ? "none" : "auto" }} aria-hidden={hidden}>
      <span className="rail-line" aria-hidden="true" />
      <div className="rail-chips">
        {socials.map((s, i) => (
          <span key={s.id} className="rc" style={{ "--i": i }}><SocialChip s={s} size={38} /></span>
        ))}
      </div>
      <span className="rail-label">@{db.profile.first.toLowerCase()}richter</span>
    </div>
  );
}

/* ===================== WINDOW 3 — FOOTER (shared) ===================== */
function FooterWindow({ domain, footerActive }) {
  const { db } = useStore();
  const { go, edit, auth, lang } = useNav();
  const editMode = !!(edit && auth);
  const t = useT();
  const socials = domainSocials(db, domain.id);
  const navDomains = db.domains.filter(d => d.display);
  const navSections = db.sections.filter(s => s.inMenu);
  const apps = db.apps.filter(a => editMode || a.published !== false);
  return (
    <section className="window win3">
      <div className="win-inner">
        <div className="foot-grid">
          {/* APPS */}
          <div className="foot-col">
            <h4>{t.apps}</h4>
            <div className="apps-grid">
              {apps.map(a => (
                <a className="app-tile" key={a.id} href={a.link} target="_blank" rel="noreferrer" title={a.desc || a.name} style={editMode && a.published === false ? { opacity: .5 } : null}>
                  <span className="logo" style={{ background: a.logo ? "#fff" : a.color, padding: a.logo ? 0 : undefined }}>
                    {a.logo ? <img src={a.logo} alt="" style={{ width: "100%", height: "100%", objectFit: "cover", borderRadius: "inherit" }} /> : a.glyph}
                  </span>
                  <span className="an">{a.name}{editMode && a.published === false ? " · draft" : ""}</span>
                </a>
              ))}
            </div>
          </div>
          {/* NAVIGATION */}
          <div className="foot-col nav-raise">
            <h4>{t.navigation}</h4>
            <div className="nav-plan">
              <div className="grp">{t.domainsLabel}</div>
              {navDomains.map(d => (
                <a key={d.id} onClick={() => go(d.route)} style={{ cursor: "pointer" }}><Icon name="arrow" size={16} />{d.label}</a>
              ))}
              <div className="grp">{t.sectionsLabel}</div>
              <a className="section-link" onClick={() => go("/about")} style={{ cursor: "pointer" }}>{t.aboutMe}</a>
              {navSections.map(s => (
                <a key={s.id} className="section-link" onClick={() => go(s.route)} style={{ cursor: "pointer" }}>{s.label}</a>
              ))}
            </div>
          </div>
          {/* CONTACT / FEEDBACK / SOCIALS */}
          <div className="foot-col">
            <h4>&nbsp;</h4>
            <div className="col gap-12" style={{ alignItems: "flex-start" }}>
              <button className="btn accent" onClick={() => go("/contact")}><Icon name="msg" size={18} />{t.contact}</button>
              <button className="btn" style={{ background: "transparent", color: "var(--paper)", borderColor: "var(--paper)", boxShadow: "2px 2px 0 rgba(255,255,255,.4)" }} onClick={() => go("/feedback")}><Icon name="star" size={18} />{t.feedback}</button>
            </div>
            <h4 style={{ marginTop: 28 }}>{t.networks}</h4>
            <div className={"row gap-8 wrap net-pop" + (footerActive ? " in" : "")}>
              {socials.map((s, i) => <span key={s.id} className="np" style={{ "--i": i }}><SocialChip s={s} size={40} /></span>)}
            </div>
          </div>
        </div>
        <div className="legal-bar">
          <a onClick={() => go("/legal/mentions")} style={{ cursor: "pointer" }}>{objLangFields((db.legal && db.legal.mentions) || {}, lang, ["title"]).title || "Legal notice"}</a>
          <a onClick={() => go("/legal/copyright")} style={{ cursor: "pointer" }}>© {new Date().getFullYear()} {db.profile.first} {db.profile.last}</a>
          <a onClick={() => go("/legal/cgu")} style={{ cursor: "pointer" }}>{objLangFields((db.legal && db.legal.cgu) || {}, lang, ["title"]).title || "Terms (CGU)"}</a>
          <span className="grow" />
          <span className="mono">cannellerichter.fr</span>
        </div>
      </div>
    </section>
  );
}

/* ===================== DOMAIN PAGE (composes everything) ===================== */
function DomainPage({ domain, isHome }) {
  const { route, edit, auth } = useNav();
  const edx = (window.useEdit ? window.useEdit() : {});
  const [menuOpen, setMenuOpen] = uS(false);
  const [activeWin, setActiveWin] = uS(1);
  const [atFooter, setAtFooter] = uS(false);
  const [railOpacity, setRailOpacity] = uS(1);
  const scrollerRef = uR(null);
  const programmaticRef = uR(false);
  const editMode = !!(edit && auth);

  const goWindow = (n) => {
    setActiveWin(n);
    programmaticRef.current = true;
    setTimeout(() => { programmaticRef.current = false; }, 700);
    const el = scrollerRef.current;
    if (!el) return;
    // scroll to the ACTUAL top of window n — windows can be taller than the
    // viewport (e.g. a long About window), so multiples of clientHeight land
    // mid-window. Use the child's real offset instead.
    const target = el.children[n - 1];
    const top = target ? target.offsetTop : (n - 1) * el.clientHeight;
    el.scrollTo({ top, behavior: "smooth" });
  };

  uE(() => {
    const el = scrollerRef.current;
    if (!el) return;
    const onScroll = () => {
      if (programmaticRef.current) return;
      const h = el.clientHeight; const st = el.scrollTop;
      const kids = [el.children[0], el.children[1], el.children[2]];
      // active window = last whose top has passed the viewport's middle
      let n = 1;
      kids.forEach((k, i) => { if (k && st >= k.offsetTop - h / 2) n = i + 1; });
      setActiveWin(Math.min(3, Math.max(1, n)));
      const win3 = kids[2];
      // brand flips to white only once window 3 (the dark footer) reaches the top
      setAtFooter(win3 ? st >= win3.offsetTop - 26 : st >= 2 * h - 26);
      // social rail fades only as window 3 approaches
      const fadeStart = win3 ? win3.offsetTop - h * 0.9 : h * 1.45;
      setRailOpacity(1 - Math.min(1, Math.max(0, (st - fadeStart) / (h * 0.5))));
    };
    el.addEventListener("scroll", onScroll, { passive: true });
    return () => el.removeEventListener("scroll", onScroll);
  }, []);

  // arriving from another page with ?win=N (e.g. the Menu's "Apps" link) — jump straight to that window
  uE(() => {
    const w = parseInt(route.query && route.query.win, 10);
    if (!(w >= 1 && w <= 3)) return;
    const t = setTimeout(() => goWindow(w), 130);
    return () => clearTimeout(t);
  }, [route.query && route.query.win, route.path]);

  // publish window state to the global edit chrome (so its panel can edit this page)
  uE(() => {
    if (!edx.setReg) return;
    if (editMode) edx.setReg({ domain, isHome, activeWin, goWindow });
    return () => { if (edx.setReg) edx.setReg(null); };
  }, [editMode, domain.id, isHome, activeWin]);

  // accent variables for this domain
  const acc = accentFor(domain.id);
  const styleVars = { "--accent": acc.accent, "--accent-ink": acc.ink, "--accent-wash": acc.wash };

  return (
    <div style={styleVars} className={editMode ? "edit-mode" : ""}>
      <div style={{ position: "relative" }}>
        <TopBar domain={domain} onMenu={() => setMenuOpen(true)} dark={atFooter} />
        <MenuPanel open={menuOpen} onClose={() => setMenuOpen(false)} domain={domain} goWindow={goWindow} editMode={editMode} />
        <SocialRail domain={domain} opacity={railOpacity} />
        <div className="windows hide-scroll" ref={scrollerRef} style={{ height: editMode ? "calc(100vh - var(--ebar-h))" : "100vh" }}>
          <HeroWindow domain={domain} goWindow={goWindow} />
          <AboutWindow domain={domain} goWindow={goWindow} />
          <FooterWindow domain={domain} footerActive={atFooter} />
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { TopBar, DomainSwitcher, MenuPanel, HeroWindow, AboutWindow, FooterWindow, DomainPage, ContentCard, useT, SocialRail, InlineSocials, BrandFlip });
