// app.jsx — Ryoko Landing Page

const { useState, useEffect, useRef, useCallback, useMemo } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "palette": "navy",
  "accent": "#b08968",
  "showGreeting": true,
  "showCardTags": true,
  "heroVariant": "single",
  "cardStyle": "mixed"
}/*EDITMODE-END*/;

const PALETTES = {
  navy:    { name: "Navy & Camel",    sidebar: "#1a2942", sidebarInk: "#f4ede0", sidebarMuted: "rgba(244,237,224,.45)", bg: "#f4ede0", surface: "#fbf8f3", ink: "#2a2620", inkSoft: "rgba(42,38,32,.62)", rule: "rgba(42,38,32,.12)" },
  ink:     { name: "Ink & Bone",      sidebar: "#1a1a1a", sidebarInk: "#f1ede5", sidebarMuted: "rgba(241,237,229,.42)", bg: "#f1ede5", surface: "#faf7f1", ink: "#1a1a1a", inkSoft: "rgba(26,26,26,.62)", rule: "rgba(26,26,26,.12)" },
  forest:  { name: "Forest & Sand",   sidebar: "#22332b", sidebarInk: "#f0ebe0", sidebarMuted: "rgba(240,235,224,.45)", bg: "#ece5d6", surface: "#f7f2e6", ink: "#22332b", inkSoft: "rgba(34,51,43,.62)", rule: "rgba(34,51,43,.14)" },
  oxblood: { name: "Oxblood & Cream", sidebar: "#3a1a1a", sidebarInk: "#f4ebdc", sidebarMuted: "rgba(244,235,220,.45)", bg: "#f4ebdc", surface: "#faf3e6", ink: "#2a1414", inkSoft: "rgba(42,20,20,.62)", rule: "rgba(42,20,20,.14)" },
};

const TYPE = {
  display: '"Cormorant Garamond", "EB Garamond", Georgia, serif',
  body: '"Inter", system-ui, -apple-system, sans-serif',
  mono: '"JetBrains Mono", "Courier New", monospace',
};

// ── Top bar ───────────────────────────────────────────────────────────────


// ── Hero ──────────────────────────────────────────────────────────────────

function Hero({ featured, member, showGreeting }) {
  const [inView, setInView] = useState(false);
  useEffect(() => { const t = setTimeout(() => setInView(true), 80); return () => clearTimeout(t); }, []);
  return (
    <section className={`rk-hero ${inView ? "in" : ""}`} data-screen-label="Hero">
      <div className="rk-hero-img" style={{ backgroundImage: `url("${featured.image}")` }} />
      <div className="rk-hero-veil" />
      <div className="rk-hero-grid" />

      <div className="rk-hero-inner">
        {showGreeting && (
          <div className="rk-hero-greet">
            <div>
              <span className="rk-hero-greet-name">{member.greeting}, {member.name}.</span>
            </div>
            <div className="rk-hero-greet-meta">
              {member.signals.map((s, i) => (
                <span key={i}>{s.k}<b>{s.v}</b></span>
              ))}
            </div>
          </div>
        )}
        <div className="rk-hero-content">
          <div className="rk-hero-eyebrow">
            <span className="rule" />
            {featured.eyebrow}
          </div>
          <h1 className="rk-hero-title">{featured.title}</h1>
          <div className="rk-hero-kicker">{featured.kicker}</div>
          <p className="rk-hero-sub">{featured.subtitle}</p>
          <div className="rk-hero-tags">
            {featured.tags.map((t, i) => (
              <span key={i} className={`rk-hero-tag ${i === 0 ? "accent" : ""}`}>{t}</span>
            ))}
          </div>
          <div className="rk-hero-meta">
            {featured.meta.map((m, i) => (
              <div key={i}>
                <span className="k">{m.k}</span>
                <span className="v">{m.v}</span>
              </div>
            ))}
          </div>
          <div className="rk-hero-cta">
            <a className="rk-cta" href="Landing.html">
              {featured.primary} <span className="rk-cta-arrow">→</span>
            </a>
            <button className="rk-cta ghost">{featured.secondary}</button>
          </div>
        </div>
      </div>

      <div className="rk-hero-credit">{featured.imageCredit}</div>
      <div className="rk-hero-scroll-cue">
        <span className="line" />
        Scroll to explore
      </div>
    </section>
  );
}

// ── Card ──────────────────────────────────────────────────────────────────

function Card({ item, layout, showTag, onTrace }) {
  const isWide = layout === "wide";
  // Build the enriched href · enrichSubject returns null for Place
  // links without a real placeId.  When it returns null we render the
  // card as an article (no anchor) — we don't bluff a destination.
  const href = window.RkLinks
    ? (item.href && item.href.includes("Activity.html")
        ? window.RkLinks.enrichActivity(item)
        : window.RkLinks.enrichSubject(item))
    : item.href;
  const Wrap = href ? "a" : "article";
  const wrapProps = href ? { href } : {};
  const onTraceClick = (e) => {
    e.preventDefault(); e.stopPropagation();
    onTrace(item);
  };
  return (
    <Wrap className={`rk-card ${layout}`} data-screen-label={item.title} {...wrapProps}>
      <div className="rk-card-img">
        <img src={item.image} alt="" loading="lazy" />
        <div className="rk-card-img-shade" />
        {showTag && item.tag && (
          <span className={`rk-card-tag ${item.eyebrow && item.eyebrow.toLowerCase().includes("held") ? "accent" : ""}`}>
            {item.tag}
          </span>
        )}
        {item.progress !== undefined && (
          <div className="rk-card-progress">
            <div className="rk-card-progress-fill" style={{ width: `${item.progress * 100}%` }} />
          </div>
        )}
      </div>
      <div className="rk-card-open">→</div>
      <div className="rk-card-body">
        {item.eyebrow && <div className="rk-card-eyebrow">{item.eyebrow}</div>}
        <h3 className="rk-card-title">{item.title}</h3>
        {item.sub && <p className="rk-card-sub">{item.sub}</p>}
        {item.why && (
          <div className="rk-why">
            {item.curator && window.RyokoCuratorByline ? (
              <window.RyokoCuratorByline id={(item.curator && item.curator.id) || item.curator} verb="Held by" className="rk-why-byline" />
            ) : (
              <span className="rk-why-label">Why this · for you</span>
            )}
            {item.why}
            {item.signals && item.signals.length > 0 && (
              <button className="rk-why-trace" onClick={onTraceClick}>↗ Trace · {item.signals.length} signals</button>
            )}
          </div>
        )}
        {(typeof item.unlocks === "number" ? item.unlocks : (item.unlocks && item.unlocks.length)) > 0 && (
          <div className="rk-card-unlocks-pip" title="Doors only the desk can open · tap the card to see them">
            <span className="rk-card-unlocks-mark" aria-hidden="true">✦</span>
            <span>{typeof item.unlocks === "number" ? item.unlocks : item.unlocks.length} doors held against your file</span>
          </div>
        )}
        {isWide && (item.meta || item.tag) && (
          <div className="rk-card-foot">
            {item.meta && (
              <span className="rk-card-foot-meta">
                {item.progress !== undefined ? <b>{item.meta}</b> : item.meta}
              </span>
            )}
            {item.tag && !showTag && <span className="rk-card-foot-ref">{item.tag}</span>}
            {item.tag && showTag && item.progress === undefined && (
              <span className="rk-card-foot-ref">↗</span>
            )}
          </div>
        )}
      </div>
    </Wrap>
  );
}

// ── Why-this Trace panel ─────────────────────────────────────────────────

function TracePanel({ card, signalTypes, onClose }) {
  if (!card) return null;
  const sorted = [...card.signals].sort((a, b) => b.strength - a.strength);
  let weight = 0, accum = 0;
  sorted.forEach((s, i) => {
    const w = 1 / Math.sqrt(i + 1);
    accum += s.strength * w;
    weight += w;
  });
  const score = Math.round((accum / Math.max(weight, 0.0001)) * 100);
  const topStrong = sorted.filter(s => s.strength >= 0.7).length;
  const curator = card.curator && window.RyokoCuratorResolve
    ? window.RyokoCuratorResolve(card.curator)
    : null;
  const curatorNote = card.curator && card.curator.note;
  return (
    <div className="rk-trace-veil" onClick={(e) => e.target === e.currentTarget && onClose()}>
      <div className="rk-trace" data-screen-label="Why this trace">
        <div className="rk-trace-eyebrow">Why we surfaced this · for you</div>
        <h2 className="rk-trace-title">{card.title}</h2>
        {curator && window.RyokoCuratorCard && (
          <window.RyokoCuratorCard
            curator={curator}
            size="trace"
            hand={curatorNote}
            verb="held this for you"
          />
        )}
        <p className="rk-trace-summary">
          {card.signals.length} signal sources stacked · {topStrong} above 70% confidence · weighted score <b>{score}/100</b>. Below, each one with its evidence.
        </p>
        {card.signals.map((s, i) => {
          const meta = signalTypes[s.type];
          if (!meta) return null;
          return (
            <div key={i} className="rk-trace-signal" style={{ borderLeftColor: meta.color }}>
              <div className="rk-trace-signal-head">
                <span className="rk-trace-signal-label" style={{ color: meta.color }}>
                  {meta.label}
                </span>
                <span className="rk-trace-signal-strength">{Math.round(s.strength * 100)}%</span>
              </div>
              <div className="rk-trace-signal-bar">
                <div className="rk-trace-signal-bar-fill"
                     style={{ width: `${s.strength * 100}%`, background: meta.color }} />
              </div>
              <p className="rk-trace-signal-why">{s.why}</p>
            </div>
          );
        })}
        <div className="rk-trace-refine">
          <button className="rk-trace-refine-btn">↑ More like this</button>
          <button className="rk-trace-refine-btn">↓ Less like this</button>
          <button className="rk-trace-refine-btn">⌀ Mute this venue</button>
          <button className="rk-trace-refine-btn">↗ Tune signal weights</button>
        </div>
        <div className="rk-trace-foot">
          <a className="rk-trace-link" href="/profile">Tune your signals →</a>
          <button className="rk-trace-close" onClick={onClose}>Close</button>
        </div>
      </div>
    </div>
  );
}

// ── Tray ──────────────────────────────────────────────────────────────────

function Tray({ tray, cardStyle, showTags, onTrace }) {
  const railRef = useRef(null);
  const [canPrev, setCanPrev] = useState(false);
  const [canNext, setCanNext] = useState(true);

  const updateArrows = useCallback(() => {
    const el = railRef.current;
    if (!el) return;
    setCanPrev(el.scrollLeft > 8);
    setCanNext(el.scrollLeft + el.clientWidth < el.scrollWidth - 8);
  }, []);

  useEffect(() => {
    updateArrows();
    const el = railRef.current;
    if (!el) return;
    el.addEventListener("scroll", updateArrows, { passive: true });
    window.addEventListener("resize", updateArrows);
    return () => {
      el.removeEventListener("scroll", updateArrows);
      window.removeEventListener("resize", updateArrows);
    };
  }, [updateArrows]);

  const scroll = (dir) => {
    const el = railRef.current;
    if (!el) return;
    const cardWidth = el.querySelector(".rk-card")?.clientWidth || 320;
    el.scrollBy({ left: dir * (cardWidth + 16) * 2, behavior: "smooth" });
  };

  // Resolve layout
  let layout = tray.layout;
  if (cardStyle === "wide") layout = "wide";
  if (cardStyle === "tall") layout = "tall";
  // mixed = use tray's own layout

  return (
    <section className={`rk-tray rk-tray-${tray.id}`} data-screen-label={`Tray · ${tray.title}`}>
      <div className="rk-tray-head">
        <div className="rk-tray-head-left">
          <div className="rk-tray-eyebrow">{tray.eyebrow}</div>
          <h2 className="rk-tray-title">{tray.title}</h2>
          {tray.note && <p className="rk-tray-note">{tray.note}</p>}
        </div>
        <div className="rk-tray-controls">
          <button className="rk-tray-link">See all</button>
          <button className="rk-tray-arrow" onClick={() => scroll(-1)} disabled={!canPrev} aria-label="Previous">‹</button>
          <button className="rk-tray-arrow" onClick={() => scroll(1)} disabled={!canNext} aria-label="Next">›</button>
        </div>
      </div>
      <div className="rk-tray-rail-wrap">
        {canPrev && <div className="rk-tray-rail-fade left" />}
        {canNext && <div className="rk-tray-rail-fade right" />}
        <div ref={railRef} className="rk-tray-rail">
          {tray.items.map((item) => (
            <Card key={item.id} item={item} layout={layout} showTag={showTags} onTrace={onTrace} />
          ))}
        </div>
      </div>
    </section>
  );
}

// ── Footer ────────────────────────────────────────────────────────────────

function Foot() {
  return (
    <footer className="rk-landing-foot" data-screen-label="Footer">
      <div className="rk-foot-brand-block">
        <div className="mark">RYOKO</div>
        <div className="tag">Lifestyle &amp; Bespoke Travel</div>
        <p>"A travel atelier for the few who travel often, and the fewer who travel well."</p>
      </div>
      <div className="rk-foot-col">
        <h5>The House</h5>
        <ul>
          <li><a href="Landing.html">Discover</a></li>
          <li><a href="Landing.html">The atlas</a></li>
          <li><a href="Landing.html">The Atelier</a></li>
          <li>Press</li>
        </ul>
      </div>
      <div className="rk-foot-col">
        <h5>Members</h5>
        <ul>
          <li>The Network</li>
          <li>Private events</li>
          <li>Insider Picks</li>
          <li>Refer a friend</li>
        </ul>
      </div>
      <div className="rk-foot-col">
        <h5>Contact</h5>
        <ul>
          <li>concierge@ryoko.travel</li>
          <li>+44 20 4525 1100</li>
          <li>London · Lisbon · Tokyo</li>
        </ul>
      </div>
      <div className="rk-landing-foot-line">
        <span>© 2026 Ryoko Atelier</span>
        <span>Held in confidence · SOC 2 · GDPR</span>
      </div>
    </footer>
  );
}

// ── On the atelier's desk · always-visible strip ─────────────────────────
// The architect-side counterpart to "for you" trays.  Shows what Camille
// (and the regional desks) are actively holding for you, right now.
// Mirror image of the concierge ON_DESK panel.

function OnDeskStrip({ items }) {
  if (!items || items.length === 0) return null;
  const toneFor = (k) =>
    k === "drafting"   ? "pending"
    : k === "confirming" ? "approved"
    : k === "watching" ? "warning"
    : "neutral";
  const labelFor = (k) =>
    k === "drafting"   ? "Drafting"
    : k === "confirming" ? "Confirming"
    : k === "watching" ? "Watching"
    : k;
  return (
    <section className="rk-ondesk" data-screen-label="On the atelier's desk">
      <window.RkSectionHead
        eyebrow="On the atelier's desk · for you, right now"
        num="·"
        title={<>What we're <em>holding for you</em>.</>}
        sub="The team's open work on your file. Always visible — your architects don't disappear between trips."
      />
      <div className="rk-ondesk-list">
        {items.map(i => (
          <article key={i.id} className={`rk-ondesk-item kind-${i.kind}`}>
            <header className="rk-ondesk-head">
              <window.RkChip tone={toneFor(i.kind)} size="sm">{labelFor(i.kind)}</window.RkChip>
              <span className="rk-ondesk-since">since {i.since}</span>
            </header>
            <h3 className="rk-ondesk-headline">{i.headline}</h3>
            <p className="rk-ondesk-detail">{i.detail}</p>
            <footer className="rk-ondesk-foot">
              <span className="rk-ondesk-arch">
                <span className="rk-ondesk-arch-av">{i.architectAvatar}</span>
                <span><b>{i.architect}</b> on point</span>
              </span>
              <a className="rk-ondesk-trip" href={i.tripHref}>→ {i.trip}</a>
            </footer>
          </article>
        ))}
      </div>
    </section>
  );
}

// ── App ───────────────────────────────────────────────────────────────────

function App() {
  const { MEMBER, FEATURED, TRAYS, SIGNAL_TYPES, FORYOU_BY_PRINCIPAL, ON_DESK } = window.RYOKO_LANDING;
  const [tweaks, setTweak] = window.useTweaks(TWEAK_DEFAULTS);

  const [scrolled, setScrolled] = useState(false);
  const [traceCard, setTraceCard] = useState(null);
  const scrollerRef = useRef(null);

  // Active principal · used to scope the Continue tray
  const [activePrincipal, setActivePrincipal] = useState(
    typeof window !== "undefined" && window.RyokoPrincipal ? window.RyokoPrincipal.current() : "ea"
  );
  useEffect(() => {
    if (!window.RyokoPrincipal) return;
    const unsub = window.RyokoPrincipal.subscribe(setActivePrincipal);
    setActivePrincipal(window.RyokoPrincipal.current());
    return unsub;
  }, []);
  const isFounder = !window.RyokoPrincipal || window.RyokoPrincipal.isFounder();
  const principalRecord = window.RyokoPrincipal ? window.RyokoPrincipal.currentRecord() : null;

  // Trip → principals lookup · mirrors the household cross matrix so the
  // Continue tray can scope properly. Items not in this map fall through
  // to the predicate's default (founder yes, trial-tier blocklist).
  const TRIP_PRINCIPALS = {
    lis: ["ea", "ma"],
    ky:  ["ea", "ma"],
    alf: ["ea", "ma"],
    asp: ["ea", "ma", "ba", "ha"],
    par: ["ma"],
    mex: ["ea", "ma", "ba"],
    isk: ["ea", "ha"],
    ngs: ["ea", "ma", "ba", "ha"],
    bht: ["ea", "ba"],
    "tok-bea": ["ba"],
  };

  // Filter the Continue tray to trips visible for the active principal,
  // and re-theme the For You tray to the active principal's stack when
  // a per-principal stack exists.
  const principalForYou = FORYOU_BY_PRINCIPAL && FORYOU_BY_PRINCIPAL[activePrincipal];
  const scopedTrays = TRAYS.map(tray => {
    if (tray.id === "foryou" && principalForYou) {
      return {
        ...tray,
        eyebrow: principalForYou.eyebrow || tray.eyebrow,
        title: principalForYou.title || tray.title,
        note: principalForYou.note || tray.note,
        items: principalForYou.items,
      };
    }
    if (tray.id === "continue") {
      return {
        ...tray,
        items: tray.items.filter(item =>
          !window.RyokoPrincipal ||
          window.RyokoPrincipal.canViewTrip({ id: item.id, principals: TRIP_PRINCIPALS[item.id] })
        ),
      };
    }
    return tray;
  });

  useEffect(() => {
    const el = scrollerRef.current;
    if (!el) return;
    const onScroll = () => setScrolled(el.scrollTop > 80);
    el.addEventListener("scroll", onScroll, { passive: true });
    return () => el.removeEventListener("scroll", onScroll);
  }, []);

  const palette = PALETTES[tweaks.palette] || PALETTES.navy;
  const accent = tweaks.accent || palette.sidebarInk;

  const cssVars = {
    "--rk-sidebar":     palette.sidebar,
    "--rk-sidebar-ink": palette.sidebarInk,
    "--rk-sidebar-muted": palette.sidebarMuted,
    "--rk-bg":          palette.bg,
    "--rk-surface":     palette.surface,
    "--rk-ink":         palette.ink,
    "--rk-ink-soft":    palette.inkSoft,
    "--rk-rule":        palette.rule,
    "--rk-accent":      accent,
    "--rk-display":     TYPE.display,
    "--rk-body":        TYPE.body,
    "--rk-mono":        TYPE.mono,
  };

  return (
    <div className="rk-landing" style={cssVars}>
      <window.RyokoTopBar active="discover" member={MEMBER} scrolled={scrolled} />
      <div ref={scrollerRef} className="rk-landing-scroll">
        <Hero featured={FEATURED} member={MEMBER} showGreeting={tweaks.showGreeting} />
        <OnDeskStrip items={ON_DESK} />
        <div className="rk-trays">
          {scopedTrays.map((tray, i) => (
            <React.Fragment key={tray.id}>
              {i > 0 && (
                <div className="rk-ornament" aria-hidden="true">
                  <span className="rk-ornament-line" />
                  <span className="rk-ornament-mark three" />
                  <span className="rk-ornament-line" />
                </div>
              )}
              <Tray tray={tray} cardStyle={tweaks.cardStyle} showTags={tweaks.showCardTags} onTrace={setTraceCard} />
            </React.Fragment>
          ))}
        </div>
        <Foot />
      </div>

      {traceCard && (
        <TracePanel card={traceCard} signalTypes={SIGNAL_TYPES} onClose={() => setTraceCard(null)} />
      )}

      <window.TweaksPanel title="Tweaks">
        <window.TweakSection label="Palette" />
        <window.TweakSelect label="Scheme" value={tweaks.palette}
          options={Object.entries(PALETTES).map(([k, v]) => ({ value: k, label: v.name }))}
          onChange={(v) => setTweak("palette", v)} />
        <window.TweakColor label="Accent" value={tweaks.accent}
          onChange={(v) => setTweak("accent", v)} />

        <window.TweakSection label="Hero" />
        <window.TweakToggle label="Member greeting" value={tweaks.showGreeting}
          onChange={(v) => setTweak("showGreeting", v)} />

        <window.TweakSection label="Cards" />
        <window.TweakRadio label="Style" value={tweaks.cardStyle}
          options={["mixed","wide","tall"]}
          onChange={(v) => setTweak("cardStyle", v)} />
        <window.TweakToggle label="Personalisation tags" value={tweaks.showCardTags}
          onChange={(v) => setTweak("showCardTags", v)} />
      </window.TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
