// Inlinr Editor — building blocks for the chrome (top bar, rails, inspector, etc.)
// Exposes everything to window so app.jsx can pick them up.

const { useState, useEffect, useRef } = React;

/* ------------------------------------------------------------------
   Icon — thin SVG wrapper. All Lucide-style; 1.5 stroke; currentColor.
   ------------------------------------------------------------------ */
const Icon = ({ name, size = 16, className = "", style }) => {
  const paths = {
    logo: <><rect x="4" y="6" width="14" height="2.4" rx="1.2"/><rect x="4" y="11" width="9" height="2.4" rx="1.2" opacity="0.55"/><rect x="4" y="16" width="12" height="2.4" rx="1.2" opacity="0.30"/><circle cx="20" cy="7.2" r="1.5" fill="#3b6bff" stroke="none"/></>,
    mail: <><rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/></>,
    grid: <><path d="M3 12h18M3 6h18M3 18h18"/></>,
    layers: <><path d="m12 2 9 5-9 5-9-5 9-5z"/><path d="m3 12 9 5 9-5"/><path d="m3 17 9 5 9-5"/></>,
    globe: <><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/></>,
    type: <><path d="M4 7V5h16v2"/><path d="M9 20h6M12 5v15"/></>,
    image: <><rect x="3" y="5" width="18" height="14" rx="2"/><circle cx="9" cy="11" r="1.6"/><path d="m21 16-5-5-9 9"/></>,
    button: <><rect x="3" y="8" width="18" height="8" rx="4"/></>,
    divider: <><path d="M3 12h18"/></>,
    columns: <><rect x="3" y="4" width="7" height="16" rx="1"/><rect x="14" y="4" width="7" height="16" rx="1"/></>,
    spacer: <><path d="M5 6V4h14v2M5 18v2h14v-2M12 6v12"/></>,
    bold: <><path d="M7 4h7a4 4 0 0 1 0 8H7zM7 12h8a4 4 0 0 1 0 8H7z"/></>,
    italic: <><path d="M19 4h-9M14 20H5M15 4 9 20"/></>,
    underline: <><path d="M6 4v8a6 6 0 0 0 12 0V4M4 20h16"/></>,
    link: <><path d="M10 13a5 5 0 0 0 7 0l3-3a5 5 0 0 0-7-7l-1 1"/><path d="M14 11a5 5 0 0 0-7 0l-3 3a5 5 0 0 0 7 7l1-1"/></>,
    alignLeft:   <><path d="M3 6h14M3 12h10M3 18h14"/></>,
    alignCenter: <><path d="M5 6h14M7 12h10M5 18h14"/></>,
    alignRight:  <><path d="M7 6h14M11 12h10M7 18h14"/></>,
    alignJustify: <><path d="M3 6h18M3 12h18M3 18h18"/></>,
    moon: <><path d="M21 13A9 9 0 0 1 11 3a7 7 0 1 0 10 10z"/></>,
    sun: <><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></>,
    chevronDown: <><path d="m6 9 6 6 6-6"/></>,
    chevronRight: <><path d="m9 6 6 6-6 6"/></>,
    chevronLeft: <><path d="m15 6-6 6 6 6"/></>,
    plus: <><path d="M12 5v14M5 12h14"/></>,
    search: <><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></>,
    trash: <><path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></>,
    duplicate: <><rect x="8" y="8" width="13" height="13" rx="2"/><path d="M16 8V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h2"/></>,
    settings: <><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 0 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 0 1-4 0v-.1A1.7 1.7 0 0 0 9 19.4a1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 0 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 0 1 0-4h.1A1.7 1.7 0 0 0 4.6 9a1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 0 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 0 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 0 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9c.4.6 1 1 1.5 1H21a2 2 0 0 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z"/></>,
    download: <><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/></>,
    eye: <><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></>,
    code: <><path d="m16 18 6-6-6-6M8 6l-6 6 6 6"/></>,
    history: <><path d="M3 12a9 9 0 1 0 3-6.7L3 8"/><path d="M3 3v5h5"/><path d="M12 7v5l3 2"/></>,
    bell: <><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/></>,
    undo: <><path d="M3 7v6h6"/><path d="M3 13a9 9 0 1 0 3-7"/></>,
    redo: <><path d="M21 7v6h-6"/><path d="M21 13a9 9 0 1 1-3-7"/></>,
    move: <><path d="M5 9l-3 3 3 3M9 5l3-3 3 3M15 19l-3 3-3-3M19 9l3 3-3 3M2 12h20M12 2v20"/></>,
    check: <><path d="m5 12 5 5L20 7"/></>,
    x: <><path d="M18 6 6 18M6 6l12 12"/></>,
    info: <><circle cx="12" cy="12" r="9"/><path d="M12 8h.01M11 12h1v4h1"/></>,
    warn: <><path d="M10.3 3.7 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.7a2 2 0 0 0-3.4 0z"/><path d="M12 9v4M12 17h.01"/></>,
    chip: <><rect x="6" y="6" width="12" height="12" rx="1.5"/><path d="M9 1v4M15 1v4M9 19v4M15 19v4M1 9h4M1 15h4M19 9h4M19 15h4"/></>,
    star: <><path d="m12 2 3 7 7 1-5 5 1 7-6-3-6 3 1-7-5-5 7-1z"/></>,
    file: <><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></>,
    monitor: <><rect x="3" y="4" width="18" height="13" rx="2"/><path d="M8 21h8M12 17v4"/></>,
    tablet: <><rect x="5" y="3" width="14" height="18" rx="2.5"/><path d="M11 18h2"/></>,
    mobile: <><rect x="7" y="2" width="10" height="20" rx="2.5"/><path d="M11 18h2"/></>,
    panelLeft: <><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M9 4v16"/></>,
    panelRight: <><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M15 4v16"/></>,
    dropper: <><path d="m2 22 1-5 11-11 4 4-11 11-5 1z"/><path d="m13 7 4 4M16 4l4 4-2 2"/></>,
    sparkle: <><path d="M12 3l1.8 4.5L18 9.3l-4.2 1.8L12 15.6l-1.8-4.5L6 9.3l4.2-1.8L12 3z"/><path d="M19 14l.9 2.1L22 17l-2.1.9L19 20l-.9-2.1L16 17l2.1-.9L19 14z"/></>,
    viewList: <><path d="M8 6h13M8 12h13M8 18h13"/><circle cx="4" cy="6" r="1"/><circle cx="4" cy="12" r="1"/><circle cx="4" cy="18" r="1"/></>,
    viewGrid: <><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></>,
    users: <><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/></>,
    shield: <><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></>,
    lock: <><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></>,
    arrowRight: <><path d="M5 12h14M13 5l7 7-7 7"/></>,
    plus2: <><path d="M12 5v14M5 12h14"/></>,
    moreH: <><circle cx="5" cy="12" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="19" cy="12" r="1.5"/></>,
  };
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
      stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"
      className={className} style={style}>
      {paths[name] || null}
    </svg>
  );
};

/* ------------------------------------------------------------------
   Primitives
   ------------------------------------------------------------------ */
const Button = ({ variant = "secondary", size = "md", iconLeft, iconRight, children, onClick, active, style, title }) => {
  const cls = `btn btn--${variant} btn--${size}${active ? " is-active" : ""}`;
  return (
    <button className={cls} onClick={onClick} style={style} title={title}>
      {iconLeft && <Icon name={iconLeft} size={size === "sm" ? 12 : 14} />}
      {children && <span>{children}</span>}
      {iconRight && <Icon name={iconRight} size={size === "sm" ? 12 : 14} />}
    </button>
  );
};

const IconBtn = ({ name, onClick, active, title, size = 16 }) => (
  <button className={`icon-btn${active ? " is-active" : ""}`} onClick={onClick} title={title}>
    <Icon name={name} size={size} />
  </button>
);

const Kbd = ({ children }) => <span className="kbd">{children}</span>;

const Pill = ({ children, tone = "neutral", dotColor }) => (
  <span className={`pill pill--${tone}`}>
    {dotColor && <span className="pill-dot" style={{ background: dotColor }} />}
    {children}
  </span>
);

const Section = ({ title, right, children, defaultOpen = true }) => {
  const [open, setOpen] = useState(defaultOpen);
  return (
    <div className={`isp-section${open ? "" : " is-collapsed"}`}>
      <button className="isp-header" onClick={() => setOpen(o => !o)}>
        <Icon name="chevronDown" size={12} className="isp-chev" />
        <span>{title}</span>
        {right && <span className="isp-right">{right}</span>}
      </button>
      {open && <div className="isp-body">{children}</div>}
    </div>
  );
};

const Field = ({ label, hint, children }) => (
  <div className="field">
    {label && <label>{label}</label>}
    {children}
    {hint && <div className="field-hint">{hint}</div>}
  </div>
);

const Slider = ({ value, min = 0, max = 100, unit = "px", onChange }) => {
  const pct = ((value - min) / (max - min)) * 100;
  return (
    <div className="slider">
      <div className="slider-track">
        <div className="slider-fill" style={{ width: `${pct}%` }} />
        <div className="slider-thumb" style={{ left: `calc(${pct}% - 7px)` }} />
      </div>
      <input type="number" className="slider-num" value={value}
        onChange={e => onChange?.(+e.target.value)} />
      <span className="slider-unit">{unit}</span>
    </div>
  );
};

const SwatchRow = ({ value, onChange, swatches = ["#0c0c0e", "#3b6bff", "#16a34a", "#d97706", "#dc2626", "transparent"] }) => (
  <div className="swatches">
    {swatches.map(c => (
      <button key={c} className={`swatch-chip${c === value ? " is-active" : ""}`}
        style={{ background: c === "transparent" ? "transparent" : c,
                 backgroundImage: c === "transparent"
                   ? "linear-gradient(45deg, rgba(0,0,0,0.18) 25%, transparent 25%, transparent 75%, rgba(0,0,0,0.18) 75%), linear-gradient(45deg, rgba(0,0,0,0.18) 25%, transparent 25%, transparent 75%, rgba(0,0,0,0.18) 75%)"
                   : undefined,
                 backgroundSize: "6px 6px",
                 backgroundPosition: "0 0, 3px 3px",
               }}
        onClick={() => onChange?.(c)} />
    ))}
  </div>
);

/* ------------------------------------------------------------------
   Color picker — full HSV picker with hex / rgb input + swatches.
   Opens a popover, closes on outside click / Esc.
   ------------------------------------------------------------------ */
const hsvToRgb = (h, s, v) => {
  s /= 100; v /= 100;
  const k = n => (n + h / 60) % 6;
  const f = n => v - v * s * Math.max(0, Math.min(k(n), 4 - k(n), 1));
  return [Math.round(f(5) * 255), Math.round(f(3) * 255), Math.round(f(1) * 255)];
};
const rgbToHex = (r, g, b) =>
  "#" + [r, g, b].map(x => x.toString(16).padStart(2, "0")).join("").toUpperCase();
const hexToRgb = (hex) => {
  if (!hex || hex === "transparent") return null;
  hex = hex.replace("#", "");
  if (hex.length === 3) hex = hex.split("").map(c => c + c).join("");
  if (hex.length !== 6 || /[^0-9a-f]/i.test(hex)) return null;
  return [parseInt(hex.slice(0, 2), 16), parseInt(hex.slice(2, 4), 16), parseInt(hex.slice(4, 6), 16)];
};
const rgbToHsv = (r, g, b) => {
  r /= 255; g /= 255; b /= 255;
  const max = Math.max(r, g, b), min = Math.min(r, g, b);
  const d = max - min;
  let h = 0;
  if (d) {
    if (max === r) h = ((g - b) / d) % 6;
    else if (max === g) h = (b - r) / d + 2;
    else h = (r - g) / d + 4;
  }
  h = Math.round(h * 60); if (h < 0) h += 360;
  return [h, max ? Math.round(d / max * 100) : 0, Math.round(max * 100)];
};

const DEFAULT_PRESETS = ["#0c0c0e", "#3b6bff", "#16a34a", "#d97706", "#dc2626", "transparent"];
const SPECTRUM = ["#FF3B30", "#FF9500", "#FFCC00", "#34C759", "#00C7BE", "#30B0C7", "#007AFF", "#5856D6", "#AF52DE", "#FF2D55", "#A2845E", "#8E8E93"];

const ColorPicker = ({ value = "#0c0c0e", onChange, swatches = DEFAULT_PRESETS, label }) => {
  const [open, setOpen] = useState(false);
  const [popPos, setPopPos] = useState({ top: 0, left: 0 });
  const isTransparent = value === "transparent";
  const initRgb = hexToRgb(value) || [12, 12, 14];
  const [hsv, setHsv] = useState(() => rgbToHsv(...initRgb));
  const [alpha, setAlpha] = useState(100);
  const [recent, setRecent] = useState([]);
  const popRef = useRef(null);
  const trigRef = useRef(null);
  const svRef = useRef(null);
  const hueRef = useRef(null);
  const alphaRef = useRef(null);

  const [h, s, v] = hsv;
  const [r, g, b] = hsvToRgb(h, s, v);
  const hex = rgbToHex(r, g, b);

  useEffect(() => {
    if (!open) return;
    const place = () => {
      const r = trigRef.current?.getBoundingClientRect();
      if (!r) return;
      const popW = 264;
      const margin = 10;
      let left = r.right - popW;
      if (left < margin) left = margin;
      if (left + popW > window.innerWidth - margin) left = window.innerWidth - popW - margin;
      let top = r.bottom + 8;
      if (top + 420 > window.innerHeight) top = Math.max(margin, r.top - 420 - 8);
      setPopPos({ top, left });
    };
    place();
    const onDown = (e) => {
      if (popRef.current?.contains(e.target) || trigRef.current?.contains(e.target)) return;
      setOpen(false);
    };
    const onKey = (e) => { if (e.key === "Escape") setOpen(false); };
    window.addEventListener("resize", place);
    window.addEventListener("scroll", place, true);
    document.addEventListener("mousedown", onDown);
    document.addEventListener("keydown", onKey);
    return () => {
      window.removeEventListener("resize", place);
      window.removeEventListener("scroll", place, true);
      document.removeEventListener("mousedown", onDown);
      document.removeEventListener("keydown", onKey);
    };
  }, [open]);

  const commit = (nextHex) => {
    onChange?.(nextHex);
    if (nextHex && nextHex !== "transparent" && !recent.includes(nextHex)) {
      setRecent(r => [nextHex, ...r].slice(0, 8));
    }
  };

  const dragSV = (e) => {
    e.preventDefault();
    const rect = svRef.current.getBoundingClientRect();
    const upd = (ev) => {
      const cx = ev.touches ? ev.touches[0].clientX : ev.clientX;
      const cy = ev.touches ? ev.touches[0].clientY : ev.clientY;
      const x = Math.max(0, Math.min(1, (cx - rect.left) / rect.width));
      const y = Math.max(0, Math.min(1, (cy - rect.top) / rect.height));
      const ns = Math.round(x * 100), nv = Math.round((1 - y) * 100);
      setHsv(([oh]) => {
        const [nr, ng, nb] = hsvToRgb(oh, ns, nv);
        commit(rgbToHex(nr, ng, nb));
        return [oh, ns, nv];
      });
    };
    upd(e);
    const onUp = () => {
      document.removeEventListener("mousemove", upd);
      document.removeEventListener("mouseup", onUp);
    };
    document.addEventListener("mousemove", upd);
    document.addEventListener("mouseup", onUp);
  };

  const dragHue = (e) => {
    e.preventDefault();
    const rect = hueRef.current.getBoundingClientRect();
    const upd = (ev) => {
      const cx = ev.touches ? ev.touches[0].clientX : ev.clientX;
      const x = Math.max(0, Math.min(1, (cx - rect.left) / rect.width));
      const nh = Math.round(x * 360);
      setHsv(([_, os, ov]) => {
        const [nr, ng, nb] = hsvToRgb(nh, os, ov);
        commit(rgbToHex(nr, ng, nb));
        return [nh, os, ov];
      });
    };
    upd(e);
    const onUp = () => {
      document.removeEventListener("mousemove", upd);
      document.removeEventListener("mouseup", onUp);
    };
    document.addEventListener("mousemove", upd);
    document.addEventListener("mouseup", onUp);
  };

  const dragAlpha = (e) => {
    e.preventDefault();
    const rect = alphaRef.current.getBoundingClientRect();
    const upd = (ev) => {
      const cx = ev.touches ? ev.touches[0].clientX : ev.clientX;
      const x = Math.max(0, Math.min(1, (cx - rect.left) / rect.width));
      setAlpha(Math.round(x * 100));
    };
    upd(e);
    const onUp = () => {
      document.removeEventListener("mousemove", upd);
      document.removeEventListener("mouseup", onUp);
    };
    document.addEventListener("mousemove", upd);
    document.addEventListener("mouseup", onUp);
  };

  const onHex = (e) => {
    const txt = e.target.value.trim();
    const rgb = hexToRgb(txt.startsWith("#") ? txt : "#" + txt);
    if (rgb) {
      setHsv(rgbToHsv(...rgb));
      commit(rgbToHex(...rgb));
    }
  };

  const swatchBg = (c) =>
    c === "transparent"
      ? {
          backgroundImage:
            "linear-gradient(45deg, rgba(0,0,0,0.18) 25%, transparent 25%, transparent 75%, rgba(0,0,0,0.18) 75%), linear-gradient(45deg, rgba(0,0,0,0.18) 25%, transparent 25%, transparent 75%, rgba(0,0,0,0.18) 75%)",
          backgroundSize: "6px 6px",
          backgroundPosition: "0 0, 3px 3px",
        }
      : { background: c };

  return (
    <div className="cp">
      <div className="cp-row">
        <button
          ref={trigRef}
          className={`cp-trigger${open ? " is-open" : ""}`}
          onClick={() => setOpen(o => !o)}
          title={isTransparent ? "Transparent" : hex}>
          <span className="cp-swatch" style={swatchBg(value)} />
          <span className="cp-hex">{isTransparent ? "—" : hex.replace("#", "")}</span>
          <Icon name="chevronDown" size={10} />
        </button>
        <div className="cp-quick">
          {swatches.slice(0, 5).map(c => (
            <button key={c}
              className={`cp-quick-chip${c === value ? " is-active" : ""}`}
              style={swatchBg(c)}
              onClick={() => { const rgb = hexToRgb(c); if (rgb) setHsv(rgbToHsv(...rgb)); commit(c); }}
              title={c} />
          ))}
        </div>
      </div>

      {open && ReactDOM.createPortal(
        <div className="cp-pop" ref={popRef} role="dialog"
          style={{ top: popPos.top, left: popPos.left }}>
          <div
            ref={svRef}
            className="cp-sv"
            style={{ background: `hsl(${h}, 100%, 50%)` }}
            onMouseDown={dragSV}
            onTouchStart={dragSV}>
            <div className="cp-sv-white" />
            <div className="cp-sv-black" />
            <div className="cp-sv-thumb" style={{ left: `${s}%`, top: `${100 - v}%`, background: hex }} />
          </div>

          <div className="cp-strip cp-hue" ref={hueRef} onMouseDown={dragHue} onTouchStart={dragHue}>
            <div className="cp-strip-thumb" style={{ left: `${(h / 360) * 100}%` }} />
          </div>

          <div className="cp-strip cp-alpha" ref={alphaRef} onMouseDown={dragAlpha} onTouchStart={dragAlpha}>
            <div className="cp-alpha-fill" style={{ background: `linear-gradient(to right, transparent, ${hex})` }} />
            <div className="cp-strip-thumb" style={{ left: `${alpha}%` }} />
          </div>

          <div className="cp-inputs">
            <div className="cp-field cp-field--hex">
              <label>HEX</label>
              <input value={hex.replace("#", "")} onChange={onHex} maxLength={7} spellCheck={false} />
            </div>
            <div className="cp-field"><label>R</label><input value={r} onChange={(e)=>{const nr=+e.target.value||0;setHsv(rgbToHsv(nr,g,b));commit(rgbToHex(nr,g,b));}} /></div>
            <div className="cp-field"><label>G</label><input value={g} onChange={(e)=>{const ng=+e.target.value||0;setHsv(rgbToHsv(r,ng,b));commit(rgbToHex(r,ng,b));}} /></div>
            <div className="cp-field"><label>B</label><input value={b} onChange={(e)=>{const nb=+e.target.value||0;setHsv(rgbToHsv(r,g,nb));commit(rgbToHex(r,g,nb));}} /></div>
          </div>

          <div className="cp-section-label">Recent</div>
          <div className="cp-grid">
            {recent.length === 0
              ? <div className="cp-grid-empty">Picked colors land here</div>
              : recent.map(c => (
                  <button key={c} className="cp-grid-chip" style={swatchBg(c)}
                    onClick={() => { const rgb = hexToRgb(c); if (rgb) setHsv(rgbToHsv(...rgb)); commit(c); }} />
                ))}
          </div>

          <div className="cp-section-label">Spectrum</div>
          <div className="cp-grid">
            {SPECTRUM.map(c => (
              <button key={c} className="cp-grid-chip" style={swatchBg(c)}
                onClick={() => { const rgb = hexToRgb(c); if (rgb) setHsv(rgbToHsv(...rgb)); commit(c); }} />
            ))}
            <button className="cp-grid-chip cp-grid-chip--clear" style={swatchBg("transparent")}
              onClick={() => commit("transparent")} title="Transparent" />
          </div>

          <div className="cp-foot">
            <span className="cp-foot-meta">hsv {h}° · {s}% · {v}%</span>
            <button className="cp-foot-save" onClick={() => { setRecent(r => r.includes(hex) ? r : [hex, ...r].slice(0, 8)); }}>
              <Icon name="plus" size={11} /> Save
            </button>
          </div>
        </div>,
        document.body
      )}
    </div>
  );
};

const SegControl = ({ value, onChange, options }) => (
  <div className="seg">
    {options.map(o => (
      <button key={o.value}
        className={`seg-btn${o.value === value ? " is-active" : ""}`}
        onClick={() => onChange?.(o.value)} title={o.label}>
        {o.icon ? <Icon name={o.icon} size={14} /> : o.label}
      </button>
    ))}
  </div>
);

/* ------------------------------------------------------------------
   Device switcher — segmented control for canvas viewport
   ------------------------------------------------------------------ */
const DeviceSwitcher = ({ device, setDevice }) => {
  const devices = [
    { id: "desktop", icon: "monitor", label: "Desktop · 720" },
    { id: "tablet",  icon: "tablet",  label: "Tablet · 480" },
    { id: "mobile",  icon: "mobile",  label: "Mobile · 360" },
  ];
  return (
    <div className="device-switch" role="radiogroup" aria-label="Preview device">
      {devices.map(d => (
        <button key={d.id}
          className={`device-btn${device === d.id ? " is-active" : ""}`}
          onClick={() => setDevice(d.id)} title={d.label} aria-pressed={device === d.id}>
          <Icon name={d.icon} size={14} />
        </button>
      ))}
    </div>
  );
};

/* ------------------------------------------------------------------
   TopBar — fixed 48px
   ------------------------------------------------------------------ */
const TopBar = ({ docName, locale, locales, onLocaleChange, theme, setTheme, onExport, device, setDevice }) => (
  <div className="topbar">
    <div className="topbar-left">
      <div className="brand">
        <Icon name="logo" size={22} />
        <span className="brand-text">inlinr<span className="brand-dot">.</span></span>
      </div>
      <span className="topbar-sep" />
      <div className="breadcrumb">
        <span className="bc-muted">Templates</span>
        <Icon name="chevronRight" size={11} className="bc-chev" />
        <span className="bc-muted">Lifecycle</span>
        <Icon name="chevronRight" size={11} className="bc-chev" />
        <span className="bc-current">{docName}</span>
        <button className="bc-version" title="Version history">
          <span>v14</span>
          <Icon name="chevronDown" size={10} />
        </button>
      </div>
    </div>

    <div className="topbar-center">
      <div className="autosave">
        <span className="autosave-dot" />
        <span>Autosaved 2 min ago</span>
      </div>
    </div>

    <div className="topbar-right">
      <div className="locale-switcher">
        {locales.map(l => (
          <button key={l.code}
            className={`locale-chip${l.code === locale ? " is-active" : ""}${l.warn ? " has-warn" : ""}`}
            onClick={() => onLocaleChange(l.code)}
            title={l.warn ? `${l.code.toUpperCase()} · ${l.warn}` : l.code.toUpperCase()}>
            {l.code.toUpperCase()}
            {l.warn && <span className="locale-warn" />}
          </button>
        ))}
        <button className="locale-chip locale-add" title="Add locale">
          <Icon name="plus" size={11} />
        </button>
      </div>

      <span className="topbar-sep" />

      <DeviceSwitcher device={device} setDevice={setDevice} />

      <span className="topbar-sep" />

      <IconBtn name={theme === "dark" ? "sun" : "moon"} onClick={() => setTheme(theme === "dark" ? "light" : "dark")} title="Toggle dark preview (⌥ D)" />
      <IconBtn name="eye" title="Preview" />
      <IconBtn name="code" title="View HTML" />

      <span className="topbar-sep" />

      <Button variant="secondary" iconLeft="history">History</Button>
      <Button variant="primary" iconLeft="download" onClick={onExport}>
        Export ZIP
      </Button>

      <span className="topbar-sep" />

      <div className="avatar">AD</div>
    </div>
  </div>
);

/* ------------------------------------------------------------------
   LeftRail — narrow tab strip
   ------------------------------------------------------------------ */
const LeftRail = ({ tab, setTab, collapsed, onToggleCollapse }) => {
  const tabs = [
    { id: "blocks", icon: "grid", label: "Blocks" },
    { id: "layers", icon: "layers", label: "Layers" },
    { id: "locales", icon: "globe", label: "Locales" },
    { id: "media", icon: "image", label: "Media" },
    { id: "settings", icon: "settings", label: "Settings" },
  ];
  const click = (id) => {
    if (id === tab) onToggleCollapse?.();
    else { setTab(id); if (collapsed) onToggleCollapse?.(); }
  };
  return (
    <div className="leftrail">
      {tabs.map(t => (
        <button key={t.id}
          className={`rail-btn${t.id === tab && !collapsed ? " is-active" : ""}`}
          onClick={() => click(t.id)} title={`${t.label}${t.id === tab ? " — click again to collapse" : ""}`}>
          <Icon name={t.icon} size={18} />
        </button>
      ))}
      <div style={{ flex: 1 }} />
      <button className="rail-btn" title={collapsed ? "Expand panel" : "Collapse panel"}
        onClick={() => onToggleCollapse?.()}>
        <Icon name={collapsed ? "panelRight" : "panelLeft"} size={18} />
      </button>
      <button className="rail-btn" title="Help"><Icon name="info" size={18} /></button>
    </div>
  );
};

/* ------------------------------------------------------------------
   SidePanel — the expanded panel next to the rail
   ------------------------------------------------------------------ */
const BlocksPanel = ({ onDragBlock }) => {
  const blocks = [
    { id: "hero", icon: "image", label: "Hero" },
    { id: "text", icon: "type", label: "Text" },
    { id: "cta", icon: "button", label: "Button" },
    { id: "columns", icon: "columns", label: "Columns" },
    { id: "image", icon: "image", label: "Image" },
    { id: "divider", icon: "divider", label: "Divider" },
    { id: "spacer", icon: "spacer", label: "Spacer" },
    { id: "footer", icon: "mail", label: "Footer" },
  ];
  return (
    <div className="side-panel">
      <div className="panel-head">
        <div className="panel-title">Blocks</div>
        <div className="panel-search">
          <Icon name="search" size={12} />
          <input placeholder="Search blocks…" />
        </div>
      </div>
      <div className="panel-section">Layout</div>
      <div className="block-grid">
        {blocks.slice(0, 4).map(b => (
          <div key={b.id} className="block-tile" draggable onDragStart={() => onDragBlock?.(b.id)}>
            <div className="block-tile-art"><Icon name={b.icon} size={20} /></div>
            <div className="block-tile-label">{b.label}</div>
          </div>
        ))}
      </div>
      <div className="panel-section">Content</div>
      <div className="block-grid">
        {blocks.slice(4).map(b => (
          <div key={b.id} className="block-tile" draggable onDragStart={() => onDragBlock?.(b.id)}>
            <div className="block-tile-art"><Icon name={b.icon} size={20} /></div>
            <div className="block-tile-label">{b.label}</div>
          </div>
        ))}
      </div>
      <div className="panel-section">Saved</div>
      <div className="saved-row">
        <div className="saved-art"><Icon name="star" size={12} /></div>
        <div className="saved-meta"><div>Pro upsell hero</div><span>used in 4 templates</span></div>
      </div>
      <div className="saved-row">
        <div className="saved-art"><Icon name="star" size={12} /></div>
        <div className="saved-meta"><div>Legal footer (EU)</div><span>used in 12 templates</span></div>
      </div>
    </div>
  );
};

const LayersPanel = ({ blocks, selectedId, onSelect }) => (
  <div className="side-panel">
    <div className="panel-head">
      <div className="panel-title">Layers</div>
      <button className="panel-action"><Icon name="plus" size={12} /></button>
    </div>
    <div className="layers-list">
      {blocks.map((b, i) => (
        <button key={b.id} className={`layer-row${b.id === selectedId ? " is-active" : ""}`} onClick={() => onSelect(b.id)}>
          <span className="layer-handle"><Icon name="move" size={12} /></span>
          <Icon name={b.icon} size={14} className="layer-icon" />
          <span className="layer-label">{b.label}</span>
          <span className="layer-meta">{b.locales}</span>
        </button>
      ))}
    </div>
  </div>
);

const LocalesPanel = ({ locales, active, onSelect }) => (
  <div className="side-panel">
    <div className="panel-head">
      <div className="panel-title">Locales</div>
      <button className="panel-action"><Icon name="plus" size={12} /></button>
    </div>
    <div className="locales-list">
      {locales.map(l => (
        <button key={l.code} className={`locale-row${l.code === active ? " is-active" : ""}`} onClick={() => onSelect(l.code)}>
          <div className="locale-flag">{l.code.toUpperCase()}</div>
          <div className="locale-meta">
            <div className="locale-name">{l.name}</div>
            <div className="locale-progress">
              <div className="locale-progress-track"><div className="locale-progress-fill" style={{ width: `${l.progress}%` }} /></div>
              <span>{l.progress}%</span>
            </div>
          </div>
          {l.warn && <span className="locale-row-warn" title={l.warn}><Icon name="warn" size={12} /></span>}
        </button>
      ))}
    </div>
    <div className="panel-section">Default</div>
    <div className="locales-default">English (en‑US) is the source for translation.</div>
  </div>
);

/* ------------------------------------------------------------------
   Inspector — right rail
   ------------------------------------------------------------------ */
const Inspector = ({ block, onChange, collapsed, onToggleCollapse }) => {
  if (collapsed) {
    return (
      <div className="inspector inspector--collapsed">
        <button className="inspector-expand" onClick={onToggleCollapse} title="Expand inspector">
          <Icon name="chevronLeft" size={14} />
        </button>
        <div className="inspector-stub-label">Inspector</div>
        {block && (
          <div className="inspector-stub-icon" title={block.label}>
            <Icon name={block.icon} size={14} />
          </div>
        )}
      </div>
    );
  }
  if (!block) {
    return (
      <div className="inspector">
        <div className="inspector-collapse-bar">
          <button className="inspector-collapse" onClick={onToggleCollapse} title="Collapse inspector">
            <Icon name="chevronRight" size={13} />
          </button>
        </div>
        <div className="inspector-empty">
          <Icon name="chip" size={22} className="ie-icon" />
          <div className="ie-title">Nothing selected</div>
          <div className="ie-help">Click a block on the canvas to edit its content and styling.</div>
        </div>
      </div>
    );
  }

  return (
    <div className="inspector">
      <div className="inspector-head">
        <button className="inspector-collapse" onClick={onToggleCollapse} title="Collapse inspector">
          <Icon name="chevronRight" size={13} />
        </button>
        <div className="ih-title">
          <Icon name={block.icon} size={14} />
          <span>{block.label}</span>
        </div>
        <div className="ih-actions">
          <IconBtn name="duplicate" title="Duplicate (⌘D)" size={14} />
          <IconBtn name="trash" title="Delete (⌫)" size={14} />
        </div>
      </div>

      <Section title="Content" right={<Pill tone="neutral">EN</Pill>}>
        <Field label="Heading">
          <input className="input" defaultValue={block.heading || "Welcome to your Pro plan"} />
        </Field>
        <Field label="Body" hint="Markdown supported · `**bold**` · `[link](url)`">
          <textarea className="input input--ta" rows={3} defaultValue={block.body || "Everything you need to ship better email, in one workspace."} />
        </Field>
      </Section>

      <Section title="Typography">
        <Field label="Family">
          <div className="input input--select">
            <span>Plus Jakarta Sans</span>
            <Icon name="chevronDown" size={12} />
          </div>
        </Field>
        <Field label="Size">
          <Slider value={28} min={10} max={64} />
        </Field>
        <Field label="Weight">
          <SegControl value="600" onChange={()=>{}} options={[
            { value: "400", label: "400" },
            { value: "500", label: "500" },
            { value: "600", label: "600" },
            { value: "700", label: "700" },
          ]}/>
        </Field>
        <Field label="Alignment">
          <SegControl value="left" onChange={()=>{}} options={[
            { value: "left", icon: "alignLeft" },
            { value: "center", icon: "alignCenter" },
            { value: "right", icon: "alignRight" },
            { value: "justify", icon: "alignJustify" },
          ]}/>
        </Field>
        <Field label="Color">
          <ColorPicker value="#0c0c0e" />
        </Field>
      </Section>

      <Section title="Spacing" defaultOpen={false}>
        <Field label="Padding"><Slider value={32} min={0} max={96} /></Field>
        <Field label="Margin"><Slider value={16} min={0} max={64} /></Field>
      </Section>

      <Section title="Background" defaultOpen={false}>
        <Field label="Color"><ColorPicker value="transparent" /></Field>
      </Section>

      <Section title="Link" defaultOpen={false}>
        <Field label="URL">
          <input className="input" defaultValue="https://app.inlinr.com/upgrade" />
        </Field>
        <Field label="Tracking">
          <SegControl value="utm" onChange={()=>{}} options={[
            { value: "off", label: "Off" },
            { value: "utm", label: "UTM" },
            { value: "id", label: "ID" },
          ]}/>
        </Field>
      </Section>
    </div>
  );
};

/* ------------------------------------------------------------------
   StatusBar — bottom 28 px
   ------------------------------------------------------------------ */
const StatusBar = ({ blocks, locale, locales, theme }) => {
  const wordCount = 248;
  return (
    <div className="statusbar">
      <div className="sb-left">
        <span className="sb-item"><Icon name="check" size={11} /> Inline CSS ready</span>
        <span className="sb-item"><Icon name="globe" size={11} /> {locales.length} locales</span>
        <span className="sb-item"><Icon name="layers" size={11} /> {blocks.length} blocks</span>
      </div>
      <div className="sb-right">
        <span className="sb-item">{wordCount} words</span>
        <span className="sb-dot" />
        <span className="sb-item">2.4 MB · gz 612 KB</span>
        <span className="sb-dot" />
        <span className="sb-item">{theme === "dark" ? "Dark preview" : "Light preview"}</span>
        <span className="sb-dot" />
        <span className="sb-item">100%</span>
      </div>
    </div>
  );
};

/* ------------------------------------------------------------------
   FloatingToolbar — glass over the canvas (block-level controls)
   ------------------------------------------------------------------ */
const FloatingToolbar = () => (
  <div className="floattool">
    <IconBtn name="bold" title="Bold" size={14} />
    <IconBtn name="italic" title="Italic" size={14} active />
    <IconBtn name="underline" title="Underline" size={14} />
    <span className="ft-sep" />
    <IconBtn name="link" title="Link" size={14} />
    <IconBtn name="alignLeft" title="Align" size={14} />
    <span className="ft-sep" />
    <button className="ft-locale">EN <Icon name="chevronDown" size={11} /></button>
  </div>
);

Object.assign(window, { Icon, Button, IconBtn, Kbd, Pill, Section, Field, Slider, SwatchRow, ColorPicker, DeviceSwitcher, SegControl, TopBar, LeftRail, BlocksPanel, LayersPanel, LocalesPanel, Inspector, StatusBar, FloatingToolbar });
