// HeroIndieChat.jsx - onboarding-style homepage chat for solocontent.studio.

(function () {
  const { useEffect, useLayoutEffect, useRef, useState } = React;

  function convexSiteUrl() {
    return (window.SCS_CONVEX_SITE || "https://vibrant-kiwi-635.convex.site").replace(/\/$/, "");
  }

  const COOKIE_SESSION = "scs_indie_session";
  const COOKIE_CLAIM = "scs_indie_claim";
  const SESSION_CLAIM_KEY = "scs_indie_pending_claim";
  const COOKIE_TTL_DAYS = 14;

  function setCookie(name, value) {
    const exp = new Date(Date.now() + COOKIE_TTL_DAYS * 86400 * 1000).toUTCString();
    document.cookie = `${name}=${value}; Path=/; Expires=${exp}; SameSite=Lax`;
  }

  function getCookie(name) {
    return document.cookie
      .split("; ")
      .find((row) => row.startsWith(name + "="))
      ?.split("=")[1] ?? null;
  }

  function getStoredClaimToken() {
    try {
      return sessionStorage.getItem(SESSION_CLAIM_KEY);
    } catch {
      return null;
    }
  }

  function setStoredClaimToken(value) {
    try {
      sessionStorage.setItem(SESSION_CLAIM_KEY, value);
    } catch {}
  }

  function isModelToggleVisible() {
    try {
      if (location.hostname === "localhost" || location.hostname === "127.0.0.1") return true;
      return localStorage.getItem("SCS_INDIE_MODEL_OVERRIDE_VISIBLE") === "1";
    } catch {
      return false;
    }
  }

  function getModelOverride(kind) {
    try {
      return localStorage.getItem(`SCS_INDIE_${kind.toUpperCase()}_MODEL`) || "";
    } catch {
      return "";
    }
  }

  function setModelOverride(kind, value) {
    try {
      if (value) localStorage.setItem(`SCS_INDIE_${kind.toUpperCase()}_MODEL`, value);
      else localStorage.removeItem(`SCS_INDIE_${kind.toUpperCase()}_MODEL`);
    } catch {}
  }

  async function apiPost(path, body, extraHeaders) {
    const res = await fetch(`${convexSiteUrl()}${path}`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        ...(extraHeaders || {}),
      },
      body: JSON.stringify(body || {}),
    });
    if (!res.ok && res.status !== 200) throw new Error(`HTTP ${res.status}`);
    return await res.json();
  }

  function modelHeaders() {
    const tease = getModelOverride("tease");
    const unlock = getModelOverride("unlock");
    const h = {};
    if (tease) h["X-SCS-Indie-Tease-Model"] = tease;
    if (unlock) h["X-SCS-Indie-Unlock-Model"] = unlock;
    return h;
  }

  const INITIAL_MESSAGES = [
    {
      kind: "manager",
      text: "Hi, I'm Indie, your Studio Manager. Answer five quick questions and I'll write your first newsletter, in your voice, in less than two minutes. It's free and there's no credit card required. You'll add your email at the end to unlock the full draft. Ready? What's your business called?",
    },
  ];

  const WORK_STEPS = [
    "Pulling up your style guide",
    "Sitting with your topic",
    "Brainstorming 3 subject lines",
    "Drafting your opening",
    "Filling in your sections",
    "One more read-through",
  ];

  const WORK_CHAT = [
    "Indie is translating your brief into a working angle.",
    "Indie is looking for the hook your audience would recognize.",
    "Indie is testing subject-line promises before the body gets written.",
    "Indie is shaping the opening so it sounds specific to your business.",
    "Indie is filling in the useful sections first, polish second.",
    "Indie is cutting anything that sounds generic.",
  ];

  function buildUnlockSteps(brandName) {
    const name = (brandName || "your business").trim();
    return [
      "Organizing your newsletter",
      "Assembling the content team",
      "Building your studio dashboard",
      `Setting up ${name} as a project`,
      "Putting the finishing touches on it",
      "Opening The Studio",
    ];
  }

  const UNLOCK_CHAT = [
    "Indie is organizing your newsletter and saving the draft.",
    "Indie is bringing in the specialist team that handles every format.",
    "Indie is wiring up your studio dashboard so everything has a home.",
    "Indie is creating your first project so the brief and voice travel with it.",
    "Indie is doing one last pass before handing the keys over.",
    "Indie is opening The Studio with your draft ready to read.",
  ];

  function HeroIndieChat() {
    const [step, setStep] = useState("businessName");
    const [messages, setMessages] = useState(INITIAL_MESSAGES);
    const [inputValue, setInputValue] = useState("");
    const [business, setBusiness] = useState({
      businessName: "",
      audience: "",
      difference: "",
      voice: "",
    });
    const [topic, setTopic] = useState("");
    const [snapshot, setSnapshot] = useState(null);
    const [teaser, setTeaser] = useState(null);
    const [previewDraft, setPreviewDraft] = useState(null);
    const [previewChars, setPreviewChars] = useState(0);
    const [previewLocked, setPreviewLocked] = useState(false);
    const [activeWorkStep, setActiveWorkStep] = useState(-1);
    const [activeUnlockStep, setActiveUnlockStep] = useState(-1);
    const [typing, setTyping] = useState(false);
    const [workNote, setWorkNote] = useState("");
    const [unlockNote, setUnlockNote] = useState("");
    const [email, setEmail] = useState("");
    const [busy, setBusy] = useState(false);
    const [error, setError] = useState("");
    const [sessionId, setSessionId] = useState(null);
    const [claimToken, setClaimToken] = useState(null);
    const [turnstileToken, setTurnstileToken] = useState(null);
    const [inputInteracted, setInputInteracted] = useState(false);
    const [isMobileViewport, setIsMobileViewport] = useState(false);
    const [chatIsolated, setChatIsolated] = useState(false);
    const [portalNode, setPortalNode] = useState(null);
    const [portalFrame, setPortalFrame] = useState({
      normal: null,
      viewport: null,
    });
    const [formStartedAt] = useState(() => Date.now());
    const timersRef = useRef([]);
    const convoRef = useRef(null);
    const chatCardRef = useRef(null);
    const placeholderRef = useRef(null);
    const lockScrollYRef = useRef(0);

    function clearTimers() {
      timersRef.current.forEach((id) => {
        window.clearTimeout(id);
        window.clearInterval(id);
      });
      timersRef.current = [];
    }

    function later(fn, delay) {
      const id = window.setTimeout(fn, delay);
      timersRef.current.push(id);
      return id;
    }

    function appendManager(text, hint) {
      setTyping(false);
      setMessages((current) => [
        ...current,
        { kind: "manager", text, ...(hint ? { hint } : {}) },
      ]);
    }

    function appendUser(text) {
      setMessages((current) => [...current, { kind: "user", text }]);
    }

    function managerTyping(text, delay = 420, hint) {
      setTyping(true);
      later(() => appendManager(text, hint), delay);
    }

    useEffect(() => {
      return () => clearTimers();
    }, []);

    useEffect(() => {
      const media = window.matchMedia("(max-width: 760px)");
      const sync = () => setIsMobileViewport(media.matches);
      sync();
      if (media.addEventListener) media.addEventListener("change", sync);
      else media.addListener(sync);

      const node = document.createElement("div");
      node.className = "ihc-mobile-portal-root";
      document.body.appendChild(node);
      setPortalNode(node);

      return () => {
        if (media.removeEventListener) media.removeEventListener("change", sync);
        else media.removeListener(sync);
        node.remove();
      };
    }, []);

    useLayoutEffect(() => {
      if (!isMobileViewport) return;
      let raf = 0;
      let ro = null;
      const sameFrame = (a, b) => (
        !!a && !!b &&
        Math.abs(a.left - b.left) < 0.5 &&
        Math.abs(a.top - b.top) < 0.5 &&
        Math.abs(a.width - b.width) < 0.5 &&
        Math.abs(a.height - b.height) < 0.5
      );
      const readFrame = () => {
        raf = 0;
        const placeholder = placeholderRef.current;
        if (!placeholder) return;
        const rect = placeholder.getBoundingClientRect();
        const vv = window.visualViewport;
        const next = {
          normal: {
            left: rect.left + window.scrollX,
            top: rect.top + window.scrollY,
            width: rect.width,
            height: rect.height,
          },
          viewport: {
            left: vv ? vv.offsetLeft : 0,
            top: vv ? vv.offsetTop : 0,
            width: vv ? vv.width : window.innerWidth,
            height: vv ? vv.height : window.innerHeight,
          },
        };
        setPortalFrame((current) => {
          if (sameFrame(current.normal, next.normal) && sameFrame(current.viewport, next.viewport)) {
            return current;
          }
          return next;
        });
      };
      const schedule = () => {
        if (!raf) raf = window.requestAnimationFrame(readFrame);
      };

      schedule();
      window.addEventListener("resize", schedule);
      window.addEventListener("scroll", schedule, true);
      if (window.visualViewport) {
        window.visualViewport.addEventListener("resize", schedule);
        window.visualViewport.addEventListener("scroll", schedule);
      }
      if (window.ResizeObserver && placeholderRef.current) {
        ro = new ResizeObserver(schedule);
        ro.observe(placeholderRef.current);
      }
      return () => {
        if (raf) window.cancelAnimationFrame(raf);
        window.removeEventListener("resize", schedule);
        window.removeEventListener("scroll", schedule, true);
        if (window.visualViewport) {
          window.visualViewport.removeEventListener("resize", schedule);
          window.visualViewport.removeEventListener("scroll", schedule);
        }
        if (ro) ro.disconnect();
      };
    }, [isMobileViewport, chatIsolated, messages.length, step, previewChars, previewLocked]);

    useEffect(() => {
      if (!isMobileViewport && chatIsolated) setChatIsolated(false);
    }, [isMobileViewport, chatIsolated]);

    useEffect(() => {
      if (!isMobileViewport || !chatIsolated) return;
      const scrollY = window.scrollY;
      lockScrollYRef.current = scrollY;
      document.documentElement.classList.add("ihc-chat-isolated-root");
      document.body.classList.add("ihc-chat-isolated-body");
      document.body.style.top = `-${scrollY}px`;

      requestAnimationFrame(() => {
        const el = convoRef.current;
        if (el) el.scrollTo({ top: el.scrollHeight, behavior: "auto" });
      });

      return () => {
        document.documentElement.classList.remove("ihc-chat-isolated-root");
        document.body.classList.remove("ihc-chat-isolated-body");
        document.body.style.top = "";
        window.scrollTo(0, lockScrollYRef.current);
      };
    }, [isMobileViewport, chatIsolated]);

    function isChatTextTarget(target) {
      if (!target || typeof target.matches !== "function") return false;
      return target.matches(".ihc-input, .ihc-email-input");
    }

    function enterChatIsolation() {
      if (isMobileViewport) setChatIsolated(true);
    }

    function leaveChatIsolationSoon() {
      if (!isMobileViewport) return;
      window.setTimeout(() => {
        const active = document.activeElement;
        if (chatCardRef.current && active && chatCardRef.current.contains(active)) return;
        setChatIsolated(false);
      }, 80);
    }

    function handleChatPointer(event) {
      if (!isChatTextTarget(event.target)) return;
      if (!isMobileViewport) {
        enterChatIsolation();
      }
    }

    function handleChatFocus(event) {
      if (isChatTextTarget(event.target)) enterChatIsolation();
    }


    useEffect(() => {
      const el = convoRef.current;
      if (!el) return;
      // Two anchored scroll modes interrupt the default "scroll to bottom":
      //   gateActive: email gate is showing — anchor newsletter card top.
      //   briefActive: brief just appeared (briefing or topic step) — anchor
      //     the manager intro bubble immediately above the brief card so the
      //     visitor reads "Good. Here's the brief..." first, then scrolls
      //     down through the brief itself, instead of the convo snapping past
      //     the intro to the bottom "Let's write" prompt.
      const gateActive = (step === "email" || step === "unlocking") && previewLocked;
      const briefActive =
        (step === "briefing" || step === "topic") &&
        messages.some((m) => m.kind === "brief");
      // Compute scrollTop offset that places `target`'s top edge `pad` px
      // from the top of the convo, using bounding rects (avoids offsetParent
      // pitfalls when the convo has flex/positioned ancestors).
      const scrollAnchorTo = (target, pad) => {
        const convoRect = el.getBoundingClientRect();
        const targetRect = target.getBoundingClientRect();
        const offset = targetRect.top - convoRect.top + el.scrollTop - pad;
        el.scrollTo({ top: Math.max(0, offset), behavior: "smooth" });
      };
      requestAnimationFrame(() => {
        if (gateActive) {
          const card = el.querySelector(".ihc-newsletter-card");
          if (card) {
            scrollAnchorTo(card, 12);
            return;
          }
        }
        if (briefActive) {
          const card = el.querySelector(".ihc-brief-card");
          // The message added right before the brief card is the manager
          // bubble that introduces it ("Good. Here's the brief..."). Pin
          // scroll to that bubble so the visitor reads it first.
          const intro = card?.previousElementSibling ?? card;
          if (intro) {
            scrollAnchorTo(intro, 12);
            return;
          }
        }
        el.scrollTo({ top: el.scrollHeight, behavior: "smooth" });
      });
    }, [messages.length, typing, step, previewLocked]);

    useEffect(() => {
      const existing = getCookie(COOKIE_SESSION);
      const storedClaim = getStoredClaimToken();
      if (existing) setSessionId(existing);
      if (storedClaim) setClaimToken(storedClaim);
      if (!existing) return;

      apiPost("/indie/state", {
        sessionId: existing,
        claimToken: getCookie(COOKIE_CLAIM) || storedClaim || undefined,
      })
        .then((res) => {
          if (!res?.view) return;
          const v = res.view;
          if (v.brandSnapshotJson) {
            try {
              const parsed = JSON.parse(v.brandSnapshotJson);
              setSnapshot(parsed);
              setBusiness((current) => ({
                businessName: current.businessName || parsed.brandName || "",
                audience: current.audience || parsed.audience || "",
                difference: current.difference,
                voice: current.voice,
              }));
            } catch {}
          }
          if (v.newsletterTopic) setTopic(v.newsletterTopic);
          if (v.fullDraftJson && (getCookie(COOKIE_CLAIM) || storedClaim)) {
            window.location.assign("/indie-preview");
            return;
          }
          if (v.teaserDraftJson) {
            try {
              const parsedTeaser = JSON.parse(v.teaserDraftJson);
              setTeaser(parsedTeaser);
              setPreviewDraft(buildPreviewDraft(parsedTeaser));
              setPreviewChars(99999);
              setPreviewLocked(true);
              setStep("email");
              setMessages([
                { kind: "manager", text: "Your newsletter is ready!" },
              ]);
            } catch {}
          } else if (v.status === "snapshot_ready") {
            setStep("topic");
            setMessages([
              { kind: "manager", text: "Welcome back. I have your business brief." },
              { kind: "brief" },
              { kind: "manager", text: `We'll write a newsletter for ${business.businessName || "your business"} first. What topic should it cover?` },
            ]);
          }
        })
        .catch(() => {});
    }, []);

    async function ensureSession() {
      if (sessionId && claimToken) return { sessionId, claimToken };
      const res = await apiPost("/indie/start", {});
      if (res?.error) throw new Error(res.error);
      setCookie(COOKIE_SESSION, res.sessionId);
      setStoredClaimToken(res.claimToken);
      setSessionId(res.sessionId);
      setClaimToken(res.claimToken);
      return { sessionId: res.sessionId, claimToken: res.claimToken };
    }

    function businessPaste(nextBusiness) {
      return [
        `Business name: ${nextBusiness.businessName.trim()}`,
        `Who they help: ${nextBusiness.audience.trim()}`,
        `What makes them different: ${nextBusiness.difference.trim()}`,
        `How they sound when writing: ${(nextBusiness.voice || "").trim()}`,
      ].join("\n");
    }

    function validateInput(value, min, message) {
      const clean = value.trim();
      if (clean.length < min) {
        setError(message);
        return null;
      }
      return clean;
    }

    async function submitChat(valueOverride) {
      if (busy) return;
      setError("");
      const clean = (typeof valueOverride === "string" ? valueOverride : inputValue).trim();
      if (step === "businessName") {
        const value = validateInput(clean, 2, "Give Indie a business name to work with.");
        if (!value) return;
        appendUser(value);
        setBusiness((current) => ({ ...current, businessName: value }));
        setInputValue("");
        setStep("audience");
        managerTyping(`Got it. Who does ${value} help?`, 420);
        return;
      }
      if (step === "audience") {
        const value = validateInput(clean, 10, "Give Indie one clear sentence about who you help.");
        if (!value) return;
        appendUser(value);
        setBusiness((current) => ({ ...current, audience: value }));
        setInputValue("");
        setStep("difference");
        managerTyping(`Perfect. What makes ${business.businessName || "this business"} meaningfully different from the competition?`, 420);
        return;
      }
      if (step === "difference") {
        const value = validateInput(clean, 10, "Give Indie one clear sentence about what makes you different.");
        if (!value) return;
        appendUser(value);
        setBusiness((current) => ({ ...current, difference: value }));
        setInputValue("");
        setStep("voice");
        managerTyping(`One more. How do you sound when you write? Paste a sentence in your voice, or describe your style.`, 420);
        return;
      }
      if (step === "voice") {
        const value = validateInput(clean, 5, "Give Indie a quick sense of your writing voice.");
        if (!value) return;
        const nextBusiness = { ...business, voice: value };
        appendUser(value);
        setBusiness(nextBusiness);
        setInputValue("");
        await createBrief(nextBusiness);
        return;
      }
      if (step === "topic") {
        const value = validateInput(clean, 5, "Add a specific topic for the newsletter.");
        if (!value) return;
        appendUser(value);
        setTopic(value);
        setInputValue("");
        await writePreview(value);
      }
    }

    async function createBrief(nextBusiness) {
      setBusy(true);
      setStep("briefing");
      setTyping(true);
      try {
        const { sessionId: sid } = await ensureSession();
        const res = await apiPost(
          "/indie/summarize",
          { sessionId: sid, paste: businessPaste(nextBusiness) },
          modelHeaders(),
        );
        if (res.error) {
          setError(errorCopy(res.error, "I could not build the brief. Try again."));
          setStep("voice");
          return;
        }
        setSnapshot(res.snapshot);
        const name = nextBusiness.businessName || res.snapshot?.brandName || "your business";
        appendManager(`Good. Here's the brief I'll use so ${name} doesn't get a generic newsletter.`);
        later(() => {
          setMessages((current) => [...current, { kind: "brief" }]);
        }, 250);
        later(() => {
          managerTyping(
            `Let's write an email newsletter for ${name}! What topic should this newsletter cover?`,
            360,
            "Later, we can turn the same topic into social posts, short-form videos, blog posts, email sequences, podcast outlines, and sales pages.",
          );
          setStep("topic");
        }, 3800);
      } catch {
        setTyping(false);
        setError("I could not reach Indie. Refresh and try again.");
        setStep("voice");
      } finally {
        setBusy(false);
      }
    }

    function startWorkSteps(setter, steps, delay, chatMessages, noteSetter) {
      setter(0);
      if (chatMessages?.[0] && noteSetter) noteSetter(chatMessages[0]);
      steps.forEach((_, i) => {
        if (i === 0) return;
        later(() => {
          setter(i);
          if (chatMessages?.[i] && noteSetter) noteSetter(chatMessages[i]);
        }, delay * i);
      });
    }

    async function writePreview(cleanTopic) {
      setBusy(true);
      setStep("writing");
      setPreviewDraft({
        title: "",
        content: "",
        subjectLines: [],
        topic: cleanTopic,
      });
      setPreviewChars(0);
      setPreviewLocked(false);
      setActiveWorkStep(0);
      setWorkNote(WORK_CHAT[0]);
      managerTyping("I'm writing the preview now. You'll see the draft card start to take shape as soon as the first pass lands.", 420);
      startWorkSteps(setActiveWorkStep, WORK_STEPS, 1500, WORK_CHAT, setWorkNote);

      try {
        const { sessionId: sid } = await ensureSession();
        const res = await apiPost(
          "/indie/teaser-draft",
          { sessionId: sid, formatKey: "newsletter", topic: cleanTopic },
          modelHeaders(),
        );
        if (res.error) {
          // Surface the server-provided error detail to devtools so we can
          // diagnose the real root cause without needing live log tails.
          console.error("[indie/teaser-draft] failed:", res.error, res.errorDetail || "(no detail)");
          clearTimers();
          setTyping(false);
          setWorkNote("");
          setActiveWorkStep(-1);
          setPreviewDraft(null);
          setError(errorCopy(res.error, "I hit a snag writing the preview. Try a shorter topic, or try again."));
          setStep("topic");
          setBusy(false);
          return;
        }
        setTeaser(res.teaser);
        animatePreview(res.teaser);
      } catch (err) {
        console.error("[indie/teaser-draft] network error:", err);
        clearTimers();
        setTyping(false);
        setWorkNote("");
        setActiveWorkStep(-1);
        setPreviewDraft(null);
        setError("I could not reach Indie. Check the connection and try again.");
        setStep("topic");
        setBusy(false);
      }
    }

    function animatePreview(draft) {
      const preview = buildPreviewDraft(draft);
      setPreviewDraft(preview);
      setPreviewChars(0);
      const total = preview.title.length + preview.content.length;
      const startedAt = Date.now();
      const duration = Math.min(3600, Math.max(1500, total * 9));
      const tick = window.setInterval(() => {
        const pct = Math.min(1, (Date.now() - startedAt) / duration);
        const chars = Math.ceil(total * pct);
        setPreviewChars(chars);
        if (pct >= 1) {
          window.clearInterval(tick);
          setPreviewLocked(true);
          setActiveWorkStep(WORK_STEPS.length - 1);
          setWorkNote("");
          appendManager("Your newsletter is ready!");
          setStep("email");
          setBusy(false);
        }
      }, 35);
      timersRef.current.push(tick);
    }

    async function submitEmail() {
      setError("");
      const honey = document.querySelector('input[name="company_url"]')?.value || "";
      const cleanEmail = email.trim();
      if (!cleanEmail || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(cleanEmail)) {
        setError("Enter a real email so Indie can open the draft in Studio.");
        return;
      }
      if (Date.now() - formStartedAt < 2000) return;
      const turnstileEnabled = !!(typeof window !== "undefined" && window.SCS_TURNSTILE_SITE_KEY);
      if (turnstileEnabled && !turnstileToken) {
        setError("Verification is still finishing. Try again in a second.");
        return;
      }

      const unlockSteps = buildUnlockSteps(business.businessName || snapshot?.brandName);
      setBusy(true);
      setStep("unlocking");
      setActiveUnlockStep(0);
      setUnlockNote(UNLOCK_CHAT[0]);
      appendManager("We are preparing your Solo Content Studio...");
      // Animate steps 0..n-2 only — reserve the final "Opening The Studio"
      // step for the moment the claim actually returns, so it never sits
      // on the last step waiting for the redirect. Slower per-step pacing
      // (2600ms) spreads the earlier steps over ~13s, matching the typical
      // Opus path without prefetch.
      startWorkSteps(
        setActiveUnlockStep,
        unlockSteps.slice(0, -1),
        2600,
        UNLOCK_CHAT.slice(0, -1),
        setUnlockNote,
      );
      try {
        const { sessionId: sid, claimToken: tok } = await ensureSession();
        const res = await apiPost(
          "/indie/claim",
          {
            sessionId: sid,
            claimToken: tok,
            email: cleanEmail,
            honeypot: honey,
            turnstileToken: turnstileToken || undefined,
          },
          modelHeaders(),
        );
        if (res.error) {
          clearTimers();
          setUnlockNote("");
          setError(errorCopy(res.error, "I could not finish the full draft. Try again."));
          setBusy(false);
          setStep("email");
          return;
        }
        setCookie(COOKIE_CLAIM, tok);
        setActiveUnlockStep(unlockSteps.length - 1);
        setUnlockNote(UNLOCK_CHAT[UNLOCK_CHAT.length - 1]);
        window.location.assign("/indie-preview");
      } catch {
        clearTimers();
        setUnlockNote("");
        setError("I could not reach Indie. Refresh and try again.");
        setBusy(false);
        setStep("email");
      }
    }

    const inputConfig = inputForStep(step, business.businessName || snapshot?.brandName);
    const keepMobileComposer =
      isMobileViewport &&
      chatIsolated &&
      !inputConfig &&
      step !== "email" &&
      step !== "unlocking";
    const activeInputConfig = inputConfig || (keepMobileComposer
      ? { label: "", placeholder: "Indie is thinking..." }
      : null);
    const inputSubmitLocked = busy || !inputConfig;
    const showInput = !!activeInputConfig;
    const viewportFrame = portalFrame.viewport || {
      left: 0,
      top: 0,
      width: typeof window !== "undefined" ? window.innerWidth : 0,
      height: typeof window !== "undefined" ? window.innerHeight : 0,
    };
    const normalFrame = portalFrame.normal;
    const MeshBg = window.MeshBg;
    const mobilePortalStyle = chatIsolated
      ? {
          "--ihc-vv-left": `${viewportFrame.left}px`,
          "--ihc-vv-top": `${viewportFrame.top}px`,
          "--ihc-vv-width": `${viewportFrame.width}px`,
          "--ihc-vv-height": `${viewportFrame.height}px`,
        }
      : normalFrame
        ? {
            left: `${normalFrame.left}px`,
            top: `${normalFrame.top}px`,
            width: `${normalFrame.width}px`,
            height: `${normalFrame.height}px`,
          }
        : { visibility: "hidden" };

    const chatCard = (
      <div
        className={`ihc-chat-card${chatIsolated ? " is-isolated" : ""}`}
        ref={chatCardRef}
        onFocusCapture={handleChatFocus}
        onBlurCapture={leaveChatIsolationSoon}
        onPointerDownCapture={handleChatPointer}
        onTouchStartCapture={handleChatPointer}
      >
        <main className="ihc-convo" ref={convoRef}>
          {messages.map((message, i) =>
            message.kind === "user" ? (
              <UserMessage key={i} text={message.text} />
            ) : message.kind === "brief" ? (
              snapshot ? <BriefCard key={i} snapshot={snapshot} business={business} /> : null
            ) : (
              <ManagerMessage key={i} text={message.text} hint={message.hint} />
            ),
          )}

          {typing && <TypingIndicator />}

          {(step === "writing" || step === "email" || step === "unlocking") && (
            <NewsletterPreview
              draft={previewDraft}
              chars={previewChars}
              locked={previewLocked}
              steps={WORK_STEPS}
              activeStep={activeWorkStep}
              statusNote={step === "writing" ? workNote : unlockNote}
            />
          )}

        </main>

        {error && <p className="ihc-err">{error}</p>}

        {(step !== "email" && step !== "unlocking") && (
          <ProgressTracker step={step} />
        )}
        {showInput ? (
          <ChatInput
            label={activeInputConfig.label}
            placeholder={activeInputConfig.placeholder}
            value={inputValue}
            onChange={setInputValue}
            onSubmit={submitChat}
            locked={inputSubmitLocked}
            pulse={step === "businessName" && !inputInteracted}
            onInteract={() => setInputInteracted(true)}
            maxLength={activeInputConfig.maxLength}
          />
        ) : (step === "email" || step === "unlocking") ? null : (
          <footer className="ihc-input-area ihc-input-spacer" />
        )}
        {(step === "email" || step === "unlocking") && previewLocked && (
          <div className={`ihc-email-gate-overlay${step === "unlocking" ? " is-unlocking" : ""}`}>
            {step === "unlocking" ? (
              <UnlockProgress
                steps={buildUnlockSteps(business.businessName || snapshot?.brandName)}
                activeStep={activeUnlockStep}
                note={unlockNote}
              />
            ) : (
              <EmailGate
                email={email}
                setEmail={setEmail}
                onSubmit={submitEmail}
                busy={false}
                onTurnstileToken={setTurnstileToken}
                error={error}
              />
            )}
          </div>
        )}
      </div>
    );

    return (
      <section style={shellStyle}>
        <style>{styles}</style>
        {MeshBg ? <MeshBg variant="warm" /> : null}
        <div className="ihc-shell">
          <div className="ihc-text-side">
            <h1 className="ihc-headline">
              In the next two minutes, you'll have a <em>custom newsletter</em> written for your business.
            </h1>
            <p className="ihc-sub">
              Answer five quick questions and your newsletter appears, written in your voice. No credit card, nothing to install.
            </p>
          </div>

          <div className="ihc-chat-side">
            {isMobileViewport ? (
              <div className="ihc-chat-card ihc-chat-placeholder" ref={placeholderRef} aria-hidden="true" />
            ) : chatCard}
          </div>
        </div>
        {isMobileViewport && portalNode && ReactDOM.createPortal(
          <div
            className={`ihc-mobile-portal${chatIsolated ? " is-isolated" : ""}`}
            style={mobilePortalStyle}
          >
            {chatCard}
          </div>,
          portalNode,
        )}
      </section>
    );
  }

  function inputForStep(step, brandName) {
    if (step === "businessName") {
      return { label: "", placeholder: "Type your business name...", maxLength: 100 };
    }
    if (step === "audience") {
      return { label: "", placeholder: "e.g., busy solo founders who need consistent content", maxLength: 100 };
    }
    if (step === "difference") {
      return { label: "", placeholder: "e.g., we pair strategy with specialist AI agents trained on your voice", maxLength: 500 };
    }
    if (step === "voice") {
      return { label: "", placeholder: "e.g., warm and direct, with specific examples and no fluff", maxLength: 500 };
    }
    if (step === "topic") {
      return {
        label: "",
        placeholder: "Give a quick suggestion based on the business info",
        maxLength: 500,
      };
    }
    return null;
  }

  function errorCopy(code, fallback) {
    if (code === "BUSY") return "Indie is busy right now. Try again in a minute.";
    if (code === "BAD_EMAIL") return "That email does not look right.";
    if (code === "PASTE_TOO_SHORT") return "Add a little more detail before we build the brief.";
    if (code === "SNAPSHOT_FAILED") return "Indie hit a snag building the brief. Try again.";
    if (code === "TEASER_FAILED") return "I hit a snag writing the preview. Try a shorter topic, or try again.";
    if (code === "OPUS_FAILED") return "Indie hit a snag finishing the full draft. Try again.";
    if (code === "INVALID_SESSION") return "This session expired. Refresh and start again.";
    if (code === "TURNSTILE_REQUIRED") return "Verification did not complete. Wait a moment and try again.";
    return fallback;
  }

  function buildPreviewDraft(draft) {
    const blocks = splitBlocks(draft?.content || "").slice(0, 4);
    return {
      title: cleanMarkdown(draft?.title || "Your first newsletter"),
      subjectLines: Array.isArray(draft?.subjectLines) ? draft.subjectLines.slice(0, 3) : [],
      content: blocks.join("\n\n"),
      topic: draft?.topic || "",
    };
  }

  function splitBlocks(content) {
    return String(content || "")
      .replace(/\r\n/g, "\n")
      .split(/\n{2,}/)
      .map((block) => cleanMarkdown(block))
      .filter(Boolean);
  }

  function cleanMarkdown(text) {
    return String(text || "")
      .replace(/\*\*/g, "")
      .replace(/\*/g, "")
      .replace(/__/g, "")
      .replace(/`/g, "")
      .replace(/^#{1,6}\s+/gm, "")
      .replace(/\s*—\s*/g, ", ")
      .replace(/\s*–\s*/g, ", ")
      .trim();
  }

  function ManagerMessage({ text, hint }) {
    return (
      <div className="ihc-msg ihc-msg-manager">
        <div className="ihc-avatar">
          <img src="/illustrations-headshots-frameless/fox_studio-manager.png" alt="Indie" />
        </div>
        <div className="ihc-msg-body">
          <p className="ihc-manager-name">Indie, your studio manager</p>
          <div className="ihc-bubble">{text}</div>
          {hint && <p className="ihc-bubble-hint">{hint}</p>}
        </div>
      </div>
    );
  }

  function TypingIndicator() {
    return (
      <div className="ihc-msg ihc-msg-manager ihc-typing-msg">
        <div className="ihc-avatar">
          <img src="/illustrations-headshots-frameless/fox_studio-manager.png" alt="Indie" />
        </div>
        <div className="ihc-msg-body">
          <p className="ihc-manager-name">Indie, your studio manager</p>
          <div className="ihc-typing-bubble" aria-label="Indie is typing">
            <span />
            <span />
            <span />
          </div>
        </div>
      </div>
    );
  }

  function BriefCard({ snapshot, business }) {
    const brandName = snapshot?.brandName || business.businessName || "Your business";
    const audience = snapshot?.audience || business.audience || "The audience you described";
    const angle = snapshot?.business || business.difference || "Turn your expertise into useful content";
    const voice = Array.isArray(snapshot?.voiceTraits) && snapshot.voiceTraits.length
      ? snapshot.voiceTraits.slice(0, 4).join(", ")
      : "clear, useful, specific";
    const pillars = Array.isArray(snapshot?.pillars) ? snapshot.pillars.slice(0, 3) : [];

    return (
      <section className="ihc-brief-card" aria-label={`${brandName} brief`}>
        <p className="ihc-brief-eyebrow">Studio brief</p>
        <h3>{brandName}</h3>
        <dl>
          <div>
            <dt>Audience</dt>
            <dd>{audience}</dd>
          </div>
          <div>
            <dt>Angle</dt>
            <dd>{angle}</dd>
          </div>
          <div>
            <dt>Voice</dt>
            <dd>{voice}</dd>
          </div>
        </dl>
        {pillars.length > 0 && (
          <div className="ihc-brief-pills">
            {pillars.map((pillar, i) => (
              <span key={pillar.title || i}>{pillar.title || pillar.desc}</span>
            ))}
          </div>
        )}
      </section>
    );
  }

  function UserMessage({ text }) {
    return (
      <div className="ihc-msg ihc-msg-user">
        <div className="ihc-user-bubble">{text}</div>
      </div>
    );
  }

  const QUESTION_TOTAL = 5;

  function stepProgress(step) {
    // Returns { visible, index } where index is 0-based segment position.
    // "briefing" holds at Q4 (voice just submitted, brief is processing)
    // so the tracker doesn't blink before the topic step appears.
    if (step === "businessName") return { visible: true, index: 0 };
    if (step === "audience") return { visible: true, index: 1 };
    if (step === "difference") return { visible: true, index: 2 };
    if (step === "voice") return { visible: true, index: 3 };
    if (step === "briefing") return { visible: true, index: 3 };
    if (step === "topic") return { visible: true, index: 4 };
    return { visible: false, index: -1 };
  }

  function ProgressTracker({ step }) {
    const { visible, index } = stepProgress(step);
    // Hold the last visible state during the fade-out so the bars don't
    // collapse to "0 of 5" while the tracker is animating away.
    const lastVisibleIndexRef = useRef(visible ? index : 0);
    if (visible) lastVisibleIndexRef.current = index;
    const displayIndex = visible ? index : lastVisibleIndexRef.current;
    const currentNumber = displayIndex + 1;
    return (
      <div
        className={`ihc-progress${visible ? "" : " is-hidden"}`}
        aria-hidden={visible ? "false" : "true"}
      >
        <span className="ihc-progress-label">{`Question ${currentNumber} of ${QUESTION_TOTAL}`}</span>
        <div
          className="ihc-progress-bars"
          role="progressbar"
          aria-valuemin={1}
          aria-valuemax={QUESTION_TOTAL}
          aria-valuenow={currentNumber}
          aria-label={`Question ${currentNumber} of ${QUESTION_TOTAL}`}
        >
          {Array.from({ length: QUESTION_TOTAL }).map((_, i) => (
            <span
              key={i}
              className={`ihc-progress-bar${i <= displayIndex ? " is-done" : ""}`}
            />
          ))}
        </div>
      </div>
    );
  }

  function ChatInput({ label, placeholder, value, onChange, onSubmit, locked, pulse, onInteract, onFocus, maxLength }) {
    const ref = useRef(null);
    const footerRef = useRef(null);
    const sendRef = useRef(null);
    const prevLabelRef = useRef(null);
    const lastSubmitAtRef = useRef(0);
    const pendingSendTapAtRef = useRef(0);
    const retainFocusRef = useRef(false);
    useEffect(() => {
      if (!ref.current) return;
      if (prevLabelRef.current === null) {
        prevLabelRef.current = label;
        return;
      }
      if (prevLabelRef.current !== label) {
        focusTextarea();
        prevLabelRef.current = label;
      }
    }, [label]);
    useEffect(() => {
      const el = ref.current;
      if (!el) return;
      if (!retainFocusRef.current && Date.now() - lastSubmitAtRef.current > 1600) return;
      focusTextarea();
    }, [placeholder, locked]);
    useEffect(() => {
      const el = ref.current;
      if (!el) return;
      el.style.height = "44px";
      el.style.height = Math.min(el.scrollHeight, 132) + "px";
    }, [value]);
    useEffect(() => {
      const send = sendRef.current;
      if (!send) return;
      const eventPoint = (event) => {
        const touch = event.touches?.[0] || event.changedTouches?.[0];
        if (touch) return { x: touch.clientX, y: touch.clientY };
        return { x: event.clientX, y: event.clientY };
      };
      const handlePress = (event) => {
        const point = eventPoint(event);
        const rect = send.getBoundingClientRect();
        const pad = 24;
        const inSend =
          point.x >= rect.left - pad &&
          point.x <= rect.right + pad &&
          point.y >= rect.top - pad &&
          point.y <= rect.bottom + pad;
        if (!inSend) return;
        event.preventDefault();
        event.stopPropagation();
        const now = Date.now();
        if (now - pendingSendTapAtRef.current < 900) {
          focusTextarea();
          return;
        }
        pendingSendTapAtRef.current = now;
        submitAndKeepFocus();
      };
      document.addEventListener("touchstart", handlePress, { capture: true, passive: false });
      document.addEventListener("touchend", handlePress, { capture: true, passive: false });
      document.addEventListener("pointerdown", handlePress, true);
      document.addEventListener("mousedown", handlePress, true);
      document.addEventListener("click", handlePress, true);
      return () => {
        document.removeEventListener("touchstart", handlePress, { capture: true });
        document.removeEventListener("touchend", handlePress, { capture: true });
        document.removeEventListener("pointerdown", handlePress, true);
        document.removeEventListener("mousedown", handlePress, true);
        document.removeEventListener("click", handlePress, true);
      };
    }, [value, locked]);
    function focusTextarea() {
      const focus = () => {
        const el = ref.current;
        if (!el) return;
        try {
          el.focus({ preventScroll: true });
        } catch {
          el.focus();
        }
      };
      focus();
      requestAnimationFrame(focus);
      window.setTimeout(focus, 80);
      window.setTimeout(focus, 220);
      window.setTimeout(focus, 520);
    }
    function submitAndKeepFocus(valueOverride) {
      const submittedValue = typeof valueOverride === "string" ? valueOverride : value;
      if (locked || !submittedValue.trim()) {
        focusTextarea();
        return;
      }
      const now = Date.now();
      if (now - lastSubmitAtRef.current < 250) return;
      lastSubmitAtRef.current = now;
      retainFocusRef.current = true;
      focusTextarea();
      onSubmit(submittedValue);
      focusTextarea();
    }
    function keepTextareaFocused(event, submit) {
      event.preventDefault();
      focusTextarea();
      if (submit) submitAndKeepFocus();
    }
    function handleBlur() {
      if (Date.now() - pendingSendTapAtRef.current < 1200) {
        focusTextarea();
        return;
      }
      if (Date.now() - lastSubmitAtRef.current > 1400) {
        retainFocusRef.current = false;
        return;
      }
      focusTextarea();
    }
    function handleChange(event) {
      const nextValue = event.target.value;
      if (!nextValue.includes("\n")) {
        onChange(nextValue);
        return;
      }
      const submittedValue = nextValue.replace(/\n/g, "");
      onChange(submittedValue);
      submitAndKeepFocus(submittedValue);
    }
    const remaining = typeof maxLength === "number" ? maxLength - value.length : null;
    const showCharHint = remaining !== null && remaining <= 20;
    const charHintWarn = remaining !== null && remaining <= 5;
    return (
      <footer className="ihc-input-area ihc-input-area-show" ref={footerRef}>
        {label ? <p className="ihc-input-label">{label}</p> : null}
        <div className={`ihc-input-row${pulse ? " is-pulsing" : ""}`}>
          <textarea
            ref={ref}
            className="ihc-input"
            placeholder={placeholder}
            value={value}
            rows={1}
            maxLength={maxLength}
            onFocus={() => {
              retainFocusRef.current = true;
              if (onInteract) onInteract();
              if (onFocus) onFocus();
            }}
            onBlur={handleBlur}
            onMouseDown={() => onInteract && onInteract()}
            onBeforeInput={(e) => {
              if (e.nativeEvent?.inputType === "insertLineBreak") {
                e.preventDefault();
                submitAndKeepFocus();
              }
            }}
            onChange={handleChange}
            onKeyDown={(e) => {
              if (e.key === "Enter" && !e.shiftKey) {
                e.preventDefault();
                submitAndKeepFocus();
              }
            }}
          />
          <button
            ref={sendRef}
            className="ihc-send"
            type="button"
            onPointerDown={(e) => keepTextareaFocused(e, true)}
            onPointerUp={(e) => keepTextareaFocused(e, false)}
            onMouseDown={(e) => keepTextareaFocused(e, false)}
            onMouseUp={(e) => keepTextareaFocused(e, false)}
            onTouchStart={(e) => keepTextareaFocused(e, true)}
            onTouchEnd={(e) => keepTextareaFocused(e, false)}
            onClick={submitAndKeepFocus}
            disabled={locked || !value.trim()}
            aria-label="Send"
          >
            ↑
          </button>
        </div>
        {showCharHint && (
          <p className={`ihc-char-hint${charHintWarn ? " is-warning" : ""}`}>
            {remaining === 0
              ? "Character limit reached"
              : `${remaining} character${remaining === 1 ? "" : "s"} left`}
          </p>
        )}
      </footer>
    );
  }

  function UnlockProgress({ steps, activeStep, note }) {
    return (
      <div className="ihc-unlock-progress">
        <h3>Preparing your Solo Content Studio</h3>
        <WorkTimeline steps={steps} activeStep={activeStep} note={note} />
      </div>
    );
  }

  function WorkTimeline({ steps, activeStep, note }) {
    return (
      <div className="ihc-work-card">
        {note && <p className="ihc-work-note">{note}</p>}
        <ul className="ihc-work-list">
          {steps.map((step, i) => {
            const status = i < activeStep ? "done" : i === activeStep ? "active" : "pending";
            return (
              <li key={step} className={`ihc-work-step ${status}`}>
                <span className="ihc-work-dot" />
                <span>{step}</span>
              </li>
            );
          })}
        </ul>
      </div>
    );
  }

  function NewsletterPreview({
    draft,
    chars,
    locked,
    steps,
    activeStep,
    statusNote,
  }) {
    const title = draft?.title || "";
    const content = draft?.content || "";
    const titleChars = Math.min(chars, title.length);
    const bodyChars = Math.max(0, chars - title.length);
    const shownTitle = title.slice(0, titleChars);
    const shownBody = content.slice(0, bodyChars);

    return (
      <div className="ihc-draft-wrap">
        <article className={`ihc-newsletter-card${locked ? " is-locked" : ""}`}>
          {!locked && (
            <div className="ihc-card-working">
              <WorkTimeline steps={steps} activeStep={activeStep} note={statusNote} />
            </div>
          )}

          <div className="ihc-newsletter-body">
            {!draft || (!shownTitle && !shownBody) ? (
              <div className="ihc-draft-skeleton">
                <span className="wide" />
                <span />
                <span className="short" />
                <span className="para" />
                <span className="para wide" />
                <span className="para short" />
              </div>
            ) : (
              <>
                <h2>
                  {shownTitle}
                  {!locked && titleChars < title.length ? <span className="ihc-caret" /> : null}
                </h2>
                <div className="ihc-preview-prose">
                  {splitBlocks(shownBody).map((p, i) => (
                    <p key={i}>{p}</p>
                  ))}
                  {!locked && shownBody ? <span className="ihc-caret inline" /> : null}
                </div>
              </>
            )}
          </div>

        </article>
      </div>
    );
  }

  function EmailGate({ email, setEmail, onSubmit, busy, onTurnstileToken, error, onFocus }) {
    const turnstileRef = useRef(null);
    const widgetIdRef = useRef(null);
    const siteKey = typeof window !== "undefined" ? window.SCS_TURNSTILE_SITE_KEY : "";

    useEffect(() => {
      if (!siteKey || !turnstileRef.current) return;
      const tryRender = () => {
        if (!window.turnstile || !turnstileRef.current) return false;
        if (widgetIdRef.current) return true;
        try {
          widgetIdRef.current = window.turnstile.render(turnstileRef.current, {
            sitekey: siteKey,
            size: "invisible",
            callback: (token) => {
              if (typeof onTurnstileToken === "function") onTurnstileToken(token);
            },
            "error-callback": () => {
              if (typeof onTurnstileToken === "function") onTurnstileToken(null);
            },
            "expired-callback": () => {
              if (typeof onTurnstileToken === "function") onTurnstileToken(null);
              if (window.turnstile && widgetIdRef.current) {
                try {
                  window.turnstile.reset(widgetIdRef.current);
                } catch {}
              }
            },
          });
          if (window.turnstile.execute && widgetIdRef.current) {
            try {
              window.turnstile.execute(widgetIdRef.current);
            } catch {}
          }
          return true;
        } catch {
          return false;
        }
      };
      if (tryRender()) return;
      const id = setInterval(() => {
        if (tryRender()) clearInterval(id);
      }, 200);
      return () => clearInterval(id);
    }, [siteKey, onTurnstileToken]);

    return (
      <section className="ihc-email-gate">
        <h3>Want to read the whole thing?</h3>
        <p>Drop your email to read the full newsletter.</p>
        {error ? <p className="ihc-gate-err">{error}</p> : null}
        <div className="ihc-email-row">
          <input
            type="text"
            name="company_url"
            className="ihc-honeypot"
            tabIndex={-1}
            autoComplete="off"
            aria-hidden="true"
          />
          <input
            type="email"
            className="ihc-email-input"
            placeholder="you@company.com"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            onFocus={() => { if (onFocus) onFocus(); }}
            onKeyDown={(e) => {
              if (e.key === "Enter") {
                e.preventDefault();
                onSubmit();
              }
            }}
            disabled={busy}
          />
          <button className="ihc-unlock-btn" onClick={onSubmit} disabled={busy}>
            {busy ? "Writing..." : "Unlock draft →"}
          </button>
        </div>
        {siteKey && <div ref={turnstileRef} style={{ marginTop: 10 }} />}
      </section>
    );
  }

  function DevModelToggle() {
    const [tease, setTease] = useState(getModelOverride("tease"));
    const [unlock, setUnlock] = useState(getModelOverride("unlock"));
    function commit(kind, value, setter) {
      setModelOverride(kind, value);
      setter(value);
    }
    const models = ["haiku", "sonnet", "opus"];
    return (
      <div className="ihc-toggle">
        <h5>Dev: tease model</h5>
        <div className="row">
          <button className="opt" aria-current={!tease ? "true" : "false"} onClick={() => commit("tease", "", setTease)}>
            default
          </button>
          {models.map((m) => (
            <button key={m} className="opt" aria-current={tease === m ? "true" : "false"} onClick={() => commit("tease", m, setTease)}>
              {m}
            </button>
          ))}
        </div>
        <h5>Dev: unlock model</h5>
        <div className="row">
          <button className="opt" aria-current={!unlock ? "true" : "false"} onClick={() => commit("unlock", "", setUnlock)}>
            default
          </button>
          {models.map((m) => (
            <button key={m} className="opt" aria-current={unlock === m ? "true" : "false"} onClick={() => commit("unlock", m, setUnlock)}>
              {m}
            </button>
          ))}
        </div>
        <div className="toggle-note">Reload to apply.</div>
      </div>
    );
  }

  const shellStyle = {
    position: "relative",
    overflow: "hidden",
    background: "var(--cream, #fbfaf7)",
  };

  const styles = `
    html.ihc-chat-isolated-root,
    html.ihc-chat-isolated-root body {
      overflow: hidden;
      overscroll-behavior: none;
    }
    body.ihc-chat-isolated-body {
      position: fixed;
      left: 0;
      right: 0;
      width: 100%;
    }
    .ihc-mobile-portal-root {
      position: static;
    }
    .ihc-mobile-portal {
      position: absolute;
      z-index: 30;
      box-sizing: border-box;
    }
    .ihc-mobile-portal.is-isolated {
      position: fixed;
      left: var(--ihc-vv-left, 0px);
      top: var(--ihc-vv-top, 0px);
      width: var(--ihc-vv-width, 100vw);
      height: var(--ihc-vv-height, 100dvh);
      z-index: 2147483000;
      background: #fffefd;
      overflow: hidden;
      overscroll-behavior: none;
      touch-action: auto;
    }
    .ihc-mobile-portal .ihc-chat-card {
      width: 100%;
      max-width: none;
      height: 100%;
      max-height: none;
    }
    .ihc-mobile-portal.is-isolated .ihc-chat-card {
      min-height: 0;
      border: none;
      border-radius: 0;
      box-shadow: none;
      background: #fffefd;
    }
    .ihc-shell {
      position: relative;
      box-sizing: border-box;
      max-width: 1200px;
      min-height: calc(100svh - 130px);
      margin: 0 auto;
      padding: clamp(16px, 2.4vh, 28px) clamp(16px, 3vw, 56px);
      display: grid;
      grid-template-columns: auto auto;
      justify-content: center;
      align-content: center;
      align-items: center;
      gap: clamp(16px, 2.4vw, 40px);
    }
    .ihc-shell *,
    .ihc-shell *::before,
    .ihc-shell *::after {
      box-sizing: border-box;
    }
    .ihc-text-side {
      max-width: clamp(320px, 35vw, 520px);
    }
    .ihc-chat-side {
      display: flex;
      justify-content: flex-start;
    }
    .ihc-headline {
      margin: 0 0 22px;
      color: var(--ink, #2a1014);
      font-family: var(--font-display, Georgia, serif);
      font-size: clamp(38px, 4.4vw, 62px);
      font-weight: 500;
      line-height: 1.12;
      text-align: left;
      letter-spacing: -0.03em;
      text-wrap: balance;
    }
    .ihc-sub {
      max-width: 560px;
      margin: 0;
      color: var(--ink-light, #64484e);
      font-size: 19px;
      line-height: 1.55;
      text-align: left;
    }
    .ihc-chat-card {
      position: relative;
      width: clamp(360px, 30vw, 400px);
      max-width: clamp(360px, 30vw, 400px);
      /* Cap height on tall viewports so iPad portrait (1024-1180px tall)
         doesn't render a 900px+ chat box that's mostly empty. */
      height: clamp(420px, 80svh, 640px);
      max-height: 640px;
      display: flex;
      flex-direction: column;
      overflow: hidden;
      border: 1px solid rgba(48,14,22,0.09);
      border-radius: 24px;
      background: rgba(255,255,255,0.94);
      box-shadow: 0 18px 48px rgba(48,14,22,0.11);
    }
    .ihc-convo {
      flex: 1 1 auto;
      min-height: 0;
      overflow-y: auto;
      padding: 20px 20px 16px;
      scroll-behavior: smooth;
    }
    .ihc-msg {
      display: flex;
      gap: 12px;
      margin: 0 0 16px;
    }
    .ihc-msg-user {
      justify-content: flex-end;
    }
    .ihc-avatar {
      width: 42px;
      height: 42px;
      border-radius: 50%;
      overflow: hidden;
      background: #f7ded5;
      flex: 0 0 auto;
    }
    .ihc-avatar img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
    }
    .ihc-msg-body {
      max-width: min(560px, calc(100% - 54px));
    }
    .ihc-manager-name {
      margin: 0 0 5px;
      color: #6d5358;
      font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
      font-size: 10px;
      font-weight: 700;
      letter-spacing: 0.12em;
      text-transform: uppercase;
    }
    .ihc-bubble {
      width: fit-content;
      max-width: 100%;
      border: 1px solid rgba(48,14,22,0.08);
      border-radius: 18px 18px 18px 7px;
      background: #f7f3ee;
      color: #2a1014;
      padding: 13px 15px;
      font-size: 15px;
      line-height: 1.55;
    }
    .ihc-bubble-hint {
      margin: 6px 4px 0;
      max-width: 100%;
      color: #82686d;
      font-size: 12.5px;
      line-height: 1.45;
      font-style: italic;
    }
    .ihc-typing-msg {
      margin-bottom: 16px;
    }
    .ihc-typing-bubble {
      width: fit-content;
      display: inline-flex;
      align-items: center;
      gap: 5px;
      border: 1px solid rgba(48,14,22,0.08);
      border-radius: 18px 18px 18px 7px;
      background: #f7f3ee;
      padding: 14px 16px;
      box-shadow: 0 1px 2px rgba(48,14,22,0.04);
    }
    .ihc-typing-bubble span {
      width: 7px;
      height: 7px;
      border-radius: 50%;
      background: #8a6f74;
      animation: ihc-typing 1.05s ease-in-out infinite;
    }
    .ihc-typing-bubble span:nth-child(2) { animation-delay: 0.12s; }
    .ihc-typing-bubble span:nth-child(3) { animation-delay: 0.24s; }
    @keyframes ihc-typing {
      0%, 65%, 100% { opacity: 0.32; transform: translateY(0); }
      32% { opacity: 1; transform: translateY(-3px); }
    }
    .ihc-user-bubble {
      max-width: min(540px, 86%);
      border-radius: 18px 18px 7px 18px;
      background: #dd5f3b;
      color: white;
      padding: 12px 15px;
      font-size: 15px;
      line-height: 1.5;
      white-space: pre-wrap;
    }
    .ihc-input-area {
      border-top: 1px solid rgba(48,14,22,0.08);
      background: rgba(255,255,255,0.96);
      padding: 12px 14px 14px;
    }
    .ihc-input-spacer {
      min-height: 70px;
    }
    .ihc-progress {
      display: flex;
      align-items: center;
      gap: 14px;
      border-top: 1px solid rgba(48,14,22,0.08);
      background: rgba(255,255,255,0.96);
      padding: 10px 14px 8px;
      opacity: 1;
      max-height: 48px;
      overflow: hidden;
      transform: translateY(0);
      transition:
        max-height 0.55s cubic-bezier(0.4, 0, 0.2, 1),
        opacity 0.4s ease,
        padding 0.55s cubic-bezier(0.4, 0, 0.2, 1),
        border-color 0.4s ease,
        transform 0.55s cubic-bezier(0.4, 0, 0.2, 1);
    }
    .ihc-progress.is-hidden {
      max-height: 0;
      opacity: 0;
      padding-top: 0;
      padding-bottom: 0;
      border-top-color: transparent;
      transform: translateY(-6px);
      pointer-events: none;
    }
    .ihc-progress + .ihc-input-area {
      border-top: none;
      padding-top: 6px;
    }
    .ihc-progress.is-hidden + .ihc-input-area {
      border-top: 1px solid rgba(48,14,22,0.08);
      padding-top: 12px;
    }
    .ihc-progress-label {
      color: #7b6367;
      font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
      font-size: 10px;
      font-weight: 700;
      letter-spacing: 0.12em;
      text-transform: uppercase;
      white-space: nowrap;
      flex: 0 0 auto;
    }
    .ihc-progress-bars {
      flex: 1 1 auto;
      display: grid;
      grid-template-columns: repeat(5, 1fr);
      gap: 6px;
      min-width: 0;
    }
    .ihc-progress-bar {
      height: 6px;
      border-radius: 999px;
      background: #e7dcda;
      transition: background 0.35s ease;
    }
    .ihc-progress-bar.is-done {
      background: #dd5f3b;
    }
    @media (prefers-reduced-motion: reduce) {
      .ihc-progress {
        transition: opacity 0.2s ease;
      }
      .ihc-progress.is-hidden {
        transform: none;
      }
      .ihc-progress-bar {
        transition: none;
      }
    }
    .ihc-input-label {
      margin: 0 0 8px;
      color: #7b6367;
      font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
      font-size: 10px;
      font-weight: 700;
      letter-spacing: 0.12em;
      text-transform: uppercase;
    }
    .ihc-input-row {
      position: relative;
      display: grid;
      grid-template-columns: minmax(0, 1fr) 44px;
      align-items: end;
      gap: 8px;
      border: 1px solid rgba(48,14,22,0.14);
      border-radius: 17px;
      background: #fffefd;
      padding: 8px 8px 8px 14px;
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.7);
      transition: border-color 0.15s ease, box-shadow 0.15s ease;
    }
    .ihc-input-row:focus-within {
      border-color: #dd5f3b;
      box-shadow:
        0 0 0 3px rgba(221,95,59,0.13),
        inset 0 1px 0 rgba(255,255,255,0.7);
    }
    .ihc-input {
      width: 100%;
      min-height: 44px;
      max-height: 124px;
      resize: none;
      border: none;
      border-radius: 0;
      outline: none;
      background: transparent;
      color: #2a1014;
      padding: 5px 0;
      font-family: inherit;
      font-size: 16px;
      line-height: 1.35;
      overflow-y: hidden;
      scrollbar-width: none;
    }
    .ihc-input::-webkit-scrollbar {
      display: none;
    }
    .ihc-input:focus {
      box-shadow: none;
    }
    .ihc-send {
      width: 44px;
      height: 44px;
      margin: 0;
      border: none;
      border-radius: 14px;
      background: #dd5f3b;
      color: white;
      font-size: 18px;
      line-height: 1;
      font-weight: 700;
      cursor: pointer;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      box-shadow: 0 4px 12px rgba(221,95,59,0.24);
      transition: background 0.15s ease, transform 0.15s ease, opacity 0.15s ease;
      touch-action: manipulation;
      -webkit-tap-highlight-color: transparent;
    }
    .ihc-send:hover:not(:disabled) {
      background: #c94e2e;
      transform: translateY(-1px);
    }
    @keyframes ihc-sonar-input {
      0% {
        box-shadow:
          0 0 0 0 rgba(221, 95, 59, 0.55),
          inset 0 1px 0 rgba(255,255,255,0.7);
      }
      80%, 100% {
        box-shadow:
          0 0 0 14px rgba(221, 95, 59, 0),
          inset 0 1px 0 rgba(255,255,255,0.7);
      }
    }
    .ihc-input-row.is-pulsing {
      border-color: #dd5f3b;
      animation: ihc-sonar-input 1.8s ease-out infinite;
    }
    @media (prefers-reduced-motion: reduce) {
      .ihc-input-row.is-pulsing {
        animation: none;
      }
    }
    .ihc-send:disabled {
      background: #e7dcda;
      color: #9a8588;
      box-shadow: none;
      opacity: 1;
      cursor: not-allowed;
    }
    .ihc-char-hint {
      margin: 6px 4px 0;
      color: #7b6367;
      font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
      font-size: 11px;
      font-weight: 700;
      letter-spacing: 0.06em;
      text-align: right;
    }
    .ihc-char-hint.is-warning {
      color: #c94e2e;
    }
    .ihc-err {
      margin: 0;
      padding: 0 18px 12px;
      color: #a83232;
      font-size: 14px;
      line-height: 1.45;
    }
    .ihc-brief-card {
      margin: 8px 0 18px 54px;
      border: 1px solid rgba(48,14,22,0.1);
      border-radius: 18px;
      background: #fffefd;
      padding: 16px 18px;
      box-shadow: 0 8px 22px rgba(48,14,22,0.06);
    }
    .ihc-brief-eyebrow {
      margin: 0 0 7px;
      color: #82686d;
      font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
      font-size: 10px;
      font-weight: 800;
      letter-spacing: 0.14em;
      text-transform: uppercase;
    }
    .ihc-brief-card h3 {
      margin: 0 0 12px;
      color: #2a1014;
      font-size: 18px;
      line-height: 1.2;
    }
    .ihc-brief-card dl {
      margin: 0;
      display: grid;
      gap: 14px;
    }
    .ihc-brief-card dl div {
      display: grid;
      grid-template-columns: 1fr;
      gap: 4px;
    }
    .ihc-brief-card dt {
      color: #82686d;
      font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
      font-size: 10px;
      font-weight: 800;
      letter-spacing: 0.12em;
      text-transform: uppercase;
    }
    .ihc-brief-card dd {
      margin: 0;
      color: #4a3035;
      font-size: 14.5px;
      line-height: 1.45;
    }
    .ihc-brief-pills {
      display: flex;
      flex-wrap: wrap;
      gap: 7px;
      margin-top: 13px;
    }
    .ihc-brief-pills span {
      border: 1px solid rgba(221,95,59,0.2);
      border-radius: 999px;
      background: #fff3ed;
      color: #8b3e28;
      padding: 5px 9px;
      font-size: 12px;
      font-weight: 700;
    }
    .ihc-draft-wrap {
      margin: 10px 0 18px 54px;
    }
    .ihc-newsletter-card {
      overflow: hidden;
      border: 1px solid rgba(48,14,22,0.1);
      border-radius: 22px;
      background: white;
      box-shadow: 0 14px 40px rgba(48,14,22,0.09);
    }
    .ihc-email-gate-overlay {
      position: absolute;
      left: 0;
      right: 0;
      bottom: 0;
      /* Overlay starts well above the readable CTA so the gradient has
         room for a long, smooth fade from transparent (newsletter visible)
         to opaque white (newsletter hidden). We deliberately avoid both
         mask-image (broken on iOS Safari, hides backdrop-filter too) and
         backdrop-filter (creates a hard visual edge at the overlay's top
         boundary). The gradient alone produces the cleanest fade. */
      top: 18%;
      display: flex;
      flex-direction: column;
      justify-content: flex-end;
      padding: 28px 16px 18px;
      /* Long smooth fade across the top ~36% of the overlay, then
         fully opaque white from 36% down. Tuned so the "Want to read..."
         CTA always sits on solid white (CTA section ~240px tall on
         mobile, overlay ~390-443px tall depending on card height — so
         CTA's top edge lands inside the solid region). */
      background: linear-gradient(180deg,
        rgba(255,254,253,0) 0%,
        rgba(255,254,253,0.04) 8%,
        rgba(255,254,253,0.12) 14%,
        rgba(255,254,253,0.24) 20%,
        rgba(255,254,253,0.42) 25%,
        rgba(255,254,253,0.62) 30%,
        rgba(255,254,253,0.86) 34%,
        #fffefd 38%,
        #fffefd 100%);
      border-bottom-left-radius: 24px;
      border-bottom-right-radius: 24px;
      pointer-events: auto;
      animation: ihc-gate-rise 520ms cubic-bezier(0.2, 0.9, 0.3, 1);
      z-index: 5;
    }
    .ihc-email-gate-overlay .ihc-email-gate {
      border-top: none;
      background: transparent;
      padding: 0;
    }
    .ihc-email-gate-overlay.is-unlocking {
      top: 0;
      padding: clamp(28px, 6vh, 56px) 18px 24px;
      background: #fffefd;
      backdrop-filter: none;
      -webkit-backdrop-filter: none;
      mask-image: none;
      -webkit-mask-image: none;
      border-radius: 22px;
      justify-content: center;
    }
    .ihc-unlock-progress {
      padding: 0 4px 6px;
    }
    .ihc-unlock-progress h3 {
      margin: 0 0 12px;
      color: #3a1119;
      font-family: var(--font-display, Georgia, serif);
      font-size: clamp(18px, 2vw, 22px);
      font-weight: 600;
      line-height: 1.2;
      text-align: center;
    }
    .ihc-unlock-progress .ihc-work-card {
      background: rgba(255,255,255,0.85);
    }
    @keyframes ihc-gate-rise {
      from {
        opacity: 0;
        transform: translateY(28%);
      }
      to {
        opacity: 1;
        transform: translateY(0);
      }
    }
    @media (prefers-reduced-motion: reduce) {
      .ihc-email-gate-overlay {
        animation: none;
      }
    }
    .ihc-newsletter-body {
      min-height: 260px;
      padding: 20px 22px 24px;
      position: relative;
    }
    .ihc-newsletter-body h2 {
      max-width: 100%;
      margin: 0 0 14px;
      color: #3a1119;
      font-family: var(--font-display, Georgia, serif);
      font-size: clamp(22px, 2.6vw, 28px);
      font-weight: 600;
      line-height: 1.18;
      letter-spacing: 0;
    }
    .ihc-preview-prose {
      color: #4a3035;
      font-size: 15px;
      line-height: 1.65;
    }
    .ihc-preview-prose p {
      margin: 0 0 14px;
    }
    .ihc-newsletter-card.is-locked .ihc-preview-prose {
      max-height: 280px;
      overflow: hidden;
      user-select: none;
    }
    .ihc-card-working {
      padding: 16px 16px 4px;
    }
    .ihc-work-card {
      border: 1px solid rgba(14,124,120,0.16);
      border-radius: 14px;
      background: #f3faf8;
      padding: 12px 13px;
    }
    .ihc-work-note {
      margin: 0 0 10px;
      color: #2a1014;
      font-size: 13.5px;
      font-weight: 700;
      line-height: 1.35;
    }
    .ihc-work-head {
      display: flex;
      align-items: center;
      gap: 12px;
      margin-bottom: 13px;
    }
    .ihc-work-list {
      list-style: none;
      padding: 0;
      margin: 0;
      display: grid;
      gap: 7px;
    }
    .ihc-work-step {
      display: flex;
      align-items: center;
      gap: 10px;
      color: #7a6668;
      font-size: 13px;
    }
    .ihc-work-step.done {
      color: #145f5b;
    }
    .ihc-work-step.active {
      color: #2a1014;
      font-weight: 700;
    }
    .ihc-work-dot {
      width: 12px;
      height: 12px;
      border-radius: 50%;
      border: 1px solid #9bcac5;
      background: white;
      flex: 0 0 auto;
    }
    .ihc-work-step.done .ihc-work-dot {
      background: #0e7c78;
      border-color: #0e7c78;
    }
    .ihc-work-step.active .ihc-work-dot {
      border-color: #0e7c78;
      box-shadow: 0 0 0 4px rgba(14,124,120,0.12);
      animation: ihc-work-pulse 1.2s ease-in-out infinite;
    }
    @keyframes ihc-work-pulse {
      0%, 100% { transform: scale(1); }
      50% { transform: scale(0.76); }
    }
    .ihc-caret {
      display: inline-block;
      width: 0.08em;
      height: 0.85em;
      margin-left: 2px;
      background: #dd5f3b;
      animation: ihc-caret 0.8s step-end infinite;
      vertical-align: -0.06em;
    }
    .ihc-caret.inline {
      height: 1em;
    }
    @keyframes ihc-caret {
      50% { opacity: 0; }
    }
    .ihc-draft-skeleton {
      display: grid;
      gap: 16px;
    }
    .ihc-draft-skeleton span {
      display: block;
      height: 18px;
      width: 74%;
      border-radius: 999px;
      background: linear-gradient(90deg, #f4eded, #fff7f2, #f4eded);
      background-size: 220% 100%;
      animation: ihc-skeleton 1.35s ease-in-out infinite;
    }
    .ihc-draft-skeleton span.wide { width: 92%; height: 46px; }
    .ihc-draft-skeleton span.short { width: 42%; }
    .ihc-draft-skeleton span.para { width: 88%; height: 14px; }
    @keyframes ihc-skeleton {
      0% { background-position: 0% 50%; }
      100% { background-position: -220% 50%; }
    }
    .ihc-email-gate {
      border-top: 1px solid rgba(48,14,22,0.08);
      background: linear-gradient(180deg, #fffefd 0%, #fdf5ef 100%);
      padding: 22px 22px 22px;
      text-align: center;
    }
    .ihc-email-gate h3 {
      margin: 0 0 6px;
      color: #3a1119;
      font-family: var(--font-display, Georgia, serif);
      font-size: clamp(20px, 2.4vw, 26px);
      font-weight: 600;
      line-height: 1.2;
      letter-spacing: 0;
    }
    .ihc-email-gate p {
      max-width: 380px;
      margin: 0 auto 14px;
      color: #6b4d53;
      font-size: 13.5px;
      line-height: 1.5;
    }
    .ihc-email-gate .ihc-gate-err {
      margin: -4px auto 12px;
      max-width: 380px;
      color: #a83232;
      font-size: 13.5px;
      font-weight: 600;
      line-height: 1.45;
    }
    .ihc-email-row {
      display: flex;
      align-items: stretch;
      gap: 6px;
      max-width: 400px;
      margin: 0 auto;
      padding: 5px;
      border: 1px solid rgba(48,14,22,0.18);
      border-radius: 999px;
      background: white;
      box-shadow: 0 4px 14px rgba(48,14,22,0.06);
    }
    .ihc-email-row:focus-within {
      border-color: #dd5f3b;
      box-shadow: 0 0 0 3px rgba(221,95,59,0.13), 0 4px 14px rgba(48,14,22,0.06);
    }
    .ihc-email-input {
      flex: 1 1 auto;
      min-width: 0;
      min-height: 42px;
      border: none;
      background: transparent;
      color: #2a1014;
      padding: 0 14px;
      font-family: inherit;
      font-size: 15px;
      outline: none;
    }
    .ihc-email-input:focus {
      outline: none;
      box-shadow: none;
    }
    .ihc-unlock-btn {
      flex: 0 0 auto;
      min-height: 42px;
      border: none;
      border-radius: 999px;
      background: #dd5f3b;
      color: white;
      padding: 0 18px;
      font-family: inherit;
      font-size: 14px;
      font-weight: 700;
      cursor: pointer;
      white-space: nowrap;
      transition: background 0.15s ease, transform 0.15s ease;
    }
    .ihc-unlock-btn:hover:not(:disabled) {
      background: #bc4629;
      transform: translateY(-1px);
    }
    .ihc-unlock-btn:disabled {
      background: #b9a6a8;
      cursor: not-allowed;
      transform: none;
    }
    .ihc-honeypot {
      position: absolute;
      left: -9999px;
      top: -9999px;
      width: 0;
      height: 0;
      opacity: 0;
    }
    .ihc-toggle {
      position: fixed;
      left: 16px;
      bottom: 16px;
      z-index: 9999;
      min-width: 200px;
      border: 1px solid rgba(48,14,22,0.15);
      border-radius: 14px;
      background: white;
      box-shadow: 0 4px 16px rgba(48,14,22,0.1);
      padding: 12px 14px;
      font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
      font-size: 11px;
    }
    .ihc-toggle h5 {
      margin: 0 0 6px;
      color: #9b8588;
      font-size: 10px;
      letter-spacing: 0.14em;
      text-transform: uppercase;
    }
    .ihc-toggle .row {
      display: flex;
      gap: 4px;
      margin-bottom: 10px;
    }
    .ihc-toggle .opt {
      flex: 1;
      border: 1px solid rgba(48,14,22,0.12);
      border-radius: 6px;
      background: white;
      padding: 4px 6px;
      cursor: pointer;
      font-family: inherit;
      font-size: 10.5px;
    }
    .ihc-toggle .opt[aria-current="true"] {
      border-color: #2a1014;
      background: #2a1014;
      color: white;
    }
    .toggle-note {
      margin-top: 4px;
      color: #9b8588;
      font-size: 9px;
    }
    @media (max-width: 760px) {
      .ihc-shell {
        min-height: calc(100svh - 121px);
        display: flex;
        flex-direction: column;
        align-items: stretch;
        justify-content: flex-start;
        padding: clamp(28px, 5vh, 56px) 16px clamp(16px, 2vh, 22px);
        gap: clamp(14px, 2.2vh, 22px);
      }
      .ihc-text-side {
        flex: 0 0 auto;
        max-width: 100%;
        text-align: center;
      }
      .ihc-chat-side {
        flex: 0 0 auto;
        min-height: 0;
        justify-content: center;
      }
      .ihc-headline {
        /* Smaller than the desktop clamp(38, 4.4vw, 62) so the chat input
           stays above the fold on iPhone SE 2/3 (375x667) and bigger. */
        font-size: clamp(28px, 6.4vw, 40px);
        line-height: 1.1;
        letter-spacing: -0.02em;
        margin: 0 auto clamp(6px, 0.8vh, 12px);
        text-align: center;
        max-width: 640px;
      }
      .ihc-sub {
        display: block;
        font-size: clamp(14.5px, 3.2vw, 16px);
        line-height: 1.5;
        max-width: 540px;
        margin: 0 auto;
        text-align: center;
      }
      .ihc-chat-card {
        flex: 0 0 auto;
        width: 100%;
        max-width: 100%;
        /* The in-flow mobile card stays compact. On input focus, the
           body-level portal below takes over and sizes against
           visualViewport so the keyboard does not cover the composer. */
        height: clamp(280px, 56dvh, 540px);
        max-height: 540px;
        min-height: 280px;
        border-radius: 18px;
      }
      .ihc-chat-placeholder {
        visibility: hidden;
        pointer-events: none;
      }
      .ihc-mobile-portal.is-isolated .ihc-chat-card {
        height: 100%;
        max-height: none;
        min-height: 0;
        border-radius: 0;
      }
      .ihc-mobile-portal.is-isolated .ihc-convo {
        padding-top: max(14px, env(safe-area-inset-top));
      }
      .ihc-mobile-portal.is-isolated .ihc-input-area {
        padding-bottom: max(14px, env(safe-area-inset-bottom));
      }
      .ihc-progress {
        gap: 10px;
        padding: 8px 12px 6px;
      }
      .ihc-progress-label {
        font-size: 9.5px;
        letter-spacing: 0.1em;
      }
      .ihc-progress-bars {
        gap: 5px;
      }
      .ihc-progress-bar {
        height: 5px;
      }

      .ihc-fullscreen-close {
        display: none;
      }
      .ihc-convo {
        padding: 18px 14px 14px;
      }
      .ihc-msg-body {
        max-width: calc(100% - 50px);
      }
      .ihc-draft-wrap {
        margin-left: 0;
      }
      .ihc-brief-card {
        margin-left: 0;
      }
      .ihc-newsletter-body {
        min-height: 220px;
        padding: 18px 18px 24px;
      }
      .ihc-card-working {
        padding: 14px 14px 14px;
      }
      .ihc-email-gate {
        padding: 20px 16px 22px;
      }
      .ihc-email-row {
        flex-direction: column;
        border: none;
        background: transparent;
        box-shadow: none;
        padding: 0;
        gap: 8px;
      }
      .ihc-email-row:focus-within {
        box-shadow: none;
      }
      .ihc-email-input {
        min-height: 46px;
        border: 1px solid rgba(48,14,22,0.18);
        border-radius: 12px;
        background: white;
        padding: 0 14px;
        font-size: 16px;
      }
      .ihc-unlock-btn {
        min-height: 46px;
        border-radius: 12px;
        width: 100%;
        font-size: 15px;
      }
      .ihc-toggle {
        display: none;
      }
    }
  `;

  window.HeroIndieChat = HeroIndieChat;
})();
