// Auto-ported from new-site
"use client";
import React, { useState, useEffect, useRef, Fragment } from "react";

// --- tweaks-panel.jsx ---

// tweaks-panel.jsx
// Reusable Tweaks shell + form-control helpers.
//
// Owns the host protocol (listens for __activate_edit_mode / __deactivate_edit_mode,
// posts __edit_mode_available / __edit_mode_set_keys / __edit_mode_dismissed) so
// individual prototypes don't re-roll it. Ships a consistent set of controls so you
// don't hand-draw <input type="range">, segmented radios, steppers, etc.
//
// Usage (in an HTML file that loads React + Babel):
//
//   const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
//     "primaryColor": "#D97757",
//     "palette": ["#D97757", "#29261b", "#f6f4ef"],
//     "fontSize": 16,
//     "density": "regular",
//     "dark": false
//   }/*EDITMODE-END*/;
//
//   function App() {
//     const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
//     return (
//       <div style={{ fontSize: t.fontSize, color: t.primaryColor }}>
//         Hello
//         <TweaksPanel>
//           <TweakSection label="Typography" />
//           <TweakSlider label="Font size" value={t.fontSize} min={10} max={32} unit="px"
//                        onChange={(v) => setTweak('fontSize', v)} />
//           <TweakRadio  label="Density" value={t.density}
//                        options={['compact', 'regular', 'comfy']}
//                        onChange={(v) => setTweak('density', v)} />
//           <TweakSection label="Theme" />
//           <TweakColor  label="Primary" value={t.primaryColor}
//                        options={['#D97757', '#2A6FDB', '#1F8A5B', '#7A5AE0']}
//                        onChange={(v) => setTweak('primaryColor', v)} />
//           <TweakColor  label="Palette" value={t.palette}
//                        options={[['#D97757', '#29261b', '#f6f4ef'],
//                                  ['#475569', '#0f172a', '#f1f5f9']]}
//                        onChange={(v) => setTweak('palette', v)} />
//           <TweakToggle label="Dark mode" value={t.dark}
//                        onChange={(v) => setTweak('dark', v)} />
//         </TweaksPanel>
//       </div>
//     );
//   }
//
// ─────────────────────────────────────────────────────────────────────────────

const __TWEAKS_STYLE = `
  .twk-panel{position:fixed;right:16px;bottom:16px;z-index:2147483646;width:280px;
    max-height:calc(100vh - 32px);display:flex;flex-direction:column;
    transform:scale(var(--dc-inv-zoom,1));transform-origin:bottom right;
    background:rgba(250,249,247,.78);color:#29261b;
    -webkit-backdrop-filter:blur(24px) saturate(160%);backdrop-filter:blur(24px) saturate(160%);
    border:.5px solid rgba(255,255,255,.6);border-radius:14px;
    box-shadow:0 1px 0 rgba(255,255,255,.5) inset,0 12px 40px rgba(0,0,0,.18);
    font:11.5px/1.4 ui-sans-serif,system-ui,-apple-system,sans-serif;overflow:hidden}
  .twk-hd{display:flex;align-items:center;justify-content:space-between;
    padding:10px 8px 10px 14px;cursor:move;user-select:none}
  .twk-hd b{font-size:12px;font-weight:600;letter-spacing:.01em}
  .twk-x{appearance:none;border:0;background:transparent;color:rgba(41,38,27,.55);
    width:22px;height:22px;border-radius:6px;cursor:default;font-size:13px;line-height:1}
  .twk-x:hover{background:rgba(0,0,0,.06);color:#29261b}
  .twk-body{padding:2px 14px 14px;display:flex;flex-direction:column;gap:10px;
    overflow-y:auto;overflow-x:hidden;min-height:0;
    scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.15) transparent}
  .twk-body::-webkit-scrollbar{width:8px}
  .twk-body::-webkit-scrollbar-track{background:transparent;margin:2px}
  .twk-body::-webkit-scrollbar-thumb{background:rgba(0,0,0,.15);border-radius:4px;
    border:2px solid transparent;background-clip:content-box}
  .twk-body::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,.25);
    border:2px solid transparent;background-clip:content-box}
  .twk-row{display:flex;flex-direction:column;gap:5px}
  .twk-row-h{flex-direction:row;align-items:center;justify-content:space-between;gap:10px}
  .twk-lbl{display:flex;justify-content:space-between;align-items:baseline;
    color:rgba(41,38,27,.72)}
  .twk-lbl>span:first-child{font-weight:500}
  .twk-val{color:rgba(41,38,27,.5);font-variant-numeric:tabular-nums}

  .twk-sect{font-size:10px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
    color:rgba(41,38,27,.45);padding:10px 0 0}
  .twk-sect:first-child{padding-top:0}

  .twk-field{appearance:none;box-sizing:border-box;width:100%;min-width:0;height:26px;padding:0 8px;
    border:.5px solid rgba(0,0,0,.1);border-radius:7px;
    background:rgba(255,255,255,.6);color:inherit;font:inherit;outline:none}
  .twk-field:focus{border-color:rgba(0,0,0,.25);background:rgba(255,255,255,.85)}
  select.twk-field{padding-right:22px;
    background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='rgba(0,0,0,.5)' d='M0 0h10L5 6z'/></svg>");
    background-repeat:no-repeat;background-position:right 8px center}

  .twk-slider{appearance:none;-webkit-appearance:none;width:100%;height:4px;margin:6px 0;
    border-radius:999px;background:rgba(0,0,0,.12);outline:none}
  .twk-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;
    width:14px;height:14px;border-radius:50%;background:#fff;
    border:.5px solid rgba(0,0,0,.12);box-shadow:0 1px 3px rgba(0,0,0,.2);cursor:default}
  .twk-slider::-moz-range-thumb{width:14px;height:14px;border-radius:50%;
    background:#fff;border:.5px solid rgba(0,0,0,.12);box-shadow:0 1px 3px rgba(0,0,0,.2);cursor:default}

  .twk-seg{position:relative;display:flex;padding:2px;border-radius:8px;
    background:rgba(0,0,0,.06);user-select:none}
  .twk-seg-thumb{position:absolute;top:2px;bottom:2px;border-radius:6px;
    background:rgba(255,255,255,.9);box-shadow:0 1px 2px rgba(0,0,0,.12);
    transition:left .15s cubic-bezier(.3,.7,.4,1),width .15s}
  .twk-seg.dragging .twk-seg-thumb{transition:none}
  .twk-seg button{appearance:none;position:relative;z-index:1;flex:1;border:0;
    background:transparent;color:inherit;font:inherit;font-weight:500;min-height:22px;
    border-radius:6px;cursor:default;padding:4px 6px;line-height:1.2;
    overflow-wrap:anywhere}

  .twk-toggle{position:relative;width:32px;height:18px;border:0;border-radius:999px;
    background:rgba(0,0,0,.15);transition:background .15s;cursor:default;padding:0}
  .twk-toggle[data-on="1"]{background:#34c759}
  .twk-toggle i{position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;
    background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.25);transition:transform .15s}
  .twk-toggle[data-on="1"] i{transform:translateX(14px)}

  .twk-num{display:flex;align-items:center;box-sizing:border-box;min-width:0;height:26px;padding:0 0 0 8px;
    border:.5px solid rgba(0,0,0,.1);border-radius:7px;background:rgba(255,255,255,.6)}
  .twk-num-lbl{font-weight:500;color:rgba(41,38,27,.6);cursor:ew-resize;
    user-select:none;padding-right:8px}
  .twk-num input{flex:1;min-width:0;height:100%;border:0;background:transparent;
    font:inherit;font-variant-numeric:tabular-nums;text-align:right;padding:0 8px 0 0;
    outline:none;color:inherit;-moz-appearance:textfield}
  .twk-num input::-webkit-inner-spin-button,.twk-num input::-webkit-outer-spin-button{
    -webkit-appearance:none;margin:0}
  .twk-num-unit{padding-right:8px;color:rgba(41,38,27,.45)}

  .twk-btn{appearance:none;height:26px;padding:0 12px;border:0;border-radius:7px;
    background:rgba(0,0,0,.78);color:#fff;font:inherit;font-weight:500;cursor:default}
  .twk-btn:hover{background:rgba(0,0,0,.88)}
  .twk-btn.secondary{background:rgba(0,0,0,.06);color:inherit}
  .twk-btn.secondary:hover{background:rgba(0,0,0,.1)}

  .twk-swatch{appearance:none;-webkit-appearance:none;width:56px;height:22px;
    border:.5px solid rgba(0,0,0,.1);border-radius:6px;padding:0;cursor:default;
    background:transparent;flex-shrink:0}
  .twk-swatch::-webkit-color-swatch-wrapper{padding:0}
  .twk-swatch::-webkit-color-swatch{border:0;border-radius:5.5px}
  .twk-swatch::-moz-color-swatch{border:0;border-radius:5.5px}

  .twk-chips{display:flex;gap:6px}
  .twk-chip{position:relative;appearance:none;flex:1;min-width:0;height:46px;
    padding:0;border:0;border-radius:6px;overflow:hidden;cursor:default;
    box-shadow:0 0 0 .5px rgba(0,0,0,.12),0 1px 2px rgba(0,0,0,.06);
    transition:transform .12s cubic-bezier(.3,.7,.4,1),box-shadow .12s}
  .twk-chip:hover{transform:translateY(-1px);
    box-shadow:0 0 0 .5px rgba(0,0,0,.18),0 4px 10px rgba(0,0,0,.12)}
  .twk-chip[data-on="1"]{box-shadow:0 0 0 1.5px rgba(0,0,0,.85),
    0 2px 6px rgba(0,0,0,.15)}
  .twk-chip>span{position:absolute;top:0;bottom:0;right:0;width:34%;
    display:flex;flex-direction:column;box-shadow:-1px 0 0 rgba(0,0,0,.1)}
  .twk-chip>span>i{flex:1;box-shadow:0 -1px 0 rgba(0,0,0,.1)}
  .twk-chip>span>i:first-child{box-shadow:none}
  .twk-chip svg{position:absolute;top:6px;left:6px;width:13px;height:13px;
    filter:drop-shadow(0 1px 1px rgba(0,0,0,.3))}
`;

// ── useTweaks ───────────────────────────────────────────────────────────────
// Single source of truth for tweak values. setTweak persists via the host
// (__edit_mode_set_keys → host rewrites the EDITMODE block on disk).
function useTweaks(defaults) {
  const [values, setValues] = useState(defaults);
  // Accepts either setTweak('key', value) or setTweak({ key: value, ... }) so a
  // useState-style call doesn't write a "[object Object]" key into the persisted
  // JSON block.
  const setTweak = React.useCallback((keyOrEdits, val) => {
    const edits =
      typeof keyOrEdits === "object" && keyOrEdits !== null
        ? keyOrEdits
        : { [keyOrEdits]: val };
    setValues((prev) => ({ ...prev, ...edits }));
    window.parent.postMessage({ type: "__edit_mode_set_keys", edits }, "*");
    // Same-window signal so in-page listeners (deck-stage rail thumbnails)
    // can react — the parent message only reaches the host, not peers.
    window.dispatchEvent(new CustomEvent("tweakchange", { detail: edits }));
  }, []);
  return [values, setTweak];
}

// ── TweaksPanel ─────────────────────────────────────────────────────────────
// Floating shell. Registers the protocol listener BEFORE announcing
// availability — if the announce ran first, the host's activate could land
// before our handler exists and the toolbar toggle would silently no-op.
// The close button posts __edit_mode_dismissed so the host's toolbar toggle
// flips off in lockstep; the host echoes __deactivate_edit_mode back which
// is what actually hides the panel.
function TweaksPanel({ title = "Tweaks", noDeckControls = false, children }) {
  const [open, setOpen] = useState(false);
  const dragRef = useRef(null);
  // Auto-inject a rail toggle when a <deck-stage> is on the page. The
  // toggle drives the deck's per-viewer _railVisible via window message;
  // state is mirrored from the same localStorage key the deck reads so
  // the control reflects reality across reloads. The mechanism is the
  // message — authors who want custom placement can post it directly
  // and pass noDeckControls to suppress this one.
  const hasDeckStage = React.useMemo(
    () =>
      typeof document !== "undefined" && !!document.querySelector("deck-stage"),
    [],
  );
  // deck-stage enables its rail in connectedCallback, but this panel can
  // mount before that element has upgraded. The initial read catches the
  // common case; the listener covers mounting first. (Older deck-stage.js
  // copies still wait for the host's __omelette_rail_enabled postMessage —
  // same listener handles those.)
  const [railEnabled, setRailEnabled] = useState(
    () => hasDeckStage && !!document.querySelector("deck-stage")?._railEnabled,
  );
  useEffect(() => {
    if (!hasDeckStage || railEnabled) return undefined;
    const onMsg = (e) => {
      if (e.data && e.data.type === "__omelette_rail_enabled")
        setRailEnabled(true);
    };
    window.addEventListener("message", onMsg);
    return () => window.removeEventListener("message", onMsg);
  }, [hasDeckStage, railEnabled]);
  const [railVisible, setRailVisible] = useState(() => {
    try {
      return localStorage.getItem("deck-stage.railVisible") !== "0";
    } catch (e) {
      return true;
    }
  });
  const toggleRail = (on) => {
    setRailVisible(on);
    window.postMessage({ type: "__deck_rail_visible", on }, "*");
  };
  const offsetRef = useRef({ x: 16, y: 16 });
  const PAD = 16;

  const clampToViewport = React.useCallback(() => {
    const panel = dragRef.current;
    if (!panel) return;
    const w = panel.offsetWidth,
      h = panel.offsetHeight;
    const maxRight = Math.max(PAD, window.innerWidth - w - PAD);
    const maxBottom = Math.max(PAD, window.innerHeight - h - PAD);
    offsetRef.current = {
      x: Math.min(maxRight, Math.max(PAD, offsetRef.current.x)),
      y: Math.min(maxBottom, Math.max(PAD, offsetRef.current.y)),
    };
    panel.style.right = offsetRef.current.x + "px";
    panel.style.bottom = offsetRef.current.y + "px";
  }, []);

  useEffect(() => {
    if (!open) return;
    clampToViewport();
    if (typeof ResizeObserver === "undefined") {
      window.addEventListener("resize", clampToViewport);
      return () => window.removeEventListener("resize", clampToViewport);
    }
    const ro = new ResizeObserver(clampToViewport);
    ro.observe(document.documentElement);
    return () => ro.disconnect();
  }, [open, clampToViewport]);

  useEffect(() => {
    const onMsg = (e) => {
      const t = e?.data?.type;
      if (t === "__activate_edit_mode") setOpen(true);
      else if (t === "__deactivate_edit_mode") setOpen(false);
    };
    window.addEventListener("message", onMsg);
    window.parent.postMessage({ type: "__edit_mode_available" }, "*");
    return () => window.removeEventListener("message", onMsg);
  }, []);

  const dismiss = () => {
    setOpen(false);
    window.parent.postMessage({ type: "__edit_mode_dismissed" }, "*");
  };

  const onDragStart = (e) => {
    const panel = dragRef.current;
    if (!panel) return;
    const r = panel.getBoundingClientRect();
    const sx = e.clientX,
      sy = e.clientY;
    const startRight = window.innerWidth - r.right;
    const startBottom = window.innerHeight - r.bottom;
    const move = (ev) => {
      offsetRef.current = {
        x: startRight - (ev.clientX - sx),
        y: startBottom - (ev.clientY - sy),
      };
      clampToViewport();
    };
    const up = () => {
      window.removeEventListener("mousemove", move);
      window.removeEventListener("mouseup", up);
    };
    window.addEventListener("mousemove", move);
    window.addEventListener("mouseup", up);
  };

  if (!open) return null;
  return (
    <>
      <style>{__TWEAKS_STYLE}</style>
      <div
        ref={dragRef}
        className="twk-panel"
        data-noncommentable=""
        style={{ right: offsetRef.current.x, bottom: offsetRef.current.y }}
      >
        <div className="twk-hd" onMouseDown={onDragStart}>
          <b>{title}</b>
          <button
            className="twk-x"
            aria-label="Close tweaks"
            onMouseDown={(e) => e.stopPropagation()}
            onClick={dismiss}
          >
            ✕
          </button>
        </div>
        <div className="twk-body">
          {children}
          {hasDeckStage && railEnabled && !noDeckControls && (
            <TweakSection label="Deck">
              <TweakToggle
                label="Thumbnail rail"
                value={railVisible}
                onChange={toggleRail}
              />
            </TweakSection>
          )}
        </div>
      </div>
    </>
  );
}

// ── Layout helpers ──────────────────────────────────────────────────────────

function TweakSection({ label, children }) {
  return (
    <>
      <div className="twk-sect">{label}</div>
      {children}
    </>
  );
}

function TweakRow({ label, value, children, inline = false }) {
  return (
    <div className={inline ? "twk-row twk-row-h" : "twk-row"}>
      <div className="twk-lbl">
        <span>{label}</span>
        {value != null && <span className="twk-val">{value}</span>}
      </div>
      {children}
    </div>
  );
}

// ── Controls ────────────────────────────────────────────────────────────────

function TweakSlider({
  label,
  value,
  min = 0,
  max = 100,
  step = 1,
  unit = "",
  onChange,
}) {
  return (
    <TweakRow label={label} value={`${value}${unit}`}>
      <input
        type="range"
        className="twk-slider"
        min={min}
        max={max}
        step={step}
        value={value}
        onChange={(e) => onChange(Number(e.target.value))}
      />
    </TweakRow>
  );
}

function TweakToggle({ label, value, onChange }) {
  return (
    <div className="twk-row twk-row-h">
      <div className="twk-lbl">
        <span>{label}</span>
      </div>
      <button
        type="button"
        className="twk-toggle"
        data-on={value ? "1" : "0"}
        role="switch"
        aria-checked={!!value}
        onClick={() => onChange(!value)}
      >
        <i />
      </button>
    </div>
  );
}

function TweakRadio({ label, value, options, onChange }) {
  const trackRef = useRef(null);
  const [dragging, setDragging] = useState(false);
  // The active value is read by pointer-move handlers attached for the lifetime
  // of a drag — ref it so a stale closure doesn't fire onChange for every move.
  const valueRef = useRef(value);
  valueRef.current = value;

  // Segments wrap mid-word once per-segment width runs out. The track is
  // ~248px (280 panel − 28 body pad − 4 seg pad), each button loses 12px
  // to its own padding, and 11.5px system-ui averages ~6.3px/char — so 2
  // options fit ~16 chars each, 3 fit ~10. Past that (or >3 options), fall
  // back to a dropdown rather than wrap.
  const labelLen = (o) => String(typeof o === "object" ? o.label : o).length;
  const maxLen = options.reduce((m, o) => Math.max(m, labelLen(o)), 0);
  const fitsAsSegments = maxLen <= ({ 2: 16, 3: 10 }[options.length] ?? 0);
  if (!fitsAsSegments) {
    // <select> emits strings — map back to the original option value so the
    // fallback stays type-preserving (numbers, booleans) like the segment path.
    const resolve = (s) => {
      const m = options.find(
        (o) => String(typeof o === "object" ? o.value : o) === s,
      );
      return m === undefined ? s : typeof m === "object" ? m.value : m;
    };
    return (
      <TweakSelect
        label={label}
        value={value}
        options={options}
        onChange={(s) => onChange(resolve(s))}
      />
    );
  }
  const opts = options.map((o) =>
    typeof o === "object" ? o : { value: o, label: o },
  );
  const idx = Math.max(
    0,
    opts.findIndex((o) => o.value === value),
  );
  const n = opts.length;

  const segAt = (clientX) => {
    const r = trackRef.current.getBoundingClientRect();
    const inner = r.width - 4;
    const i = Math.floor(((clientX - r.left - 2) / inner) * n);
    return opts[Math.max(0, Math.min(n - 1, i))].value;
  };

  const onPointerDown = (e) => {
    setDragging(true);
    const v0 = segAt(e.clientX);
    if (v0 !== valueRef.current) onChange(v0);
    const move = (ev) => {
      if (!trackRef.current) return;
      const v = segAt(ev.clientX);
      if (v !== valueRef.current) onChange(v);
    };
    const up = () => {
      setDragging(false);
      window.removeEventListener("pointermove", move);
      window.removeEventListener("pointerup", up);
    };
    window.addEventListener("pointermove", move);
    window.addEventListener("pointerup", up);
  };

  return (
    <TweakRow label={label}>
      <div
        ref={trackRef}
        role="radiogroup"
        onPointerDown={onPointerDown}
        className={dragging ? "twk-seg dragging" : "twk-seg"}
      >
        <div
          className="twk-seg-thumb"
          style={{
            left: `calc(2px + ${idx} * (100% - 4px) / ${n})`,
            width: `calc((100% - 4px) / ${n})`,
          }}
        />
        {opts.map((o) => (
          <button
            key={o.value}
            type="button"
            role="radio"
            aria-checked={o.value === value}
          >
            {o.label}
          </button>
        ))}
      </div>
    </TweakRow>
  );
}

function TweakSelect({ label, value, options, onChange }) {
  return (
    <TweakRow label={label}>
      <select
        className="twk-field"
        value={value}
        onChange={(e) => onChange(e.target.value)}
      >
        {options.map((o) => {
          const v = typeof o === "object" ? o.value : o;
          const l = typeof o === "object" ? o.label : o;
          return (
            <option key={v} value={v}>
              {l}
            </option>
          );
        })}
      </select>
    </TweakRow>
  );
}

function TweakText({ label, value, placeholder, onChange }) {
  return (
    <TweakRow label={label}>
      <input
        className="twk-field"
        type="text"
        value={value}
        placeholder={placeholder}
        onChange={(e) => onChange(e.target.value)}
      />
    </TweakRow>
  );
}

function TweakNumber({
  label,
  value,
  min,
  max,
  step = 1,
  unit = "",
  onChange,
}) {
  const clamp = (n) => {
    if (min != null && n < min) return min;
    if (max != null && n > max) return max;
    return n;
  };
  const startRef = useRef({ x: 0, val: 0 });
  const onScrubStart = (e) => {
    e.preventDefault();
    startRef.current = { x: e.clientX, val: value };
    const decimals = (String(step).split(".")[1] || "").length;
    const move = (ev) => {
      const dx = ev.clientX - startRef.current.x;
      const raw = startRef.current.val + dx * step;
      const snapped = Math.round(raw / step) * step;
      onChange(clamp(Number(snapped.toFixed(decimals))));
    };
    const up = () => {
      window.removeEventListener("pointermove", move);
      window.removeEventListener("pointerup", up);
    };
    window.addEventListener("pointermove", move);
    window.addEventListener("pointerup", up);
  };
  return (
    <div className="twk-num">
      <span className="twk-num-lbl" onPointerDown={onScrubStart}>
        {label}
      </span>
      <input
        type="number"
        value={value}
        min={min}
        max={max}
        step={step}
        onChange={(e) => onChange(clamp(Number(e.target.value)))}
      />
      {unit && <span className="twk-num-unit">{unit}</span>}
    </div>
  );
}

// Relative-luminance contrast pick — checkmarks drawn over a swatch need to
// read on both #111 and #fafafa without per-option configuration. Hex input
// only (#rgb / #rrggbb); named or rgb()/hsl() colors fall through to "light".
function __twkIsLight(hex) {
  const h = String(hex).replace("#", "");
  const x = h.length === 3 ? h.replace(/./g, (c) => c + c) : h.padEnd(6, "0");
  const n = parseInt(x.slice(0, 6), 16);
  if (Number.isNaN(n)) return true;
  const r = (n >> 16) & 255,
    g = (n >> 8) & 255,
    b = n & 255;
  return r * 299 + g * 587 + b * 114 > 148000;
}

const __TwkCheck = ({ light }) => (
  <svg viewBox="0 0 14 14" aria-hidden="true">
    <path
      d="M3 7.2 5.8 10 11 4.2"
      fill="none"
      strokeWidth="2.2"
      strokeLinecap="round"
      strokeLinejoin="round"
      stroke={light ? "rgba(0,0,0,.78)" : "#fff"}
    />
  </svg>
);

// TweakColor — curated color/palette picker. Each option is either a single
// hex string or an array of 1-5 hex strings; the card adapts — a lone color
// renders solid, a palette renders colors[0] as the hero (left ~2/3) with the
// rest stacked in a sharp column on the right. onChange emits the
// option in the shape it was passed (string stays string, array stays array).
// Without options it falls back to the native color input for back-compat.
function TweakColor({ label, value, options, onChange }) {
  if (!options || !options.length) {
    return (
      <div className="twk-row twk-row-h">
        <div className="twk-lbl">
          <span>{label}</span>
        </div>
        <input
          type="color"
          className="twk-swatch"
          value={value}
          onChange={(e) => onChange(e.target.value)}
        />
      </div>
    );
  }
  // Native <input type=color> emits lowercase hex per the HTML spec, so
  // compare case-insensitively. String() guards JSON.stringify(undefined),
  // which returns the primitive undefined (no .toLowerCase).
  const key = (o) => String(JSON.stringify(o)).toLowerCase();
  const cur = key(value);
  return (
    <TweakRow label={label}>
      <div className="twk-chips" role="radiogroup">
        {options.map((o, i) => {
          const colors = Array.isArray(o) ? o : [o];
          const [hero, ...rest] = colors;
          const sup = rest.slice(0, 4);
          const on = key(o) === cur;
          return (
            <button
              key={i}
              type="button"
              className="twk-chip"
              role="radio"
              aria-checked={on}
              data-on={on ? "1" : "0"}
              aria-label={colors.join(", ")}
              title={colors.join(" · ")}
              style={{ background: hero }}
              onClick={() => onChange(o)}
            >
              {sup.length > 0 && (
                <span>
                  {sup.map((c, j) => (
                    <i key={j} style={{ background: c }} />
                  ))}
                </span>
              )}
              {on && <__TwkCheck light={__twkIsLight(hero)} />}
            </button>
          );
        })}
      </div>
    </TweakRow>
  );
}

function TweakButton({ label, onClick, secondary = false }) {
  return (
    <button
      type="button"
      className={secondary ? "twk-btn secondary" : "twk-btn"}
      onClick={onClick}
    >
      {label}
    </button>
  );
}

// --- primitives.jsx ---
// Small shared primitives: logo, arrow icon, ambient glow, eyebrow, trust strip.

// The "V_" mark — bold filled V + terminal-cursor underscore. Sized via the
// outer .logo-mark; the SVG fills it. `stroke-linejoin="round"` + a thin
// stroke on the filled paths softens the corners just enough.
export function LogoMark({
  size = 26,
  blink = true,
}: {
  size?: number;
  blink?: boolean;
}) {
  return (
    <span className="logo-mark" style={{ width: size, height: size }}>
      <svg
        viewBox="0 0 36 32"
        width="74%"
        height="74%"
        fill="currentColor"
        stroke="currentColor"
        strokeWidth="1.2"
        strokeLinejoin="round"
        aria-hidden="true"
      >
        <path d="M4 5 L10 5 L12 18 L14 5 L20 5 L12 27 Z" />
        <rect
          x="22.5"
          y="23"
          width="9.5"
          height="3.8"
          rx="0.7"
          className={blink ? "logo-caret" : ""}
        />
      </svg>
    </span>
  );
}

export function Logo({ size = 26 }: { size?: number }) {
  return (
    <span className="logo">
      <LogoMark size={size} />
      <span>vibn</span>
    </span>
  );
}

function Arrow({ size = 14 }) {
  return (
    <svg
      className="arrow"
      width={size}
      height={size}
      viewBox="0 0 16 16"
      fill="none"
      aria-hidden="true"
    >
      <path
        d="M3 8h10M9 4l4 4-4 4"
        stroke="currentColor"
        strokeWidth="1.6"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

function Eyebrow({ children }) {
  return <div className="eyebrow">{children}</div>;
}

// Soft radial glow blob for ambient backgrounds. Place absolutely positioned.
function Glow({
  color = "var(--accent-glow)",
  size = 700,
  opacity = 1,
  style = {},
}) {
  return (
    <div
      aria-hidden="true"
      style={{
        position: "absolute",
        width: size,
        height: size,
        background: `radial-gradient(circle at center, ${color} 0%, transparent 62%)`,
        filter: "blur(20px)",
        opacity,
        pointerEvents: "none",
        ...style,
      }}
    />
  );
}

function TrustStrip({ items }) {
  return (
    <div
      className="mono"
      style={{
        display: "flex",
        flexWrap: "wrap",
        gap: "8px 18px",
        fontSize: 12,
        color: "var(--fg-mute)",
        letterSpacing: "0.04em",
      }}
    >
      {items.map((item, i) => (
        <Fragment key={i}>
          {i > 0 && <span style={{ color: "var(--fg-faint)" }}>·</span>}
          <span>{item}</span>
        </Fragment>
      ))}
    </div>
  );
}

// A subtle gradient hairline used inside cards & frames.
function Hairline({ vertical = false, style = {} }) {
  return (
    <div
      aria-hidden="true"
      style={{
        background: vertical
          ? "linear-gradient(180deg, transparent, var(--hairline) 30%, var(--hairline) 70%, transparent)"
          : "linear-gradient(90deg, transparent, var(--hairline) 30%, var(--hairline) 70%, transparent)",
        height: vertical ? "100%" : 1,
        width: vertical ? 1 : "100%",
        ...style,
      }}
    />
  );
}

// --- hero.jsx ---
// Hero: the Reddit quote headline + prompt input.
// Visitors can type into the prompt; cycling placeholders, suggestion chips, submit handler logs to console.

const HERO_PLACEHOLDERS = [
  "A booking site for my dog grooming business…",
  "An invoice tracker for my freelance clients…",
  "A members-only recipe site for my supper club…",
  "A custom CRM for our 3-person real estate team…",
  "A tip calculator app for our restaurant staff…",
  "A waitlist site for my new ceramics studio…",
];

const HERO_CHIPS = [
  "📋  Client intake form",
  "📅  Booking site",
  "🧾  Invoice tracker",
  "🛒  Online store",
  "📰  Email newsletter",
];

function Hero({ onStart, variant = "quote" }) {
  const [text, setText] = useState("");
  const [phIdx, setPhIdx] = useState(0);
  const [phChars, setPhChars] = useState(0);
  const [deleting, setDeleting] = useState(false);
  const taRef = useRef(null);

  // Type-on placeholder when textarea is empty.
  useEffect(() => {
    if (text.length > 0) return undefined;
    const full = HERO_PLACEHOLDERS[phIdx];
    const speed = deleting ? 18 : 38;
    const t = setTimeout(() => {
      if (!deleting) {
        if (phChars < full.length) setPhChars(phChars + 1);
        else setTimeout(() => setDeleting(true), 1700);
      } else {
        if (phChars > 0) setPhChars(phChars - 1);
        else {
          setDeleting(false);
          setPhIdx((phIdx + 1) % HERO_PLACEHOLDERS.length);
        }
      }
    }, speed);
    return () => clearTimeout(t);
  }, [text, phIdx, phChars, deleting]);

  const placeholder = HERO_PLACEHOLDERS[phIdx].slice(0, phChars);

  const submit = () => {
    const value = text || HERO_PLACEHOLDERS[phIdx];
    if (onStart) onStart(value);
  };

  const useChip = (chip) => {
    const clean = chip.replace(/^[^\w]+/, "").trim();
    setText(`Build me ${clean.toLowerCase()} for my business.`);
    if (taRef.current) taRef.current.focus();
  };

  return (
    <header className="section hero">
      <style>{`
        .hero {
          padding-top: clamp(60px, 9vh, 120px);
          padding-bottom: clamp(60px, 10vh, 120px);
          position: relative;
          overflow: hidden;
        }
        .hero-inner {
          position: relative;
          display: flex; flex-direction: column; align-items: center;
          text-align: center;
          gap: 28px;
        }
        .hero-quote {
          font-size: clamp(44px, 7.4vw, 104px);
          font-weight: 500;
          letter-spacing: -0.035em;
          line-height: 0.98;
          text-wrap: balance;
          position: relative;
          /* Silver / brushed-metal heading: bright at the top, fading to a soft
             pewter at the bottom, clipped to the text. */
          background-image: linear-gradient(
            180deg,
            oklch(0.99 0.003 80) 0%,
            oklch(0.86 0.004 80) 42%,
            oklch(0.66 0.006 80) 100%
          );
          -webkit-background-clip: text;
          background-clip: text;
          color: transparent;
          -webkit-text-fill-color: transparent;
        }
        .hero-quote .mark {
          /* keep the accent word in full color inside the silver heading */
          color: var(--accent);
          -webkit-text-fill-color: var(--accent);
          font-family: "Geist", serif;
          font-weight: 500;
          line-height: 0;
          vertical-align: -0.05em;
          margin-inline: -0.08em;
          text-shadow: 0 0 30px var(--accent-glow);
        }
        .hero-attribution {
          font-family: var(--font-mono);
          font-size: 12px;
          color: var(--fg-dim);
          letter-spacing: 0.04em;
          margin-top: 6px;
          display: inline-flex; align-items: center; gap: 8px;
        }
        .hero-attribution::before, .hero-attribution::after {
          content: ""; width: 24px; height: 1px;
          background: var(--hairline);
        }

        .hero-sub {
          font-size: clamp(20px, 2.2vw, 28px);
          color: var(--fg-dim);
          letter-spacing: -0.01em;
          text-wrap: balance;
          max-width: 720px;
        }
        .hero-sub b {
          color: var(--fg);
          font-weight: 500;
        }

        /* Prompt input */
        .prompt {
          width: 100%;
          max-width: 720px;
          margin-top: 14px;
          position: relative;
        }
        .prompt-frame {
          position: relative;
          border-radius: var(--r-xl);
          padding: 1px;
          background: linear-gradient(180deg,
            oklch(0.50 calc(0.06 * var(--accent-cm)) var(--accent-h) / 0.6),
            oklch(0.30 0.012 60 / 0.4) 40%,
            oklch(0.25 0.012 60 / 0.4));
          box-shadow:
            0 30px 80px -20px oklch(0 0 0 / 0.6),
            0 0 80px -20px var(--accent-glow);
        }
        /* Animated border beam (Magic UI style): a light comet that travels
           the border, masked to a thin ring and themed to the accent. */
        .prompt-beam {
          position: absolute;
          inset: 0;
          border-radius: inherit;
          padding: 1.5px;
          pointer-events: none;
          z-index: 2;
          contain: paint;
          -webkit-mask:
            linear-gradient(#000 0 0) content-box,
            linear-gradient(#000 0 0);
          mask:
            linear-gradient(#000 0 0) content-box,
            linear-gradient(#000 0 0);
          -webkit-mask-composite: xor;
          mask-composite: exclude;
          overflow: hidden;
        }
        .prompt-beam::before {
          content: "";
          position: absolute;
          width: 90px;
          aspect-ratio: 1;
          offset-path: rect(0 auto auto 0 round 28px);
          offset-distance: 0%;
          offset-anchor: 50% 50%;
          /* Force GPU hardware acceleration and prevent variable re-evals */
          will-change: transform, offset-distance;
          transform: translate3d(0, 0, 0);
          background: linear-gradient(
            90deg,
            transparent,
            var(--accent) 38%,
            oklch(0.95 calc(0.05 * var(--accent-cm)) var(--accent-h)) 50%,
            var(--accent) 62%,
            transparent
          );
          filter: blur(3px);
          animation: prompt-beam 11s linear infinite;
        }
        @keyframes prompt-beam {
          to {
            offset-distance: 100%;
          }
        }
        @media (prefers-reduced-motion: reduce) {
          .prompt-beam::before {
            animation: none;
            opacity: 0;
          }
        }
        .prompt-inner {
          background: linear-gradient(180deg, oklch(0.19 0.009 60 / 0.92), oklch(0.17 0.008 60 / 0.92));
          border-radius: calc(var(--r-xl) - 1px);
          padding: 18px 18px 14px;
          backdrop-filter: blur(20px);
        }
        .prompt textarea {
          width: 100%;
          min-height: 96px;
          background: transparent;
          border: 0;
          color: var(--fg);
          font: 16px/1.45 var(--font-sans);
          resize: none;
          outline: none;
          padding: 6px 4px;
        }
        .prompt textarea::placeholder {
          color: var(--fg-faint);
        }
        .prompt-typed {
          /* simulated placeholder w/ blinking caret */
          position: absolute;
          top: 24px; left: 22px; right: 22px;
          pointer-events: none;
          color: var(--fg-faint);
          font: 16px/1.45 var(--font-sans);
          text-align: left;
        }
        .prompt-typed::after {
          content: "";
          display: inline-block;
          width: 8px; height: 18px;
          background: var(--accent);
          vertical-align: -3px;
          margin-left: 2px;
          animation: blink 1s steps(2) infinite;
          box-shadow: 0 0 12px var(--accent-glow);
        }
        @keyframes blink { 50% { opacity: 0; } }

        .prompt-bar {
          display: flex; align-items: center; justify-content: space-between;
          gap: 14px;
          margin-top: 6px;
          padding-top: 12px;
          border-top: 1px solid var(--hairline);
        }
        .prompt-tools {
          display: flex; gap: 6px; color: var(--fg-mute);
        }
        .prompt-tool {
          display: inline-flex; align-items: center; gap: 6px;
          padding: 6px 10px; border-radius: 999px;
          font-size: 12px;
          color: var(--fg-mute);
          border: 1px solid transparent;
          transition: border-color .15s, color .15s;
        }
        .prompt-tool:hover { color: var(--fg-dim); border-color: var(--hairline); }
        .prompt-send {
          display: inline-flex; align-items: center; gap: 8px;
          height: 36px; padding: 0 14px 0 16px;
          border-radius: 999px;
          background: var(--accent);
          color: var(--accent-fg);
          font-weight: 500; font-size: 14px;
          box-shadow: 0 0 0 1px oklch(0.84 calc(0.16 * var(--accent-cm)) var(--accent-h) / 0.5) inset, 0 8px 28px -8px var(--accent-glow);
          transition: transform .12s;
        }
        .prompt-send:hover { transform: translateY(-1px); }

        .chips {
          display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
          margin-top: 12px;
          font-size: 13px;
        }
        .chip {
          padding: 7px 14px;
          border-radius: 999px;
          border: 1px solid var(--hairline);
          background: oklch(0.20 0.009 60 / 0.4);
          color: var(--fg-dim);
          font-family: var(--font-sans);
          transition: border-color .15s, color .15s, transform .12s;
        }
        .chip:hover { border-color: var(--hairline-2); color: var(--fg); transform: translateY(-1px); }

        .hero-cta {
          display: flex; gap: 12px; align-items: center;
          margin-top: 10px;
          flex-wrap: wrap; justify-content: center;
        }

        .live-pill {
          display: inline-flex; align-items: center; gap: 8px;
          padding: 6px 12px; border-radius: 999px;
          background: oklch(0.78 0.16 155 / 0.10);
          border: 1px solid oklch(0.78 0.16 155 / 0.35);
          color: oklch(0.85 0.14 155);
          font-family: var(--font-mono);
          font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
        }
        .live-pill .dot {
          width: 6px; height: 6px; border-radius: 50%;
          background: oklch(0.78 0.16 155);
          box-shadow: 0 0 0 0 oklch(0.78 0.16 155 / 0.6);
          animation: pulse 2s ease-out infinite;
        }
        @keyframes pulse {
          0% { box-shadow: 0 0 0 0 oklch(0.78 0.16 155 / 0.6); }
          70% { box-shadow: 0 0 0 8px oklch(0.78 0.16 155 / 0); }
          100% { box-shadow: 0 0 0 0 oklch(0.78 0.16 155 / 0); }
        }

        @media (max-width: 760px) {
          .prompt textarea { min-height: 80px; }
          .prompt-typed { top: 22px; left: 20px; right: 20px; font-size: 15px; }
          .prompt textarea { font-size: 15px; }
          .prompt-tools { display: none; }
        }
      `}</style>

      {/* ambient glows behind hero */}
      <Glow
        color="oklch(0.74 calc(0.175 * var(--accent-cm)) var(--accent-h) / 0.30)"
        size={900}
        style={{ top: "-200px", left: "50%", transform: "translateX(-50%)" }}
      />
      <Glow
        color="oklch(0.45 calc(0.10 * var(--accent-cm)) var(--accent-h) / 0.30)"
        size={600}
        style={{ top: "20%", left: "-200px" }}
      />
      <Glow
        color="oklch(0.45 calc(0.10 * var(--accent-cm)) var(--accent-h) / 0.20)"
        size={500}
        style={{ top: "30%", right: "-150px" }}
      />

      <div className="wrap hero-inner">
        <span className="live-pill">
          <span className="dot" /> Live from minute one
        </span>

        {variant === "promise" ? (
          <>
            <h1 className="hero-quote">
              Every small business deserves their{" "}
              <span className="mark">perfect tool</span>.
            </h1>
            <div className="hero-attribution mono">
              idea → live → marketed → customers
            </div>
            <p className="hero-sub">
              Vibn is the platform for people building them.
            </p>
          </>
        ) : (
          <>
            <h1 className="hero-quote">
              <span className="mark" style={{ fontSize: "0.95em" }}>
                "
              </span>
              I built my product,
              <br />
              now what
              <span className="mark" style={{ fontSize: "0.95em" }}>
                ?"
              </span>
            </h1>
            <div className="hero-attribution mono">
              posted 2 hours ago · r/SideProject
            </div>
            <p className="hero-sub">
              <b>Keep vibing.</b> All the way to launch.
              <br />
              Your AI handles the technical stuff, puts your idea online, and
              helps you find your first customers.
            </p>
          </>
        )}

        {/* Prompt */}
        <div className="prompt">
          <div className="prompt-frame">
            <span className="prompt-beam" aria-hidden="true" />
            <div className="prompt-inner">
              <div style={{ position: "relative" }}>
                <textarea
                  ref={taRef}
                  value={text}
                  onChange={(e) => setText(e.target.value)}
                  onKeyDown={(e) => {
                    if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) submit();
                  }}
                  placeholder=""
                  aria-label="Describe what you want to build"
                />
                {text.length === 0 && (
                  <div className="prompt-typed">{placeholder}</div>
                )}
              </div>
              <div className="prompt-bar">
                <div className="prompt-tools">
                  <button
                    className="prompt-tool"
                    type="button"
                    title="Attach a screenshot"
                  >
                    <PromptIcon name="paperclip" /> Screenshot
                  </button>
                  <button
                    className="prompt-tool"
                    type="button"
                    title="Voice prompt"
                  >
                    <PromptIcon name="mic" /> Voice
                  </button>
                  <button
                    className="prompt-tool"
                    type="button"
                    title="Start from a template"
                  >
                    <PromptIcon name="grid" /> Templates
                  </button>
                </div>
                <button className="prompt-send" type="button" onClick={submit}>
                  Start building <Arrow size={13} />
                </button>
              </div>
            </div>
          </div>

          {/* Suggestion chips */}
          <div className="chips">
            {HERO_CHIPS.map((c) => (
              <button
                className="chip"
                type="button"
                key={c}
                onClick={() => useChip(c)}
              >
                {c}
              </button>
            ))}
          </div>
        </div>

        <div className="hero-cta">
          <button className="btn btn-primary" type="button" onClick={submit}>
            Start building free <Arrow />
          </button>
          <a href="#how" className="btn btn-ghost">
            See how it works
          </a>
        </div>

        <TrustStrip
          items={["No credit card", "No homework", "No new tools to learn"]}
        />
      </div>
    </header>
  );
}

function PromptIcon({ name }) {
  const props = {
    width: 13,
    height: 13,
    viewBox: "0 0 16 16",
    fill: "none",
    stroke: "currentColor",
    strokeWidth: 1.5,
    strokeLinecap: "round",
    strokeLinejoin: "round",
  };
  if (name === "paperclip")
    return (
      <svg {...props}>
        <path d="M11.5 6.5 6.6 11.4a2 2 0 1 1-2.8-2.8l5.4-5.4a3.5 3.5 0 1 1 5 5L8.6 13.7" />
      </svg>
    );
  if (name === "mic")
    return (
      <svg {...props}>
        <rect x="6" y="2" width="4" height="8" rx="2" />
        <path d="M3.5 8a4.5 4.5 0 0 0 9 0M8 13v2" />
      </svg>
    );
  if (name === "grid")
    return (
      <svg {...props}>
        <rect x="2.5" y="2.5" width="4.5" height="4.5" />
        <rect x="9" y="2.5" width="4.5" height="4.5" />
        <rect x="2.5" y="9" width="4.5" height="4.5" />
        <rect x="9" y="9" width="4.5" height="4.5" />
      </svg>
    );
  return null;
}

// --- wall.jsx ---
// The Wall — recreates the moment the vibe dies. Faux chat from a "generic" AI
// coding tool that hands back a homework list. Ends on the punchline.

function Wall() {
  return (
    <section className="section wall" id="the-wall">
      <style>{`
        .wall { padding-block: clamp(60px, 9vh, 110px); }
        .wall-head { text-align: center; max-width: 760px; margin: 0 auto 56px; }
        .wall-title {
          font-size: clamp(36px, 4.8vw, 64px);
          font-weight: 500; letter-spacing: -0.025em; line-height: 1.02;
          text-wrap: balance;
        }
        .wall-title em {
          font-style: normal;
          color: var(--accent);
          text-shadow: 0 0 30px var(--accent-glow);
        }
        .wall-sub {
          margin-top: 20px;
          color: var(--fg-mute);
          font-size: 17px;
          text-wrap: balance;
        }

        /* Faux app window */
        .window {
          max-width: 880px; margin: 0 auto;
          position: relative;
          border-radius: 16px;
          background: oklch(0.165 0.008 60 / 0.85);
          border: 1px solid var(--hairline);
          box-shadow: 0 30px 80px -20px oklch(0 0 0 / 0.6);
          overflow: hidden;
          backdrop-filter: blur(10px);
        }
        .window-bar {
          display: flex; align-items: center; gap: 14px;
          padding: 11px 14px;
          background: oklch(0.20 0.009 60 / 0.85);
          border-bottom: 1px solid var(--hairline);
          font-family: var(--font-mono);
          font-size: 12px;
          color: var(--fg-mute);
        }
        .traffic { display: flex; gap: 7px; }
        .traffic i {
          width: 11px; height: 11px; border-radius: 50%;
          background: oklch(0.40 0.01 60);
        }
        .window-name {
          margin-left: 8px; color: var(--fg-faint);
          letter-spacing: 0.02em;
        }
        .window-tag {
          margin-left: auto;
          padding: 2px 8px; border-radius: 4px;
          background: oklch(0.25 0.01 60); color: var(--fg-faint);
          font-size: 11px;
        }

        .chat { padding: 24px; display: flex; flex-direction: column; gap: 16px; }
        .msg {
          display: flex; gap: 12px; align-items: flex-start;
          font-size: 14.5px; line-height: 1.55;
        }
        .avatar {
          width: 26px; height: 26px; border-radius: 7px;
          display: grid; place-items: center;
          font-family: var(--font-mono); font-size: 11px; font-weight: 600;
          flex-shrink: 0;
        }
        .avatar.user { background: oklch(0.28 0.01 60); color: var(--fg-dim); }
        .avatar.ai   { background: oklch(0.30 0.02 250); color: oklch(0.85 0.06 250); }
        .msg-body { flex: 1; min-width: 0; }
        .msg-name {
          font-family: var(--font-mono);
          font-size: 11px;
          color: var(--fg-faint);
          letter-spacing: 0.04em;
          margin-bottom: 3px;
          text-transform: uppercase;
        }
        .msg p { margin: 0; color: var(--fg-dim); }
        .msg.user .msg-body p { color: var(--fg); }

        .homework-intro { color: var(--fg-dim); }
        .homework-list {
          list-style: none; padding: 0; margin: 12px 0 0;
          display: flex; flex-direction: column; gap: 8px;
          counter-reset: hw;
        }
        .homework-list li {
          counter-increment: hw;
          display: flex; gap: 12px; align-items: flex-start;
          padding: 12px 14px;
          background: oklch(0.20 0.009 60);
          border: 1px solid var(--hairline);
          border-radius: 10px;
          color: var(--fg-dim);
          font-size: 14px;
          transition: opacity .3s, filter .3s;
        }
        .homework-list li::before {
          content: counter(hw, decimal-leading-zero);
          font-family: var(--font-mono);
          font-size: 11px;
          color: var(--fg-faint);
          padding: 1px 6px;
          background: oklch(0.16 0.008 60);
          border-radius: 4px;
          flex-shrink: 0;
        }
        .homework-list li .ext {
          margin-left: auto;
          font-family: var(--font-mono);
          font-size: 11px;
          color: var(--fg-faint);
          padding: 1px 7px;
          border: 1px solid var(--hairline);
          border-radius: 4px;
          flex-shrink: 0;
        }
        .homework-list li b { color: var(--fg); font-weight: 500; }

        /* desaturate the bottom of the list to convey overload */
        .homework-list li:nth-child(4) { opacity: .82; }
        .homework-list li:nth-child(5) { opacity: .65; }
        .homework-list li:nth-child(6) { opacity: .48; filter: blur(.2px); }
        .homework-list li:nth-child(7) { opacity: .34; filter: blur(.4px); }
        .homework-list li:nth-child(8) { opacity: .22; filter: blur(.7px); }
        .homework-fade {
          margin-top: -10px; padding-top: 30px;
          background: linear-gradient(180deg, transparent, oklch(0.165 0.008 60 / 0.85));
          font-family: var(--font-mono);
          font-size: 11px;
          color: var(--fg-faint);
          text-align: center;
        }

        .typing {
          display: inline-flex; gap: 3px; align-items: center;
          padding: 4px 0;
          color: var(--fg-mute);
        }
        .typing i {
          width: 5px; height: 5px; border-radius: 50%; background: var(--fg-mute);
          animation: bounce 1.2s infinite ease-in-out;
        }
        .typing i:nth-child(2) { animation-delay: .15s; }
        .typing i:nth-child(3) { animation-delay: .3s; }
        @keyframes bounce {
          0%, 80%, 100% { transform: translateY(0); opacity: .5; }
          40% { transform: translateY(-3px); opacity: 1; }
        }

        /* The punchline beat */
        .punchline {
          margin-top: 56px;
          text-align: center;
        }
        .punchline-text {
          font-size: clamp(28px, 3.4vw, 42px);
          font-weight: 500; letter-spacing: -0.022em;
          color: var(--fg-mute);
          line-height: 1.2; text-wrap: balance;
        }
        .punchline-text em {
          font-style: italic;
          color: var(--fg);
        }
        .punchline-divider {
          width: 1px; height: 56px;
          background: linear-gradient(180deg, transparent, var(--hairline), transparent);
          margin: 0 auto 28px;
        }
      `}</style>

      <div className="wrap">
        <div className="wall-head">
          <Eyebrow>The wall</Eyebrow>
          <h2 className="wall-title" style={{ marginTop: 18 }}>
            Keep vibing. <em>All the way to launch</em>.
          </h2>
          <p className="wall-sub">
            Your AI handles the technical stuff, puts your idea online, and
            helps you find customers. No extra tools. No headaches.
          </p>
          <h3
            style={{
              marginTop: 56,
              fontSize: "clamp(24px, 3vw, 32px)",
              fontWeight: 500,
              letterSpacing: "-0.02em",
            }}
          >
            Does this look familiar?
          </h3>
        </div>

        <div className="window">
          <div className="window-bar">
            <div className="traffic">
              <i />
              <i />
              <i />
            </div>
            <span className="window-name">untitled-project · main</span>
            <span className="window-tag">generic ai coder · chat</span>
          </div>

          <div className="chat">
            <div className="msg user">
              <div className="avatar user">YOU</div>
              <div className="msg-body">
                <div className="msg-name">You · just now</div>
                <p>
                  okay it works!! how do i put this online so my customers can
                  use it?
                </p>
              </div>
            </div>

            <div className="msg ai">
              <div className="avatar ai">AI</div>
              <div className="msg-body">
                <div className="msg-name">Generic AI · just now</div>
                <p className="homework-intro">
                  Great job 🎉 Your app is running locally. To take it live,
                  you'll need to set a few things up first:
                </p>
                <ol className="homework-list">
                  <li>
                    <b>Sign up for Supabase</b> and create a project for your
                    database.<span className="ext">↗ external</span>
                  </li>
                  <li>
                    <b>Configure authentication</b> with Supabase Auth or Clerk
                    — pick one.<span className="ext">↗ external</span>
                  </li>
                  <li>
                    <b>Create a GitHub repo</b>, commit your code, and push it.
                    <span className="ext">↗ external</span>
                  </li>
                  <li>
                    <b>Deploy to Vercel</b>: connect repo, configure framework
                    preset.<span className="ext">↗ external</span>
                  </li>
                  <li>
                    <b>Add environment variables</b> for your API keys and DB
                    url in the Vercel dashboard.
                    <span className="ext">↗ external</span>
                  </li>
                  <li>
                    <b>Set up DNS</b> for your custom domain and verify
                    nameservers with your registrar.
                    <span className="ext">↗ external</span>
                  </li>
                  <li>
                    <b>Configure SSL / TLS certificates</b> for HTTPS (or use
                    Vercel's automatic provisioning).
                    <span className="ext">↗ external</span>
                  </li>
                  <li>
                    <b>Set up Stripe</b> if you want to take payments, and
                    configure webhooks.<span className="ext">↗ external</span>
                  </li>
                </ol>
                <div className="homework-fade">↓ 23 more steps</div>
              </div>
            </div>

            <div className="msg user">
              <div className="avatar user">YOU</div>
              <div className="msg-body">
                <div className="msg-name">You · now</div>
                <p style={{ color: "var(--fg-mute)" }}>
                  <span className="typing">
                    <i />
                    <i />
                    <i />
                  </span>
                </p>
              </div>
            </div>
          </div>
        </div>

        <div className="punchline">
          <div className="punchline-divider"></div>
          <p className="punchline-text">
            And just like that — <em>the vibe is gone.</em>
          </p>
        </div>
      </div>
    </section>
  );
}

// --- crossed.jsx ---
// Crossed-out list — technical terms struck through, ending in "Your AI handles
// all of it. You just keep building."

const CROSSED_TERMS = [
  "Databases",
  "Auth providers",
  "GitHub",
  "Hosting",
  "API keys",
  "Environment variables",
  "Deployment",
  "Backend code",
  "Servers",
  "DNS records",
  "SSL certificates",
  "CORS errors",
  "Webhooks",
  "Build pipelines",
  "package.json",
  "npm install",
];

function CrossedOut() {
  return (
    <section className="section crossed">
      <style>{`
        .crossed { padding-block: clamp(70px, 10vh, 130px); }
        .crossed-head { text-align: center; max-width: 760px; margin: 0 auto 56px; }
        .crossed-title {
          font-size: clamp(36px, 4.8vw, 64px);
          font-weight: 500; letter-spacing: -0.025em; line-height: 1.02;
          text-wrap: balance;
        }
        .crossed-sub {
          margin-top: 18px;
          color: var(--fg-mute);
          font-size: 17px;
        }

        .crossed-wall {
          display: flex; flex-wrap: wrap; gap: 10px 14px;
          justify-content: center;
          max-width: 880px; margin: 0 auto;
        }
        .crossed-item {
          position: relative;
          font-family: var(--font-mono);
          font-size: clamp(15px, 1.7vw, 22px);
          font-weight: 400;
          color: var(--fg-mute);
          padding: 8px 14px;
          border-radius: 8px;
          background: oklch(0.20 0.009 60 / 0.45);
          border: 1px solid var(--hairline);
          letter-spacing: 0.005em;
          overflow: hidden;
        }
        .crossed-item::after {
          content: "";
          position: absolute;
          left: 8px; right: 8px;
          top: 50%;
          height: 2px;
          transform: translateY(-50%) rotate(-1deg);
          background: var(--accent);
          box-shadow: 0 0 12px var(--accent-glow);
          border-radius: 2px;
          opacity: 0;
          animation: strike 0.6s cubic-bezier(.7,.1,.3,1) forwards;
          animation-delay: var(--delay, 0s);
        }
        .crossed-item .term {
          opacity: 1;
          animation: fade 0.4s ease forwards;
          animation-delay: var(--delay, 0s);
          display: inline-block;
        }
        @keyframes strike {
          from { opacity: 0; transform: translateY(-50%) rotate(-1deg) scaleX(0); transform-origin: left center; }
          to   { opacity: 1; transform: translateY(-50%) rotate(-1deg) scaleX(1); transform-origin: left center; }
        }
        @keyframes fade {
          to { opacity: 0.5; }
        }

        .crossed-closer {
          margin-top: 56px;
          text-align: center;
          font-size: clamp(24px, 3vw, 36px);
          font-weight: 500; letter-spacing: -0.02em;
          line-height: 1.18;
          text-wrap: balance;
          max-width: 760px; margin-inline: auto; margin-top: 56px;
        }
        .crossed-closer .accent { color: var(--accent); }
        .crossed-closer .sep {
          display: block; width: 48px; height: 1px;
          background: var(--hairline);
          margin: 28px auto;
        }
      `}</style>

      <div className="wrap">
        <div className="crossed-head">
          <Eyebrow>What you don't have to learn</Eyebrow>
          <h2 className="crossed-title" style={{ marginTop: 18 }}>
            All the stuff that made you give up last time.
          </h2>
          <p className="crossed-sub">Forget every word on this list.</p>
        </div>

        <div className="crossed-wall">
          {CROSSED_TERMS.map((term, i) => (
            <span
              className="crossed-item"
              key={term}
              style={{ "--delay": `${0.12 + i * 0.06}s` }}
            >
              <span className="term">{term}</span>
            </span>
          ))}
        </div>

        <p className="crossed-closer">
          Your AI handles <span className="accent">all of it</span>.
          <span className="sep" />
          You just keep building.
        </p>
      </div>
    </section>
  );
}

// --- journey.jsx ---
// The Journey — 4 steps from idea → first 100 customers. A visual marker shows
// where most tools stop. Each step shows a tiny "demo" snippet of what Vibn does.

const JOURNEY_STEPS = [
  {
    num: "01",
    title: "You describe it.",
    sub: "The AI builds it.",
    body: "Talk to it like you'd talk to a friend who codes. It builds the screens, the buttons, the logic — whatever your idea needs.",
    demo: "describe",
  },
  {
    num: "02",
    title: "It goes live.",
    sub: "The AI puts it online.",
    body: "Logins, saving your stuff, hosting — handled. You get a live link from minute one. Share it. Show your friends. It just works.",
    demo: "live",
  },
  {
    num: "03",
    title: "It gets seen.",
    sub: "The AI markets it.",
    body: "Posts, emails, social — written, scheduled, and shipped on autopilot. The tone matches your brand because you trained it talking to your AI.",
    demo: "seen",
  },
  {
    num: "04",
    title: "It gets customers.",
    sub: "Your first 100.",
    body: "Through our Google partnership, Vibn helps the right people find your product when they're searching for what you built.",
    demo: "customers",
  },
];

function Journey() {
  return (
    <section className="section journey" id="how">
      <style>{`
        .journey { padding-block: clamp(80px, 11vh, 140px); }
        .journey-head { text-align: center; max-width: 820px; margin: 0 auto 64px; }
        .journey-title {
          font-size: clamp(36px, 4.8vw, 64px);
          font-weight: 500; letter-spacing: -0.025em; line-height: 1.02;
          text-wrap: balance;
        }
        .journey-title .accent { color: var(--accent); }
        .journey-sub {
          margin-top: 20px;
          color: var(--fg-mute); font-size: 17px;
          text-wrap: balance;
        }

        .journey-grid {
          display: grid;
          grid-template-columns: repeat(4, 1fr);
          gap: 16px;
          position: relative;
        }
        @media (max-width: 1080px) { .journey-grid { grid-template-columns: repeat(2, 1fr); } }
        @media (max-width: 640px)  { .journey-grid { grid-template-columns: 1fr; } }

        .step {
          position: relative;
          padding: 24px 24px 0;
          border-radius: 16px;
          background: linear-gradient(180deg, oklch(0.20 0.009 60 / 0.55), oklch(0.17 0.008 60 / 0.55));
          border: 1px solid var(--hairline);
          display: flex; flex-direction: column;
          min-height: 380px;
          overflow: hidden;
          isolation: isolate;
        }
        .step::before {
          /* Top accent line that varies by step */
          content: "";
          position: absolute; top: 0; left: 0; right: 0; height: 1px;
          background: linear-gradient(90deg, transparent, var(--accent) 50%, transparent);
          opacity: 0;
        }
        .step.active::before { opacity: .7; }
        .step.stopped {
          opacity: 0.46;
        }
        .step.stopped::after {
          content: "";
          position: absolute; inset: 0;
          background: linear-gradient(180deg, transparent 40%, oklch(0.155 0.008 60 / 0.6));
          pointer-events: none;
        }

        .step-num {
          font-family: var(--font-mono);
          font-size: 11px;
          color: var(--fg-faint);
          letter-spacing: 0.08em;
        }
        .step-title {
          margin-top: 12px;
          font-size: 22px; font-weight: 500;
          letter-spacing: -0.018em;
        }
        .step-sub {
          margin-top: 4px;
          color: var(--accent);
          font-size: 15px;
          font-weight: 500;
        }
        .step.stopped .step-sub { color: var(--fg-mute); }
        .step-body {
          margin-top: 12px;
          color: var(--fg-dim);
          font-size: 14px;
          line-height: 1.55;
        }
        .step-demo {
          margin-top: auto;
          margin-inline: -24px; margin-bottom: 0;
          padding: 16px 18px;
          border-top: 1px solid var(--hairline);
          background: oklch(0.16 0.008 60 / 0.6);
          font-family: var(--font-mono);
          font-size: 12px; line-height: 1.55;
          color: var(--fg-dim);
          min-height: 116px;
          display: flex; flex-direction: column;
          gap: 7px;
        }

        /* Visual marker: where other tools stop */
        .stop-marker {
          position: absolute;
          left: calc(50% - 8px);
          top: 0; bottom: 0;
          width: 16px;
          display: flex; flex-direction: column; align-items: center;
          pointer-events: none;
          z-index: 2;
        }
        @media (max-width: 1080px) { .stop-marker { display: none; } }
        .stop-marker .line {
          flex: 1; width: 1px;
          background: repeating-linear-gradient(180deg, var(--accent) 0 6px, transparent 6px 12px);
          opacity: .7;
        }
        .stop-label {
          font-family: var(--font-mono);
          font-size: 10px;
          letter-spacing: 0.12em;
          text-transform: uppercase;
          color: var(--accent);
          background: var(--bg);
          padding: 6px 12px;
          border-radius: 999px;
          border: 1px solid oklch(0.74 calc(0.175 * var(--accent-cm)) var(--accent-h) / 0.5);
          white-space: nowrap;
          box-shadow: 0 0 24px var(--accent-glow);
          transform: translateY(-1px);
        }

        /* Demo blocks */
        .demo-row { display: flex; gap: 8px; align-items: flex-start; }
        .demo-tag {
          font-family: var(--font-mono); font-size: 10px;
          padding: 1px 6px; border-radius: 4px;
          color: var(--fg-faint);
          background: oklch(0.22 0.01 60);
          letter-spacing: 0.04em;
          flex-shrink: 0;
          margin-top: 1px;
        }
        .demo-tag.you { color: oklch(0.85 0.06 250); background: oklch(0.28 0.04 250); }
        .demo-tag.ai  { color: var(--accent); background: oklch(0.35 calc(0.10 * var(--accent-cm)) var(--accent-h) / 0.4); }
        .demo-line { color: var(--fg-dim); }
        .demo-ok { color: var(--ok); }
        .demo-host {
          display: inline-flex; align-items: center; gap: 6px;
          color: var(--fg-dim);
        }
        .demo-host i {
          width: 6px; height: 6px; border-radius: 50%;
          background: var(--ok);
          box-shadow: 0 0 6px oklch(0.78 0.16 155 / 0.6);
        }
        .demo-progress {
          height: 4px; border-radius: 999px;
          background: oklch(0.25 0.01 60);
          overflow: hidden;
          position: relative;
        }
        .demo-progress span {
          position: absolute; inset: 0;
          background: var(--accent);
          width: 64%;
          box-shadow: 0 0 8px var(--accent-glow);
        }
        .demo-customer {
          display: flex; align-items: center; gap: 8px;
        }
        .demo-customer .av {
          width: 16px; height: 16px; border-radius: 50%;
          flex-shrink: 0;
        }
        .demo-num {
          font-family: var(--font-mono); font-size: 22px;
          color: var(--accent);
          letter-spacing: -0.02em;
          font-weight: 500;
        }
        .demo-num small {
          color: var(--fg-mute); font-size: 11px;
          font-weight: 400;
          margin-left: 4px;
        }

        .journey-foot {
          margin-top: 48px;
          text-align: center;
          color: var(--fg-mute);
          font-size: 15px;
          text-wrap: balance;
        }
        .journey-foot b {
          color: var(--fg);
          font-weight: 500;
        }
      `}</style>

      <div className="wrap">
        <div className="journey-head">
          <Eyebrow>The journey</Eyebrow>
          <h2 className="journey-title" style={{ marginTop: 18 }}>
            From idea to first 100 customers.
            <br />
            <span className="accent">In one chat.</span>
          </h2>
          <p className="journey-sub">
            Other tools take you to step two and wave goodbye. Vibn keeps
            building with you.
          </p>
        </div>

        <div className="journey-grid">
          {/* "Where everyone else stops" marker, sits over the gap between cards 2 and 3 */}
          <div className="stop-marker" style={{ left: "calc(50% - 1px)" }}>
            <div className="line" />
            <span className="stop-label">↑ Where every other tool stops</span>
            <div className="line" />
          </div>

          {JOURNEY_STEPS.map((step, i) => (
            <StepCard key={step.num} step={step} stopped={i >= 2} />
          ))}
        </div>

        <p className="journey-foot">
          <b>One tool. One chat.</b> From "wouldn't it be cool if…" to{" "}
          <b>real customers paying you money.</b>
        </p>
      </div>
    </section>
  );
}

function StepCard({ step, stopped }) {
  return (
    <div className={`step${stopped ? "" : " active"}`}>
      <div>
        <div className="step-num">{step.num}</div>
        <h3 className="step-title">{step.title}</h3>
        <div className="step-sub">{step.sub}</div>
        <p className="step-body">{step.body}</p>
      </div>
      <StepDemo demo={step.demo} />
    </div>
  );
}

function StepDemo({ demo }) {
  if (demo === "describe") {
    return (
      <div className="step-demo">
        <div className="demo-row">
          <span className="demo-tag you">YOU</span>
          <span className="demo-line">
            build a booking site for my dog grooming biz
          </span>
        </div>
        <div className="demo-row">
          <span className="demo-tag ai">VIBN</span>
          <span className="demo-line">on it — designing screens…</span>
        </div>
        <div className="demo-row" style={{ alignItems: "center" }}>
          <span className="demo-tag ai">VIBN</span>
          <span className="demo-ok">✓ booking flow ready</span>
        </div>
      </div>
    );
  }
  if (demo === "live") {
    return (
      <div className="step-demo">
        <div className="demo-row" style={{ alignItems: "center" }}>
          <span className="demo-tag ai">VIBN</span>
          <span className="demo-line">put it online</span>
        </div>
        <div className="demo-progress">
          <span />
        </div>
        <div
          className="demo-row"
          style={{ alignItems: "center", marginTop: 2 }}
        >
          <span className="demo-host">
            <i /> pawsandposh.vibn.app
          </span>
        </div>
        <div className="demo-row">
          <span className="demo-ok">✓ logins · ✓ saving · ✓ live</span>
        </div>
      </div>
    );
  }
  if (demo === "seen") {
    return (
      <div className="step-demo">
        <div className="demo-row">
          <span className="demo-tag ai">VIBN</span>
          <span className="demo-line">
            draft a launch post for Instagram + email blast
          </span>
        </div>
        <div className="demo-row" style={{ color: "var(--fg-faint)" }}>
          ↳ scheduled for Tue 9:00 AM
        </div>
        <div className="demo-row" style={{ color: "var(--fg-faint)" }}>
          ↳ scheduled for Thu 6:00 PM
        </div>
        <div className="demo-row">
          <span className="demo-ok">✓ 3 channels on autopilot</span>
        </div>
      </div>
    );
  }
  if (demo === "customers") {
    return (
      <div className="step-demo">
        <div className="demo-row" style={{ alignItems: "center" }}>
          <span className="demo-num">
            +47<small>this week</small>
          </span>
        </div>
        <div className="demo-customer">
          <span className="av" style={{ background: "oklch(0.55 0.14 35)" }} />
          <span className="av" style={{ background: "oklch(0.55 0.14 260)" }} />
          <span className="av" style={{ background: "oklch(0.55 0.14 155)" }} />
          <span className="av" style={{ background: "oklch(0.55 0.14 80)" }} />
          <span style={{ color: "var(--fg-mute)" }}>found you via Google</span>
        </div>
        <div className="demo-row">
          <span className="demo-ok">✓ tracking toward 100</span>
        </div>
      </div>
    );
  }
  return null;
}

// --- audience.jsx ---
// Who it's for — three audience cards, each with a Reddit-style customer quote
// and Vibn's answer.

const AUDIENCE = [
  {
    label: "Small business owners",
    icon: "shop",
    quote:
      "I'm paying $312/month for software that does 60% of what I need and zero of the rest.",
    source: "u/coffeeshop_owner · r/smallbusiness",
    answer:
      "Build the tool that actually fits your shop — exactly your workflow, no monthly fee bleed.",
  },
  {
    label: "Freelancers building for clients",
    icon: "spark",
    quote:
      "My client wants a quote tool. I can mock the frontend in a day. The backend? Two weeks I don't have.",
    source: "u/agency_of_one · r/freelance",
    answer:
      "Deliver the whole thing — login, data, hosting — in the same chat where you built the screens.",
  },
  {
    label: "Anyone with an idea",
    icon: "spark2",
    quote:
      "I built the homepage in an afternoon. Then the AI told me to 'just deploy it' and I cried.",
    source: "u/first_time_builder · r/sideproject",
    answer:
      "No deploys. No GitHub. No fear. The thing you described is online, with logins, ready for users.",
  },
];

function Audience() {
  return (
    <section className="section audience">
      <style>{`
        .audience-head { text-align: center; max-width: 820px; margin: 0 auto 56px; }
        .audience-title {
          font-size: clamp(36px, 4.8vw, 64px);
          font-weight: 500; letter-spacing: -0.025em; line-height: 1.02;
          text-wrap: balance;
        }
        .audience-sub {
          margin-top: 20px;
          color: var(--fg-mute);
          font-size: 17px;
        }

        .audience-grid {
          display: grid;
          grid-template-columns: repeat(3, 1fr);
          gap: 18px;
        }
        @media (max-width: 1000px) { .audience-grid { grid-template-columns: 1fr; } }

        .a-card {
          position: relative;
          padding: 28px 26px 26px;
          border-radius: 18px;
          background: linear-gradient(180deg, oklch(0.20 0.009 60 / 0.55), oklch(0.17 0.008 60 / 0.55));
          border: 1px solid var(--hairline);
          display: flex; flex-direction: column;
          min-height: 380px;
          overflow: hidden;
        }
        .a-card::after {
          content: "";
          position: absolute;
          top: 0; left: 24px; right: 24px;
          height: 1px;
          background: linear-gradient(90deg, transparent, var(--accent), transparent);
          opacity: .6;
        }

        .a-icon {
          width: 40px; height: 40px;
          border-radius: 10px;
          display: grid; place-items: center;
          background: oklch(0.22 0.011 60);
          border: 1px solid var(--hairline);
          color: var(--accent);
          margin-bottom: 18px;
        }
        .a-label {
          font-size: 19px; font-weight: 500;
          letter-spacing: -0.015em;
          color: var(--fg);
        }

        .a-quote {
          margin: 18px 0 0;
          padding: 16px 18px;
          background: oklch(0.16 0.008 60 / 0.55);
          border-left: 2px solid var(--accent);
          border-radius: 4px 10px 10px 4px;
          font-style: italic;
          color: var(--fg-dim);
          font-size: 14.5px;
          line-height: 1.5;
          position: relative;
        }
        .a-source {
          margin-top: 8px;
          font-family: var(--font-mono);
          font-size: 11px;
          color: var(--fg-faint);
          letter-spacing: 0.02em;
        }

        .a-answer {
          margin-top: auto;
          padding-top: 22px;
          font-size: 15px;
          color: var(--fg);
          line-height: 1.5;
          display: flex; gap: 10px; align-items: flex-start;
        }
        .a-answer .label {
          font-family: var(--font-mono);
          font-size: 10px;
          letter-spacing: 0.12em;
          text-transform: uppercase;
          color: var(--accent);
          padding: 3px 7px;
          background: oklch(0.74 calc(0.175 * var(--accent-cm)) var(--accent-h) / 0.12);
          border: 1px solid oklch(0.74 calc(0.175 * var(--accent-cm)) var(--accent-h) / 0.4);
          border-radius: 4px;
          margin-top: 1px;
          flex-shrink: 0;
        }
      `}</style>

      <div className="wrap">
        <div className="audience-head">
          <Eyebrow>Who Vibn is for</Eyebrow>
          <h2 className="audience-title" style={{ marginTop: 18 }}>
            People who have an idea — not a stack.
          </h2>
          <p className="audience-sub">
            If you've ever felt this, Vibn was built for you.
          </p>
        </div>

        <div className="audience-grid">
          {AUDIENCE.map((a) => (
            <div className="a-card" key={a.label}>
              <div className="a-icon">
                <AudienceIcon name={a.icon} />
              </div>
              <div className="a-label">{a.label}</div>

              <div className="a-quote">
                "{a.quote}"<div className="a-source">— {a.source}</div>
              </div>

              <div className="a-answer">
                <span className="label">Vibn</span>
                <span>{a.answer}</span>
              </div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

function AudienceIcon({ name }) {
  const p = {
    width: 20,
    height: 20,
    viewBox: "0 0 20 20",
    fill: "none",
    stroke: "currentColor",
    strokeWidth: 1.5,
    strokeLinecap: "round",
    strokeLinejoin: "round",
  };
  if (name === "shop")
    return (
      <svg {...p}>
        <path d="M3.5 6.5h13l-1 9.5h-11l-1-9.5Z" />
        <path d="M7 6.5V5a3 3 0 0 1 6 0v1.5" />
      </svg>
    );
  if (name === "spark")
    return (
      <svg {...p}>
        <path d="M10 3v4M10 13v4M3 10h4M13 10h4M5.3 5.3l2.8 2.8M11.9 11.9l2.8 2.8M14.7 5.3l-2.8 2.8M8.1 11.9l-2.8 2.8" />
      </svg>
    );
  if (name === "spark2")
    return (
      <svg {...p}>
        <path d="M10 2.5v3M10 14.5v3M2.5 10h3M14.5 10h3" />
        <circle cx="10" cy="10" r="3" />
      </svg>
    );
  return null;
}

// --- closing.jsx ---
// Closing CTA + Footer.

function Closing() {
  return (
    <section className="section closing">
      <style>{`
        .closing {
          padding-block: clamp(100px, 14vh, 180px);
          position: relative; overflow: hidden;
          text-align: center;
        }
        .closing-glow {
          position: absolute; inset: 0;
          pointer-events: none;
        }
        .closing-inner {
          position: relative;
          max-width: 900px; margin: 0 auto;
        }
        .closing-title {
          font-size: clamp(40px, 6vw, 84px);
          font-weight: 500; letter-spacing: -0.03em;
          line-height: 1.02;
          text-wrap: balance;
        }
        .closing-title .accent { color: var(--accent); }
        .closing-title em {
          font-style: normal;
          background: linear-gradient(180deg, var(--accent), oklch(0.62 calc(0.18 * var(--accent-cm)) calc(var(--accent-h) - 17)));
          -webkit-background-clip: text;
          background-clip: text;
          color: transparent;
        }
        .closing-sub {
          margin-top: 28px;
          font-size: clamp(17px, 1.6vw, 21px);
          color: var(--fg-dim);
          text-wrap: balance;
          max-width: 640px; margin-inline: auto;
        }
        .closing-cta {
          margin-top: 36px;
          display: inline-flex; flex-direction: column; align-items: center; gap: 14px;
        }
        .closing-cta .btn { height: 56px; padding: 0 28px; font-size: 16px; }
        .closing-cta .row {
          display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
          justify-content: center;
        }
      `}</style>

      <Glow
        color="oklch(0.74 calc(0.175 * var(--accent-cm)) var(--accent-h) / 0.35)"
        size={1000}
        style={{ top: "20%", left: "50%", transform: "translateX(-50%)" }}
      />
      <Glow
        color="oklch(0.45 calc(0.10 * var(--accent-cm)) var(--accent-h) / 0.20)"
        size={600}
        style={{ bottom: "-200px", left: "50%", transform: "translateX(-50%)" }}
      />

      <div className="wrap closing-inner">
        <h2 className="closing-title">
          If you can <em>describe</em> it,
          <br />
          you can <em>build</em> it.
        </h2>
        <p className="closing-sub">
          And you can keep building it — all the way to customers.
          <br />
          No new tools. No homework. No going back to the wall.
        </p>

        <div className="closing-cta">
          <div className="row">
            <a href="/signup" className="btn btn-primary">
              Sign up <Arrow />
            </a>
            <a href="#how" className="btn btn-ghost">
              See how it works
            </a>
          </div>
          <TrustStrip
            items={["No credit card", "No homework", "No new tools to learn"]}
          />
        </div>
      </div>
    </section>
  );
}

export function Footer() {
  return (
    <footer className="vibn-footer">
      <style>{`
        .vibn-footer {
          position: relative;
          padding: 40px 0 32px;
          border-top: 1px solid var(--hairline);
          background: oklch(0.14 0.008 60);
        }
        .vibn-footer-inner {
          display: flex; align-items: flex-start; justify-content: space-between;
          gap: 32px;
          flex-wrap: wrap;
        }
        .vibn-footer-trust {
          display: flex; gap: 20px; align-items: center;
          flex-wrap: wrap;
          font-family: var(--font-mono);
          font-size: 12px;
          color: var(--fg-mute);
          letter-spacing: 0.03em;
        }
        .vibn-footer-trust .item {
          display: inline-flex; align-items: center; gap: 8px;
        }
        .vibn-footer-trust .sep { color: var(--fg-faint); }
        .vibn-footer-bottom {
          margin-top: 24px;
          padding-top: 20px;
          border-top: 1px solid var(--hairline);
          display: flex; justify-content: space-between; align-items: center;
          gap: 16px;
          flex-wrap: wrap;
          font-family: var(--font-mono);
          font-size: 11px;
          color: var(--fg-faint);
          letter-spacing: 0.04em;
        }
        .vibn-footer-links {
          display: flex; gap: 18px;
        }
        .vibn-footer-links a:hover { color: var(--fg-dim); }
      `}</style>
      <div className="wrap">
        <div className="vibn-footer-inner">
          <a href="/" style={{ display: "inline-flex" }}>
            <Logo />
          </a>
          <div className="vibn-footer-trust">
            <span className="item">🇨🇦 Built in Canada</span>
            <span className="sep">·</span>
            <span className="item">Your data stays safe</span>
            <span className="sep">·</span>
            <span className="item">No credit card to start</span>
          </div>
        </div>
        <div className="vibn-footer-bottom">
          <span>© 2026 Vibn Inc. · Made for makers, not engineers.</span>
          <div className="vibn-footer-links">
            <a href="#">Privacy</a>
            <a href="#">Terms</a>
            <a href="#">Status</a>
            <a href="#">Changelog</a>
          </div>
        </div>
      </div>
    </footer>
  );
}

// --- app.jsx ---
// App — composes the page. Includes the sticky nav, the success modal that
// appears when the user submits the hero prompt, and the Tweaks panel.

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/ {
  accent: ["#ff6b47", "#ffae9a", "#9c3a1f"],
  heroVariant: "promise",
  showStopMarker: true,
  showLivePill: false,
}; /*EDITMODE-END*/

const ACCENT_PRESETS = {
  coral: ["#ff6b47", "#ffae9a", "#9c3a1f"], // warm coral (default)
  amber: ["#ffb347", "#ffd9a3", "#9c6e1f"], // soft amber
  lime: ["#9ee649", "#d2f3a6", "#3f7a1c"], // electric lime
  violet: ["#b07cff", "#dabfff", "#5a2fa3"], // violet
};

function applyAccent(arr) {
  // arr[0] is the hero color we map to var(--accent); compute soft + glow + fg.
  const hero = arr[0];
  const soft = `${hero}24`; // 14% alpha
  const glow = `${hero}59`; // 35% alpha
  const root = document.documentElement;
  root.style.setProperty("--accent", hero);
  root.style.setProperty("--accent-soft", soft);
  root.style.setProperty("--accent-glow", glow);
  // Foreground on accent: derive a dark-on-accent for primary buttons.
  root.style.setProperty("--accent-fg", "#1a0f0a");
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [scrolled, setScrolled] = useState(false);
  const [showLaunch, setShowLaunch] = useState(null);

  useEffect(() => {
    applyAccent(t.accent);
  }, [t.accent]);

  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 8);
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  const handleStart = (prompt) => {
    setShowLaunch(prompt || "Build me a tool for my business.");
  };

  return (
    <>
      <Nav scrolled={scrolled} />
      <main>
        <Hero onStart={handleStart} variant={t.heroVariant} />
        <Wall />
        <CrossedOut />
        <Journey />
        <Audience />
        <Closing />
      </main>
      <Footer />

      {showLaunch !== null && (
        <LaunchModal prompt={showLaunch} onClose={() => setShowLaunch(null)} />
      )}

      <TweaksPanel title="Tweaks">
        <TweakSection label="Look">
          <TweakColor
            label="Accent"
            value={t.accent}
            options={[
              ACCENT_PRESETS.coral,
              ACCENT_PRESETS.amber,
              ACCENT_PRESETS.lime,
              ACCENT_PRESETS.violet,
            ]}
            onChange={(v) => setTweak("accent", v)}
          />
        </TweakSection>
        <TweakSection label="Hero">
          <TweakRadio
            label="Headline"
            value={t.heroVariant}
            options={[
              { value: "quote", label: "Reddit quote" },
              { value: "promise", label: "The promise" },
            ]}
            onChange={(v) => setTweak("heroVariant", v)}
          />
          <TweakToggle
            label="Live pill"
            value={t.showLivePill}
            onChange={(v) => setTweak("showLivePill", v)}
          />
        </TweakSection>
        <TweakSection label="Journey">
          <TweakToggle
            label="Show 'where others stop' marker"
            value={t.showStopMarker}
            onChange={(v) => setTweak("showStopMarker", v)}
          />
        </TweakSection>
      </TweaksPanel>

      {/* Tweak-driven CSS overrides */}
      <style>{`
        ${t.showLivePill ? "" : ".live-pill { display: none !important; }"}
        ${t.showStopMarker ? "" : ".stop-marker { display: none !important; }"}
      `}</style>
    </>
  );
}

export function Nav({ scrolled = false }: { scrolled?: boolean }) {
  return (
    <nav className={`nav${scrolled ? " scrolled" : ""}`}>
      <div className="wrap nav-inner">
        <a href="/" style={{ display: "inline-flex" }}>
          <Logo />
        </a>
        <div className="nav-links">
          <a href="/#how">How it works</a>
          <a href="/mission">Our Mission</a>
          <a href="/#templates">Templates</a>
          <a href="/#pricing">Pricing</a>
        </div>
        <div className="nav-cta">
          <a
            href="/signin"
            className="btn btn-ghost"
            style={{ display: "inline-flex" }}
          >
            Sign in
          </a>
          <a href="/signup" className="btn btn-primary">
            Sign up <Arrow size={12} />
          </a>
        </div>
      </div>
    </nav>
  );
}

// Modal that fires when the user submits the hero prompt. Reassures them their
// vibe will be honored — playfully sells the rest of the flow.
function LaunchModal({ prompt, onClose }) {
  useEffect(() => {
    const onKey = (e) => {
      if (e.key === "Escape") onClose();
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  // Preserve prompt for onboarding seeding (T12)
  useEffect(() => {
    if (typeof window !== "undefined" && prompt) {
      try {
        localStorage.setItem("vibn:firstName", prompt);
      } catch (err) {
        console.error("Failed to save hero prompt to localStorage:", err);
      }
    }
  }, [prompt]);

  const [step, setStep] = useState(0);
  useEffect(() => {
    if (step >= 4) return undefined;
    const t = setTimeout(() => setStep(step + 1), 700);
    return () => clearTimeout(t);
  }, [step]);

  const [redirectCount, setRedirectCount] = useState(3);
  useEffect(() => {
    if (step < 4) return undefined;
    if (redirectCount <= 0) {
      window.location.href = "/signup";
      return undefined;
    }
    const t = setTimeout(() => setRedirectCount(redirectCount - 1), 1000);
    return () => clearTimeout(t);
  }, [step, redirectCount]);

  return (
    <div className="modal-backdrop" onClick={onClose}>
      <style>{`
        .modal-backdrop {
          position: fixed; inset: 0; z-index: 100;
          background: oklch(0.10 0.005 60 / 0.7);
          backdrop-filter: blur(8px);
          display: grid; place-items: center;
          padding: 24px;
          animation: fadein .2s ease;
        }
        @keyframes fadein { from { opacity: 0; } }
        .modal {
          position: relative;
          width: 100%; max-width: 540px;
          background: linear-gradient(180deg, oklch(0.20 0.009 60), oklch(0.17 0.008 60));
          border: 1px solid var(--hairline-2);
          border-radius: 20px;
          padding: 28px 28px 24px;
          box-shadow: 0 30px 80px -20px oklch(0 0 0 / 0.6), 0 0 60px -20px var(--accent-glow);
        }
        .modal-close {
          position: absolute; top: 14px; right: 14px;
          width: 28px; height: 28px;
          color: var(--fg-mute);
          border-radius: 6px;
        }
        .modal-close:hover { color: var(--fg); background: oklch(0.25 0.01 60); }
        .modal-eye { display: flex; align-items: center; gap: 10px; color: var(--accent); font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; }
        .modal-eye i { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 12px var(--accent-glow); animation: pulse 2s ease-out infinite; }
        .modal-title { margin-top: 12px; font-size: 24px; font-weight: 500; letter-spacing: -0.018em; line-height: 1.15; }
        .modal-prompt { margin-top: 14px; padding: 12px 14px; border-radius: 10px; background: oklch(0.16 0.008 60); border: 1px solid var(--hairline); font-family: var(--font-mono); font-size: 13px; color: var(--fg-dim); line-height: 1.5; }
        .modal-steps { margin-top: 18px; display: flex; flex-direction: column; gap: 10px; }
        .modal-step { display: flex; align-items: center; gap: 12px; padding: 11px 14px; border-radius: 10px; background: oklch(0.165 0.008 60); border: 1px solid var(--hairline); font-size: 14px; color: var(--fg-dim); transition: all .25s; }
        .modal-step.done { color: var(--fg); }
        .modal-step.done .check { color: var(--ok); }
        .modal-step .check { width: 18px; height: 18px; color: var(--fg-faint); flex-shrink: 0; }
        .modal-step .spinner { width: 14px; height: 14px; border-radius: 50%; border: 2px solid oklch(0.30 0.01 60); border-top-color: var(--accent); animation: spin .9s linear infinite; flex-shrink: 0; }
        @keyframes spin { to { transform: rotate(360deg); } }
        .modal-foot { margin-top: 18px; text-align: center; font-family: var(--font-mono); font-size: 11px; color: var(--fg-faint); letter-spacing: 0.04em; }
      `}</style>

      <div className="modal" onClick={(e) => e.stopPropagation()}>
        <button type="button" className="modal-close" onClick={onClose}>
          ✕
        </button>
        <div className="modal-eye">
          <i /> Vibn is on it
        </div>
        <h3 className="modal-title">Keep vibing — we've got the rest.</h3>
        <div className="modal-prompt">"{prompt}"</div>

        <div className="modal-steps">
          {[
            "Drafting the screens",
            "Setting up logins",
            "Saving your stuff",
            "Putting it online",
          ].map((s, i) => (
            <div key={s} className={`modal-step${i < step ? " done" : ""}`}>
              {i < step ? (
                <svg className="check" viewBox="0 0 20 20" fill="none">
                  <path
                    d="M4 10.5 8 14.5 16 6"
                    stroke="currentColor"
                    strokeWidth="2"
                    strokeLinecap="round"
                    strokeLinejoin="round"
                  />
                </svg>
              ) : i === step ? (
                <span className="spinner" />
              ) : (
                <svg className="check" viewBox="0 0 20 20" fill="none">
                  <circle
                    cx="10"
                    cy="10"
                    r="6"
                    stroke="currentColor"
                    strokeWidth="1.5"
                  />
                </svg>
              )}
              <span>{s}</span>
            </div>
          ))}
        </div>

        {step === 4 ? (
          <div
            className="modal-actions"
            style={{
              marginTop: "24px",
              display: "flex",
              flexDirection: "column",
              gap: "12px",
              alignItems: "center",
            }}
          >
            <a
              href="/signup"
              className="btn btn-primary"
              style={{
                width: "100%",
                height: "48px",
                display: "inline-flex",
                alignItems: "center",
                justifyContent: "center",
                gap: "8px",
                fontSize: "15px",
                fontWeight: "600",
                textDecoration: "none",
              }}
            >
              Launch Your Workspace <Arrow size={14} />
            </a>
            <span
              style={{
                fontSize: "11px",
                color: "var(--fg-faint)",
                fontFamily: "var(--font-mono)",
                letterSpacing: "0.04em",
              }}
            >
              Redirecting to registration in {redirectCount}s...
            </span>
          </div>
        ) : (
          <div className="modal-foot">
            No homework · No setup · No new tools to learn
          </div>
        )}
      </div>
    </div>
  );
}

export default function NewSite() {
  return (
    <div
      className="new-site-wrapper"
      style={{ display: "flex", flexDirection: "column" }}
    >
      <App />
    </div>
  );
}
