// components.jsx — Ryoko · Shared primitives + link enrichers
//
// ── window.RkLinks ───────────────────────────────────────────────
// enrichSubject(item) and enrichActivity(item) take a card-shaped
// object ({ id, title, sub, image, eyebrow, tag, href }) and return
// an enriched href that carries the card's content as query params.
// This lets the destination page (Place.html / Activity.html) render
// *that subject* even when the id doesn't match a rich data record.
//
// All cards in the app must use these instead of `item.href` directly.
//
// ── window.Rk* components ────────────────────────────────────────
//
// Six React components every feature page should reach for first.
// Goal: kill the 24× reinvented chips / cards / section heads / meta
// rows.  These map onto patterns already in styles.css + semantics.css;
// they are not a new design system, they are the contract.
//
// Loading order (in HTML <head>):
//   styles.css → semantics.css → components.css → feature.css
// Loading order (in HTML <body>):
//   ... → components.jsx → states.jsx → feature app.jsx
//
// Every component exposes itself onto window so feature pages can use
// it without imports under Babel-standalone.
//
// ── RkSectionHead ──────────────────────────────────────────────────
//   <window.RkSectionHead
//      eyebrow="Vault → action wiring"
//      num="·"
//      title={<>The vault, <em>earning its keep</em>.</>}
//      sub="A document only earns its place in the vault if it's working on a brief."
//   />
//
// ── RkChip ─────────────────────────────────────────────────────────
//   <window.RkChip tone="blocking">Action required</window.RkChip>
//   <window.RkChip tone="approved">Confirmed</window.RkChip>
//   <window.RkChip tone="discretion" size="sm">Off-book</window.RkChip>
//   <window.RkChip tone="neutral" as="button" onClick={...}>Filter</window.RkChip>
//
//   tone: blocking | approved | warning | pending | discretion | accent | neutral
//   size: sm | md (default md)
//   as:   span | button (default span)
//
// ── RkMetaRow ──────────────────────────────────────────────────────
//   <window.RkMetaRow pairs={[
//      { k: "Issued",  v: "12 Mar 2024" },
//      { k: "Expiry",  v: "14 Aug 2027",  tone: "warning" },
//      { k: "Updated", v: "3 days ago" },
//   ]} />
//
//   layout: stacked (default) | inline | dense
//
// ── RkButton ───────────────────────────────────────────────────────
//   <window.RkButton variant="primary">Brief a new trip</window.RkButton>
//   <window.RkButton variant="ghost" href="...">See all</window.RkButton>
//   <window.RkButton variant="destructive">Decline</window.RkButton>
//   <window.RkButton variant="discreet">Held off-book</window.RkButton>
//
//   variant: primary | ghost | destructive | discreet | quiet
//   size:    sm | md (default md)
//
// ── RkCard ─────────────────────────────────────────────────────────
//   <window.RkCard tone="urgent" hoverable>... contents ...</window.RkCard>
//
//   tone:      none (default) | sensitive | urgent | approved | discretion
//   hoverable: true → adds the lift+shadow polish on hover
//   as:        article (default) | a | div
//
// ── RkModal ────────────────────────────────────────────────────────
//   <window.RkModal open={open} onClose={() => setOpen(false)} title="Confirm">
//      ... contents ...
//   </window.RkModal>
//
//   onClose fires on veil click and Escape.

(function () {
  const { useEffect } = React;

  // ── RkLinks ──────────────────────────────────────────────────────
  // Enriches card hrefs with the card's own content so destination
  // pages can show real content instead of a generic placeholder.
  // Build the enriched href for a card.
  //   · If item.placeId is set, that is the canonical id (resolves to a
  //     real venue file).  Card-side ids like "fy-saved-etxebarri" are
  //     for tracking only, not routing.
  //   · For Place.html targets without a real placeId, return null —
  //     callers MUST drop the link wrapper.  We never bluff a venue.
  //   · For Activity.html / Location.html / Trips.html targets, the
  //     subjects.jsx store handles the lookup itself, so the card id is
  //     fine as the routing id.
  function enrichSubject(item) {
    if (!item || !item.href) return null;
    const isPlaceLink = /\/place\/Place\.html\b/.test(item.href);
    if (isPlaceLink) {
      // The only way a Place link is valid is with a known placeId.
      if (!item.placeId) return null;
      const sep = item.href.includes("?") ? "&" : "?";
      return `${item.href}${sep}id=${encodeURIComponent(item.placeId)}`;
    }
    // For non-Place targets · pass the card's own id + content so the
    // destination page can render it.
    const sep = item.href.includes("?") ? "&" : "?";
    const hasIdInHref = /[?&]id=/.test(item.href);
    const extras = new URLSearchParams();
    if (item.id && !hasIdInHref) extras.set("id", item.id);
    if (item.title)              extras.set("name", item.title);
    if (item.sub)                extras.set("sub", item.sub);
    if (item.image)              extras.set("image", item.image);
    if (item.eyebrow)            extras.set("eyebrow", item.eyebrow);
    if (item.tag)                extras.set("ctx", item.tag);
    const q = extras.toString();
    if (!q) return item.href;
    return `${item.href}${sep}${q}`;
  }
  function enrichActivity(item) {
    return enrichSubject(item);
  }
  if (typeof window !== "undefined") {
    window.RkLinks = { enrichSubject, enrichActivity };
  }

  // ── RkSectionHead ────────────────────────────────────────────────
  function RkSectionHead({ eyebrow, num, title, sub, align = "left", children }) {
    return (
      <header className={`rk-sh rk-sh-${align}`} data-screen-label="Section head">
        {eyebrow && (
          <div className="rk-sh-eyebrow">
            {num && <span className="rk-sh-num">{num}</span>}
            {eyebrow}
          </div>
        )}
        {title && <h2 className="rk-sh-title">{title}</h2>}
        {sub && <p className="rk-sh-sub">{sub}</p>}
        {children}
      </header>
    );
  }

  // ── RkChip ───────────────────────────────────────────────────────
  function RkChip({
    tone = "neutral",
    size = "md",
    as = "span",
    children,
    onClick,
    title,
    className = "",
    ...rest
  }) {
    const Tag = as;
    const cls = `rk-chip rk-chip-${tone} rk-chip-${size} ${className}`.trim();
    if (as === "button") {
      return (
        <button className={cls} onClick={onClick} title={title} {...rest}>
          {children}
        </button>
      );
    }
    return <Tag className={cls} title={title} {...rest}>{children}</Tag>;
  }

  // ── RkMetaRow ────────────────────────────────────────────────────
  // pairs: [{ k, v, tone, mono }]
  // tone on a pair colours the value (blocking, warning, approved, accent).
  function RkMetaRow({ pairs = [], layout = "stacked", className = "" }) {
    return (
      <dl className={`rk-meta rk-meta-${layout} ${className}`.trim()}>
        {pairs.map((p, i) => (
          <React.Fragment key={i}>
            <dt className="rk-meta-k">{p.k}</dt>
            <dd className={`rk-meta-v ${p.tone ? `tone-${p.tone}` : ""} ${p.mono ? "mono" : ""}`}>
              {p.v}
            </dd>
          </React.Fragment>
        ))}
      </dl>
    );
  }

  // ── RkButton ─────────────────────────────────────────────────────
  function RkButton({
    variant = "ghost",
    size = "md",
    href,
    onClick,
    children,
    disabled,
    arrow = false,
    title,
    className = "",
    ...rest
  }) {
    const cls = `rk-btn rk-btn-${variant} rk-btn-${size} ${arrow ? "has-arrow" : ""} ${className}`.trim();
    const inner = (
      <>
        <span className="rk-btn-label">{children}</span>
        {arrow && <span className="rk-btn-arrow">→</span>}
      </>
    );
    if (href && !disabled) {
      return (
        <a className={cls} href={href} onClick={onClick} title={title} {...rest}>
          {inner}
        </a>
      );
    }
    return (
      <button className={cls} onClick={onClick} disabled={disabled} title={title} {...rest}>
        {inner}
      </button>
    );
  }

  // ── RkCard ───────────────────────────────────────────────────────
  function RkCard({
    tone = "none",
    hoverable = false,
    as = "article",
    href,
    onClick,
    children,
    className = "",
    label,
    ...rest
  }) {
    const Tag = href ? "a" : as;
    const cls = `rk-c rk-c-tone-${tone} ${hoverable ? "rk-c-hoverable" : ""} ${className}`.trim();
    const tagProps = href ? { href, onClick } : { onClick };
    return (
      <Tag className={cls} data-screen-label={label} {...tagProps} {...rest}>
        {tone !== "none" && <span className="rk-c-rail" aria-hidden="true" />}
        {children}
      </Tag>
    );
  }

  // ── RkModal ──────────────────────────────────────────────────────
  function RkModal({ open, onClose, title, eyebrow, children, footer, size = "md" }) {
    useEffect(() => {
      if (!open) return;
      const onKey = (e) => { if (e.key === "Escape") onClose(); };
      document.addEventListener("keydown", onKey);
      const prev = document.body.style.overflow;
      document.body.style.overflow = "hidden";
      return () => {
        document.removeEventListener("keydown", onKey);
        document.body.style.overflow = prev;
      };
    }, [open, onClose]);

    if (!open) return null;
    return (
      <div className="rk-modal-veil"
           onClick={(e) => e.target === e.currentTarget && onClose()}
           data-screen-label="Modal">
        <div className={`rk-modal rk-modal-${size}`} role="dialog" aria-modal="true">
          {(eyebrow || title) && (
            <header className="rk-modal-head">
              {eyebrow && <div className="rk-modal-eyebrow">{eyebrow}</div>}
              {title && <h2 className="rk-modal-title">{title}</h2>}
              <button className="rk-modal-close" onClick={onClose} aria-label="Close">✕</button>
            </header>
          )}
          <div className="rk-modal-body">{children}</div>
          {footer && <footer className="rk-modal-foot">{footer}</footer>}
        </div>
      </div>
    );
  }

  // ── RkArchitectOnly ─────────────────────────────────────────────
  // The moat: opportunities at a venue that aren't available to the
  // general public — held inventory, off-menu wines, named after-hours
  // access, first-refusal seats, signed gestures.  Quietly exclusive,
  // always specific (named persons, exact times, real lead times).
  //
  //   <window.RkArchitectOnly
  //     opportunities={[
  //       { kind: "after-hours", title: "Pena Palace · pre-opening tour",
  //         detail: "Joana Albuquerque opens at 07:15 — breakfast in the queen's salon at 08:30.",
  //         lead: "8 weeks", via: "Joana Albuquerque · estate director" },
  //       { kind: "held",
  //         title: "Two corner suites held against your file each spring",
  //         detail: "First refusal · west light · twin baths. Released to general pool 90 days out.",
  //         lead: "Annual standing", via: "Camille · since 2022" },
  //     ]}
  //   />
  //
  // kind drives the small icon + tone:
  //   off-menu | after-hours | private | introduction | held |
  //   first-refusal | no-list | signed
  function RkArchitectOnly({ opportunities = [], eyebrow = "Doors only the architect can open", title }) {
    if (!opportunities || opportunities.length === 0) return null;
    const labelFor = (k) => ({
      "off-menu":      "Off-menu",
      "after-hours":   "After hours",
      "private":       "Private",
      "introduction":  "Introduction",
      "held":          "Held inventory",
      "first-refusal": "First refusal",
      "no-list":       "Off the list",
      "signed":        "House gesture",
    })[k] || "Held by the desk";
    return (
      <section className="rk-aon" data-screen-label="Architect-only opportunities">
        <header className="rk-aon-head">
          <span className="rk-aon-eyebrow">{eyebrow}</span>
          <h2 className="rk-aon-title">
            {title || <>The {opportunities.length} doors <em>only the desk can open</em>.</>}
          </h2>
          <p className="rk-aon-sub">
            None of these are bookable directly.  Each is held against your
            file by name, with a specific person on point and a specific
            lead-time.  The architect calls; the venue answers.
          </p>
        </header>
        <ul className="rk-aon-list">
          {opportunities.map((o, i) => (
            <li key={i} className={`rk-aon-row kind-${o.kind || "private"}`}>
              <span className="rk-aon-kind">{labelFor(o.kind)}</span>
              <div className="rk-aon-body">
                <div className="rk-aon-title-row">{o.title}</div>
                {o.detail && <p className="rk-aon-detail">{o.detail}</p>}
                <div className="rk-aon-meta">
                  {o.via  && <span className="rk-aon-via"><b>via</b> {o.via}</span>}
                  {o.lead && <span className="rk-aon-lead"><b>lead</b> {o.lead}</span>}
                  {o.cost && <span className="rk-aon-cost"><b>cost</b> {o.cost}</span>}
                </div>
              </div>
            </li>
          ))}
        </ul>
      </section>
    );
  }

  // ── RkVenueExtras ────────────────────────────────────────────────
  // Four small panels of venue intelligence that sit alongside the
  // architectOnly band — what the architect deliberately doesn't
  // recommend (restraint), the architect's single opinionated pick,
  // what travels home (memorabilia), and what the venue itself gives
  // back (philanthropy).
  //
  //   <window.RkVenueExtras
  //     restraint="..."
  //     architectPick="..."
  //     memorabilia="..."
  //     philanthropy="..."
  //   />
  //
  // All four props optional · component renders only the panels with content.
  function RkVenueExtras({ restraint, architectPick, memorabilia, philanthropy }) {
    if (!restraint && !architectPick && !memorabilia && !philanthropy) return null;
    return (
      <section className="rk-vex" data-screen-label="Venue extras">
        {architectPick && (
          <article className="rk-vex-card kind-pick">
            <header className="rk-vex-head">
              <span className="rk-vex-eyebrow">The architect's pick</span>
              <span className="rk-vex-mark">✦</span>
            </header>
            <p className="rk-vex-body">{architectPick}</p>
          </article>
        )}
        {restraint && (
          <article className="rk-vex-card kind-restraint">
            <header className="rk-vex-head">
              <span className="rk-vex-eyebrow">What we don't recommend, and why</span>
            </header>
            <p className="rk-vex-body">{restraint}</p>
          </article>
        )}
        {memorabilia && (
          <article className="rk-vex-card kind-memorabilia">
            <header className="rk-vex-head">
              <span className="rk-vex-eyebrow">What travels back with you</span>
            </header>
            <p className="rk-vex-body">{memorabilia}</p>
          </article>
        )}
        {philanthropy && (
          <article className="rk-vex-card kind-philanthropy">
            <header className="rk-vex-head">
              <span className="rk-vex-eyebrow">What the venue gives</span>
            </header>
            <p className="rk-vex-body">{philanthropy}</p>
          </article>
        )}
      </section>
    );
  }

  if (typeof window !== "undefined") {
    window.RkVenueExtras = RkVenueExtras;
    window.RkArchitectOnly = RkArchitectOnly;
    window.RkSectionHead = RkSectionHead;
    window.RkChip = RkChip;
    window.RkMetaRow = RkMetaRow;
    window.RkButton = RkButton;
    window.RkCard = RkCard;
    window.RkModal = RkModal;
    window.RkProvenance = RkProvenance;
    window.RkSurfacesIn = RkSurfacesIn;
    window.RkImage = RkImage;
    window.rkImg = rkImg;
  }

  // ── rkImg / RkImage ─────────────────────────────────────────────
  // Image provenance pattern.  An editorial product credits its
  // photography.  Every hero, card, and inline place image should be
  // an object — { src, alt, credit, date, caption } — not a bare URL.
  //
  // rkImg(url, opts) returns that object form, so legacy data callsites
  // can incrementally upgrade by wrapping their bare URL strings:
  //
  //   image: rkImg("https://...", {
  //     credit: "Takashi Nakamura",
  //     date:   "October 2025",
  //     caption:"The garden corridor at Tawaraya, an October morning.",
  //     alt:    "Cedar corridor lit from a paper screen",
  //   })
  //
  // <window.RkImage img={place.image} ratio="3/4" /> renders the picture
  // with a quiet credit line.  Backwards compatible: if `img` is a string
  // it still works — but no credit appears.
  function rkImg(src, opts = {}) {
    if (!src) return null;
    return {
      src,
      alt:     opts.alt     || "",
      credit:  opts.credit  || null,
      date:    opts.date    || null,
      caption: opts.caption || null,
    };
  }
  function RkImage({ img, ratio = "auto", className = "", showCredit = true, sizing = "cover" }) {
    if (!img) return null;
    const obj = typeof img === "string" ? { src: img, alt: "" } : img;
    return (
      <figure className={`rk-img ratio-${ratio.replace("/", "-")} ${className}`.trim()}
              data-screen-label="Image">
        <img src={obj.src} alt={obj.alt || ""} loading="lazy"
             style={{ objectFit: sizing }} />
        {showCredit && (obj.credit || obj.date || obj.caption) && (
          <figcaption className="rk-img-credit">
            {obj.caption && <span className="rk-img-cap">{obj.caption}</span>}
            <span className="rk-img-meta">
              {obj.credit && <span><i>Photograph</i> · {obj.credit}</span>}
              {obj.date   && <span>{obj.date}</span>}
            </span>
          </figcaption>
        )}
      </figure>
    );
  }

  // ── RkProvenance ─────────────────────────────────────────────────
  // Shows where a member-side artefact came from: architect's draft,
  // approval, or both.  Use on every surface that displays an output
  // of the atelier's work — approve rows, today events, spend lines,
  // vault docs.
  //
  //   <window.RkProvenance
  //      eyebrow="Origin · architect's draft"
  //      refCode="RYK-2026-LIS-0418 · Rev 4"
  //      by="Camille Okafor · today · 14:08"
  //      links={[
  //        { label: "Open in Composer", href: "..." },
  //        { label: "Approval ap2",     href: "..." },
  //      ]}
  //   />
  // NOTE: the reference-code prop is `refCode`, NOT `ref` — `ref` is reserved
  // by React and a string value crashes function components at render.
  function RkProvenance({ eyebrow = "How this got here", refCode, by, links = [], children }) {
    return (
      <div className="rk-prov" data-screen-label="Provenance">
        <span className="rk-prov-rail" aria-hidden="true" />
        <div className="rk-prov-stack">
          <span className="rk-prov-eyebrow">{eyebrow}</span>
          {refCode && <span className="rk-prov-ref">{refCode}</span>}
          {by && <span className="rk-prov-by">{by}</span>}
          {children}
          {links.length > 0 && (
            <div className="rk-prov-links">
              {links.map((l, i) => (
                <a key={i} className="rk-prov-link" href={l.href}>
                  {l.label} →
                </a>
              ))}
            </div>
          )}
        </div>
      </div>
    );
  }

  // ── RkSurfacesIn ─────────────────────────────────────────────────
  // The architect-side mirror of RkProvenance.  Shows where this draft
  // currently appears on the member side: open approvals + day-of
  // executions + held vault items.
  //
  //   <window.RkSurfacesIn surfaces={[
  //      { kind: "approval", ref: "ap2", label: "...", when: "...", href: "..." },
  //      { kind: "today",    ref: "d6e6", label: "...", when: "...", href: "..." },
  //   ]} />
  function RkSurfacesIn({ surfaces = [], eyebrow = "Where this surfaces · member-side, right now" }) {
    if (!surfaces || surfaces.length === 0) return null;
    const labelFor = (k) =>
      k === "approval" ? "Approval"
      : k === "today"  ? "On the day"
      : k === "vault"  ? "Vault"
      : k === "spend"  ? "Spend"
      : k;
    return (
      <section className="rk-surfaces" data-screen-label="Surfaces in">
        <header className="rk-surfaces-head">
          <span className="rk-surfaces-eyebrow">↗ {eyebrow}</span>
          <span className="rk-surfaces-count"><b>{surfaces.length}</b> surfaces</span>
        </header>
        <ul className="rk-surfaces-list">
          {surfaces.map((s, i) => (
            <li key={i} className={`rk-surfaces-row kind-${s.kind}`}>
              <span className="rk-surfaces-kind">{labelFor(s.kind)}</span>
              <span className="rk-surfaces-ref">{s.ref}</span>
              <span className="rk-surfaces-label">{s.label}</span>
              <span className="rk-surfaces-when">{s.when}</span>
              <a className="rk-surfaces-link" href={s.href}>Open →</a>
            </li>
          ))}
        </ul>
      </section>
    );
  }
})();
