const STAGES = {
  HOME: "home",
  OB1: "ob1",
  OB2: "ob2",
  OB3: "ob3",
  OB4: "ob4",
  OB5: "ob5",
  GEN: "gen",
  BLUEPRINT: "blueprint",
};

const STEP_ORDER = [STAGES.OB1, STAGES.OB2, STAGES.OB3, STAGES.OB4, STAGES.OB5];

const ROUTES = {
  HOME: "/",
  ONBOARDING: "/onboarding",
  GENERATION: "/generation",
  BLUEPRINT: "/blueprint",
  DASHBOARD: "/dashboard",
  CHAT: "/chat",
  MAP: "/map",
  CHECKOUT: "/checkout",
  INSTANT_DOWNLOADS: "/instant-downloads",
  CONFIRMATION: "/confirmation",
  NATAL: "/natal",
  METHODOLOGY: "/methodology",
  VEDIC: "/vedic",
  RESEARCH: "/research",
  ABOUT: "/about",
  SCROLLS: "/scrolls",
  PRESS: "/press",
  CAREERS: "/careers",
  CONTACT: "/contact",
  PRIVACY: "/privacy",
  TERMS: "/terms",
  DATA_DELETION: "/data-deletion",
  COOKIES: "/cookies",
  NEWSLETTER: "/newsletter",
  SUBSTACK: "/substack",
  RSS: "/rss",
};

const PROTECTED_ROUTES = new Set([
  ROUTES.BLUEPRINT,
  ROUTES.DASHBOARD,
  ROUTES.CHAT,
  ROUTES.MAP,
  ROUTES.CHECKOUT,
  ROUTES.CONFIRMATION,
  ROUTES.NATAL,
  ROUTES.GENERATION,
]);

const DEFAULT_FORM = {
  givenName: "",
  familyName: "",
  birthDate: "",
  birthHour: "",
  birthMinute: "",
  birthPeriod: "AM",
  birthTimeAccuracy: "Exact",
  birthLocation: "",
  latitude: "",
  longitude: "",
  timezone: "",
  assessmentAnswer: 3,
  mbtiType: "",
  enneagramType: "",
  knowsType: null,
  mbtiAnswers: [],
  tier: "atlas",
  relationshipPairing: true,
};

const MBTI_TYPES = [
  "ISTJ","ISFJ","INFJ","INTJ",
  "ISTP","ISFP","INFP","INTP",
  "ESTP","ESFP","ENFP","ENTP",
  "ESTJ","ESFJ","ENFJ","ENTJ",
];
const ENNEAGRAM_TYPES = [
  { n: 1, label: "The Reformer" },
  { n: 2, label: "The Helper" },
  { n: 3, label: "The Achiever" },
  { n: 4, label: "The Individualist" },
  { n: 5, label: "The Investigator" },
  { n: 6, label: "The Loyalist" },
  { n: 7, label: "The Enthusiast" },
  { n: 8, label: "The Challenger" },
  { n: 9, label: "The Peacemaker" },
];
// 12 questions, 3 per dichotomy. Each {q, letter} where letter = the one it indicates if agreed.
const MBTI_QUESTIONS = [
  // E/I
  { q: "After a long social event, I feel energized rather than drained.", letter: "E" },
  { q: "I prefer to think things through carefully before I speak.", letter: "I" },
  { q: "I'd rather have many acquaintances than a small circle of close friends.", letter: "E" },
  // S/N
  { q: "I trust concrete facts and experience more than abstract theories.", letter: "S" },
  { q: "I often think about future possibilities rather than what's directly in front of me.", letter: "N" },
  { q: "I'm drawn to practical, hands-on solutions over imaginative ones.", letter: "S" },
  // T/F
  { q: "I make decisions based primarily on logic, even when feelings are involved.", letter: "T" },
  { q: "I find it natural and important to tune into how others are feeling.", letter: "F" },
  { q: "I value truth over tact when giving someone feedback.", letter: "T" },
  // J/P
  { q: "I prefer having a clear plan rather than keeping my options open.", letter: "J" },
  { q: "I enjoy starting new projects more than finishing existing ones.", letter: "P" },
  { q: "I feel most comfortable when my environment is organized and structured.", letter: "J" },
];

function scoreMbti(answers) {
  // answers = array of 12 booleans (true = agree)
  if (!answers || answers.length < 12) return "";
  const score = { E: 0, I: 0, S: 0, N: 0, T: 0, F: 0, J: 0, P: 0 };
  MBTI_QUESTIONS.forEach((q, i) => {
    const agreed = answers[i];
    const letter = q.letter;
    const opposite = { E:"I", I:"E", S:"N", N:"S", T:"F", F:"T", J:"P", P:"J" }[letter];
    if (agreed) score[letter]++;
    else score[opposite]++;
  });
  return (score.E >= score.I ? "E" : "I") +
         (score.S >= score.N ? "S" : "N") +
         (score.T >= score.F ? "T" : "F") +
         (score.J >= score.P ? "J" : "P");
}

const ACCURACY_OPTIONS = ["Exact", "±15 min", "±1 hour", "Unknown"];
const ASSESSMENT_OPTIONS = [
  "Strongly disagree",
  "Disagree",
  "Neutral",
  "Agree",
  "Strongly agree",
];
const _IS_SENSEI_V = typeof window !== "undefined" && window.TAMASHI_VARIANT === "sensei";
const _IS_AMARA_V = typeof window !== "undefined" && window.SCI_IS_AMARA;
const TIER_OPTIONS = _IS_AMARA_V ? [
  { name: "daily_sign",    label: "The Daily Sign",     price: "$19", per: "per month" },
  { name: "love_union",    label: "Love & Union",       price: "$39", per: "once" },
  { name: "atlas",         label: "The Atlas",          price: "$79", per: "once · keep forever" },
  { name: "whole_world",   label: "The Whole World",    price: "$129", per: "once · lifetime" },
] : _IS_SENSEI_V ? [
  { name: "Glimpse",   label: "Shoden · 初伝", price: "$0",  per: "forever" },
  { name: "Becoming",  label: "Chuden · 中伝", price: "$49", per: "once" },
] : [
  { name: "Glimpse",   label: "Starter Reading", price: "$0",  per: "forever" },
  { name: "Becoming",  label: "Full Reading",    price: "$49", per: "once" },
];

const INSTRUMENT_LABELS = [
  "Big Five (OCEAN)",
  "MBTI 16-Type",
  "Enneagram",
  "Life Path",
  "Human Design",
  "Numerology",
  "Western Astrology",
  "Vedic Astrology",
  "Astrocartography",
  "Gene Keys",
  "I Ching",
  "Chinese Astrology",
];

function pad2(value) {
  return String(value || "").padStart(2, "0");
}

function normalizePathname(pathname) {
  const raw = String(pathname || "/").trim();
  if (!raw || raw === "/" || raw === "/tamashi.html") return "/";
  const noQuery = raw.split("?")[0].split("#")[0];
  const clean =
    noQuery.endsWith("/") && noQuery !== "/" ? noQuery.slice(0, -1) : noQuery;
  return clean || "/";
}

function parseRouteContext(pathname) {
  const clean = normalizePathname(pathname);
  return { variant: "sensei", route: clean };
}

function buildPathForVariant(route, variant) {
  return normalizePathname(route);
}

function formatUsDateFromDigits(raw) {
  const digits = String(raw || "")
    .replace(/\D/g, "")
    .slice(0, 8);
  if (!digits) return "";
  if (digits.length <= 2) return digits;
  if (digits.length <= 4) return `${digits.slice(0, 2)} / ${digits.slice(2)}`;
  return `${digits.slice(0, 2)} / ${digits.slice(2, 4)} / ${digits.slice(4)}`;
}

function parseUsDate(dateText) {
  const digits = String(dateText || "").replace(/\D/g, "");
  if (digits.length !== 8) return null;
  const month = Number(digits.slice(0, 2));
  const day = Number(digits.slice(2, 4));
  const year = Number(digits.slice(4, 8));
  if (year < 1900 || year > 2100) return null;
  if (month < 1 || month > 12) return null;
  const maxDay = new Date(year, month, 0).getDate();
  if (day < 1 || day > maxDay) return null;
  return {
    month: pad2(month),
    day: pad2(day),
    year: String(year),
    us: `${pad2(month)} / ${pad2(day)} / ${year}`,
    iso: `${year}-${pad2(month)}-${pad2(day)}`,
  };
}

function isoToUsDate(iso) {
  if (!iso || typeof iso !== "string") return {};
  const m = iso.match(/^(\d{4})-(\d{2})-(\d{2})$/);
  if (!m) return "";
  return `${m[2]} / ${m[3]} / ${m[1]}`;
}

function parseTimeParts(timeText) {
  if (!timeText || typeof timeText !== "string") return {};
  const m = timeText.match(/^(\d{1,2}):(\d{2})/);
  if (!m) return {};
  return {
    birthHour: pad2(m[1]),
    birthMinute: pad2(m[2]),
  };
}

function buildBirthTime(form) {
  const hh = String(form.birthHour || "").trim();
  const mm = String(form.birthMinute || "").trim();
  if (!hh || !mm) return null;
  if (!/^\d{1,2}$/.test(hh) || !/^\d{1,2}$/.test(mm)) return null;
  let hour = parseInt(hh, 10);
  const period = String(form.birthPeriod || "").toUpperCase();
  if (period === "AM") { if (hour === 12) hour = 0; }
  else if (period === "PM") { if (hour !== 12) hour += 12; }
  return `${pad2(hour)}:${pad2(mm)}`;
}

function normalizeForm(payload, profile) {
  const merged = { ...DEFAULT_FORM };

  const source = {
    ...(payload || {}),
    ...(profile?.onboarding_payload || {}),
  };

  if (profile?.given_name) merged.givenName = profile.given_name;
  if (profile?.family_name) merged.familyName = profile.family_name;
  if (profile?.birth_date) merged.birthDate = isoToUsDate(profile.birth_date);
  if (profile?.birth_time)
    Object.assign(merged, parseTimeParts(profile.birth_time));
  if (profile?.birth_time_accuracy)
    merged.birthTimeAccuracy = profile.birth_time_accuracy;
  if (profile?.birth_location) merged.birthLocation = profile.birth_location;
  if (profile?.tier) merged.tier = profile.tier;
  if (profile?.add_ons && typeof profile.add_ons === "object") {
    merged.relationshipPairing = Boolean(
      profile.add_ons.relationship_pairing ?? merged.relationshipPairing,
    );
  }

  if (source.givenName || source.given_name)
    merged.givenName = source.givenName || source.given_name;
  if (source.familyName || source.family_name)
    merged.familyName = source.familyName || source.family_name;
  if (source.birthDate)
    merged.birthDate = formatUsDateFromDigits(source.birthDate);
  if (!source.birthDate && source.birthYear && source.birthMonthDay) {
    merged.birthDate = formatUsDateFromDigits(
      `${source.birthMonthDay}/${source.birthYear}`,
    );
  }
  if (!source.birthDate && source.birthDateIso) {
    merged.birthDate = isoToUsDate(source.birthDateIso);
  }
  if (source.birthHour) merged.birthHour = source.birthHour;
  if (source.birthMinute) merged.birthMinute = source.birthMinute;
  if (source.birthPeriod) merged.birthPeriod = source.birthPeriod;
  if (source.birthTimeAccuracy || source.birth_time_accuracy) {
    merged.birthTimeAccuracy =
      source.birthTimeAccuracy || source.birth_time_accuracy;
  }
  if (source.birthLocation || source.birth_location) {
    merged.birthLocation = source.birthLocation || source.birth_location;
  }
  if (source.latitude) merged.latitude = source.latitude;
  if (source.longitude) merged.longitude = source.longitude;
  if (source.timezone) merged.timezone = source.timezone;
  if (typeof source.assessmentAnswer === "number")
    merged.assessmentAnswer = source.assessmentAnswer;
  if (source.mbtiType) merged.mbtiType = source.mbtiType;
  if (source.enneagramType) merged.enneagramType = source.enneagramType;
  if (source.knowsType != null) merged.knowsType = source.knowsType;
  if (Array.isArray(source.mbtiAnswers)) merged.mbtiAnswers = source.mbtiAnswers;
  if (source.tier) merged.tier = source.tier;
  if (typeof source.relationshipPairing === "boolean")
    merged.relationshipPairing = source.relationshipPairing;

  return merged;
}

function buildReportInput(form, user) {
  const parsedBirthDate = parseUsDate(form.birthDate);
  return {
    userId: user?.id || null,
    email: user?.email || null,
    givenName: form.givenName,
    familyName: form.familyName,
    birthDate: form.birthDate,
    birthYear: parsedBirthDate?.year || "",
    birthMonthDay: parsedBirthDate
      ? `${parsedBirthDate.month} / ${parsedBirthDate.day}`
      : "",
    birthDateIso: parsedBirthDate?.iso || null,
    birthHour: form.birthHour,
    birthMinute: form.birthMinute,
    birthPeriod: form.birthPeriod,
    birthTime: buildBirthTime(form),
    birthTimeAccuracy: form.birthTimeAccuracy,
    birthLocation: form.birthLocation,
    latitude: form.latitude,
    longitude: form.longitude,
    timezone: form.timezone,
    assessmentAnswer: form.assessmentAnswer,
    mbtiType: form.mbtiType || scoreMbti(form.mbtiAnswers) || "",
    enneagramType: form.enneagramType || "",
    tier: form.tier,
    relationshipPairing: form.relationshipPairing,
  };
}

function parseReport(markdownText) {
  const text = String(markdownText || "").trim();
  if (!text) {
    return {
      title: "Your Becoming.",
      summary: "Run your first report to see the generated interpretation.",
      sections: [],
    };
  }

  const lines = text.split(/\r?\n/);
  const sections = [];
  let current = null;

  lines.forEach((raw) => {
    const line = raw.trim();
    if (!line) return;

    const h = line.match(/^#{1,6}\s+(.+)/);
    if (h) {
      current = { heading: h[1].trim(), paragraphs: [] };
      sections.push(current);
      return;
    }

    if (!current) {
      current = { heading: "Overview", paragraphs: [] };
      sections.push(current);
    }

    current.paragraphs.push(line.replace(/^[-*]\s+/, ""));
  });

  const firstHeading = sections[0]?.heading || "Your Becoming";
  const firstParagraph =
    sections.find((s) => s.paragraphs.length > 0)?.paragraphs[0] ||
    text.slice(0, 220);

  return {
    title: firstHeading.endsWith(".") ? firstHeading : `${firstHeading}.`,
    summary: firstParagraph,
    sections,
  };
}

function buildReportHTML(parsed, form, reportMeta) {
  var name = [form.givenName, form.familyName].filter(Boolean).join(" ") || "Your Blueprint";
  var dob = form.birthDate || "";
  var date = reportMeta && reportMeta.created_at
    ? new Date(reportMeta.created_at).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" })
    : new Date().toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" });

  var bf5Vals = [0.88, 0.72, 0.41, 0.63, 0.22];
  var bf5Labels = ["Openness", "Conscientiousness", "Extraversion", "Agreeableness", "Neuroticism"];
  var bf5Rows = bf5Vals.map(function(v, i) {
    var y = 5 + i * 24;
    var ty = 15 + i * 24;
    var fillW = Math.round(200 * v);
    var fillColor = i === 2 ? "#c65a2a" : "#111111";
    return (
      '<text x="0" y="' + ty + '" font-family="Georgia,serif" font-size="10" fill="#3a3a36">' + bf5Labels[i] + '</text>' +
      '<rect x="130" y="' + y + '" width="200" height="10" fill="#e4dfcf" rx="1"/>' +
      '<rect x="130" y="' + y + '" width="' + fillW + '" height="10" fill="' + fillColor + '" rx="1"/>' +
      '<text x="335" y="' + ty + '" font-family="Georgia,serif" font-size="10" fill="#6e6a60" text-anchor="end">' + Math.round(v * 100) + '%</text>'
    );
  }).join("");
  var bf5Svg = '<svg viewBox="0 0 340 130" xmlns="http://www.w3.org/2000/svg" style="width:100%;max-width:340px">' + bf5Rows + '</svg>';

  var sectionsHtml = (parsed.sections || []).map(function(sec, i) {
    var num = String(i + 1).padStart(2, "0");
    var paras = (sec.paragraphs || []).map(function(p, pi) {
      var sz = pi === 0 ? "17" : "15";
      var lh = pi === 0 ? "1.65" : "1.7";
      var col = pi === 0 ? "#111111" : "#3a3a36";
      var fw = pi === 0 ? "font-weight:500;" : "";
      return '<p style="font-family:Georgia,serif;font-size:' + sz + 'px;line-height:' + lh + ';color:' + col + ';margin:0 0 16px;' + fw + '">' + p + '</p>';
    }).join("");
    var chart = i === 0 ? '<div style="margin-bottom:28px">' + bf5Svg + '</div>' : "";
    return (
      '<div style="padding:56px 72px;border-bottom:1px solid #1c1c1c;page-break-inside:avoid">' +
        '<div style="display:flex;align-items:baseline;gap:24px;margin-bottom:28px">' +
          '<span style="font-family:Georgia,serif;font-size:11px;letter-spacing:0.2em;color:#6e6a60;text-transform:uppercase">\u00a7 ' + num + '</span>' +
          '<h2 style="font-family:Georgia,serif;font-size:32px;font-weight:500;letter-spacing:-0.4px;line-height:1;color:#111111;margin:0">' + sec.heading + '</h2>' +
        '</div>' +
        chart +
        paras +
      '</div>'
    );
  }).join("");

  var css = [
    "*{box-sizing:border-box;margin:0;padding:0}",
    "html,body{font-family:Georgia,serif;background:#f3efe4;color:#111111}",
    "@page{margin:0;size:letter}",
    "@media print{",
    ".no-print{display:none!important}",
    ".cover{background:#1a1714!important;-webkit-print-color-adjust:exact;print-color-adjust:exact}",
    "}",
    ".cover{background:#1a1714;color:#ede8db;min-height:100vh;display:flex;flex-direction:column;justify-content:space-between;padding:72px;page-break-after:always;position:relative;overflow:hidden}",
    ".cover-ghost{position:absolute;right:72px;top:50%;transform:translateY(-50%);font-family:Georgia,serif;font-size:320px;line-height:1;color:#fff;opacity:0.04;pointer-events:none;user-select:none}",
    ".print-btn{position:fixed;bottom:32px;right:32px;background:#c65a2a;color:#f3efe4;border:none;font-family:Georgia,serif;font-size:14px;letter-spacing:0.1em;text-transform:uppercase;padding:14px 28px;cursor:pointer;z-index:999}",
  ].join("");

  return (
    '<!DOCTYPE html><html lang="en"><head>' +
    '<meta charset="UTF-8"/>' +
    '<title>' + name + ' \u00b7 Amara Atlas</title>' +
    '<style>' + css + '</style>' +
    '</head><body>' +

    // Cover
    '<div class="cover">' +
      '<div class="cover-ghost">\u9b42</div>' +
      '<div>' +
        '<div style="display:flex;align-items:center;gap:12px">' +
          '<div style="border:1px solid #3a3530;padding:8px 14px;font-size:18px">\u9b42</div>' +
          '<span style="font-size:11px;letter-spacing:0.3em;color:#706a5e;text-transform:uppercase">TAMASHI \u00b7 SOUL</span>' +
        '</div>' +
      '</div>' +
      '<div>' +
        '<div style="font-size:11px;letter-spacing:0.25em;color:#706a5e;text-transform:uppercase;margin-bottom:20px">YOUR BLUEPRINT</div>' +
        '<h1 style="font-family:Georgia,serif;font-size:72px;font-weight:400;line-height:0.95;letter-spacing:-1px;color:#ede8db;margin-bottom:32px">' + name + '.</h1>' +
        '<div style="display:flex;gap:32px">' +
          '<div><div style="font-size:10px;letter-spacing:0.2em;color:#706a5e;text-transform:uppercase;margin-bottom:4px">Date of birth</div><div style="font-size:16px;color:#afa99c">' + (dob || "\u2014") + '</div></div>' +
          '<div><div style="font-size:10px;letter-spacing:0.2em;color:#706a5e;text-transform:uppercase;margin-bottom:4px">Generated</div><div style="font-size:16px;color:#afa99c">' + date + '</div></div>' +
          '<div><div style="font-size:10px;letter-spacing:0.2em;color:#706a5e;text-transform:uppercase;margin-bottom:4px">Systems</div><div style="font-size:16px;color:#afa99c">12</div></div>' +
        '</div>' +
      '</div>' +
      '<div style="display:flex;justify-content:space-between;align-items:center">' +
        '<div style="font-size:11px;letter-spacing:0.2em;color:#3a3530;text-transform:uppercase">tamashi.me</div>' +
        '<div style="font-size:11px;letter-spacing:0.15em;color:#3a3530;text-transform:uppercase">\u9b42 \u00b7 THE ANCIENT. THE MODERN. YOU.</div>' +
      '</div>' +
    '</div>' +

    // Summary
    '<div style="padding:72px 72px 48px;border-bottom:1px solid #1c1c1c;background:#f3efe4">' +
      '<div style="font-size:11px;letter-spacing:0.25em;color:#6e6a60;text-transform:uppercase;margin-bottom:16px">\u00a7 00 \u00b7 Summary</div>' +
      '<h1 style="font-family:Georgia,serif;font-size:52px;font-weight:500;letter-spacing:-0.8px;line-height:0.95;margin-bottom:28px">' + parsed.title + '</h1>' +
      '<p style="font-family:Georgia,serif;font-size:17px;line-height:1.65;color:#3a3a36;max-width:680px">' + parsed.summary + '</p>' +
    '</div>' +

    // Sections
    sectionsHtml +

    // Footer
    '<div style="padding:48px 72px;display:flex;justify-content:space-between;align-items:center;background:#f3efe4">' +
      '<div style="font-size:11px;letter-spacing:0.2em;color:#6e6a60;text-transform:uppercase">\u9b42 / END \u00b7 ' + name + '</div>' +
      '<div style="font-size:11px;letter-spacing:0.2em;color:#6e6a60;text-transform:uppercase">tamashi.me</div>' +
    '</div>' +

    '<button class="no-print print-btn" onclick="window.print()">\u2193 Save as PDF</button>' +
    '</body></html>'
  );
}

function downloadReportPDF(parsed, form, reportMeta) {
  const html = buildReportHTML(parsed, form, reportMeta);
  const blob = new Blob([html], { type: "text/html;charset=utf-8" });
  const url = URL.createObjectURL(blob);
  const win = window.open(url, "_blank");
  if (!win) {
    alert("Allow pop-ups for tamashi.me to download your PDF.");
  }
  // Clean up blob URL after window loads
  setTimeout(() => URL.revokeObjectURL(url), 10000);
}

function formatErrorMessage(error) {
  if (!error) return "Something went wrong.";
  if (typeof error === "string") return error;
  if (error?.message) return error.message;
  return "Something went wrong.";
}

function useIsMobile() {
  const [mobile, setMobile] = React.useState(() => window.innerWidth <= 800);
  React.useEffect(() => {
    const onResize = () => setMobile(window.innerWidth <= 800);
    window.addEventListener("resize", onResize);
    return () => window.removeEventListener("resize", onResize);
  }, []);
  return mobile;
}

function useDebouncedValue(value, delayMs) {
  const [debounced, setDebounced] = React.useState(value);
  React.useEffect(() => {
    const t = setTimeout(() => setDebounced(value), delayMs);
    return () => clearTimeout(t);
  }, [value, delayMs]);
  return debounced;
}

function InlineError({ message }) {
  if (!message) return null;
  return (
    <div
      style={{
        fontFamily: SCI.mono,
        fontSize: 10,
        color: SCI.orange,
        marginTop: 6,
        letterSpacing: 0.4,
      }}
    >
      {message}
    </div>
  );
}

function StatusChip({ kind = "info", children }) {
  const tone =
    kind === "error"
      ? { bg: "rgba(var(--sci-orange-rgb),0.12)", color: SCI.orange, border: SCI.orange }
      : kind === "success"
        ? { bg: "rgba(43,74,58,0.12)", color: SCI.forest, border: SCI.forest }
        : { bg: SCI.tint2, color: SCI.ink2, border: SCI.ink };

  return (
    <span
      style={{
        display: "inline-flex",
        alignItems: "center",
        border: `1px solid ${tone.border}`,
        color: tone.color,
        background: tone.bg,
        padding: "4px 10px",
        fontFamily: SCI.mono,
        fontSize: 10,
        letterSpacing: 0.5,
        textTransform: "uppercase",
      }}
    >
      {children}
    </span>
  );
}

function AuthModal({
  open,
  mode,
  email,
  password,
  loading,
  error,
  onEmail,
  onPassword,
  onMode,
  onClose,
  onSubmit,
}) {
  if (!open) return null;

  return (
    <div
      style={{
        position: "fixed",
        inset: 0,
        background: "rgba(var(--sci-ink-rgb),0.45)",
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        zIndex: 40,
        padding: 20,
      }}
    >
      <div
        style={{
          width: "min(560px, 100%)",
          border: `1px solid ${SCI.border}`,
          background: SCI.paper,
        }}
      >
        <div
          style={{
            height: 56,
            borderBottom: `1px solid ${SCI.border}`,
            display: "flex",
            alignItems: "stretch",
            justifyContent: "space-between",
          }}
        >
          <div style={{ display: "flex" }}>
            {[{ label: "Log In", value: "signin" }, { label: "Create Account", value: "signup" }].map((tab) => (
              <button
                key={tab.value}
                type="button"
                onClick={() => onMode(tab.value)}
                style={{
                  padding: "0 20px",
                  border: "none",
                  borderRight: `1px solid ${SCI.border}`,
                  borderBottom: mode === tab.value ? `2px solid ${SCI.border}` : "2px solid transparent",
                  background: mode === tab.value ? SCI.paper : SCI.tint,
                  fontFamily: SCI.mono,
                  fontSize: 11,
                  letterSpacing: 0.5,
                  textTransform: "uppercase",
                  cursor: "pointer",
                  color: mode === tab.value ? SCI.ink : SCI.ink3,
                  transition: "background 0.15s ease",
                }}
              >
                {tab.label}
              </button>
            ))}
          </div>
          <button
            type="button"
            onClick={onClose}
            style={{
              border: "none",
              borderLeft: `1px solid ${SCI.border}`,
              background: "none",
              padding: "0 18px",
              fontFamily: SCI.mono,
              fontSize: 12,
              cursor: "pointer",
              color: SCI.ink2,
            }}
          >
            ✕
          </button>
        </div>
        <div style={{ padding: "24px 24px 28px" }}>
          <div
            style={{
              fontFamily: SCI.display,
              fontSize: 44,
              lineHeight: 0.95,
              letterSpacing: -0.6,
            }}
          >
            {mode === "signin" ? "Welcome Back." : "Start Your Profile."}
          </div>
          <div
            style={{
              fontSize: 14,
              lineHeight: 1.55,
              color: SCI.ink2,
              marginTop: 12,
            }}
          >
            Use email/password authentication through Supabase. The same account
            persists your onboarding and reports.
          </div>

          <div style={{ marginTop: 18, display: "grid", gap: 12 }}>
            <AuthInput
              label="Email"
              value={email}
              onChange={onEmail}
              type="email"
              placeholder="you@example.com"
            />
            <AuthInput
              label="Password"
              value={password}
              onChange={onPassword}
              type="password"
              placeholder="min 6 characters"
            />
          </div>

          {error ? (
            <div style={{ marginTop: 10 }}>
              <StatusChip kind="error">{error}</StatusChip>
            </div>
          ) : null}

          <div
            style={{
              marginTop: 18,
              display: "flex",
              gap: 10,
              alignItems: "center",
            }}
          >
            <SciBtn primary icon="→" onClick={onSubmit} disabled={loading}>
              {loading
                ? "Working..."
                : mode === "signin"
                  ? "Sign In"
                  : "Create Account"}
            </SciBtn>
            <button
              type="button"
              onClick={() => onMode(mode === "signin" ? "signup" : "signin")}
              style={{
                border: "none",
                background: "none",
                padding: 0,
                fontFamily: SCI.mono,
                fontSize: 10,
                letterSpacing: 0.5,
                textTransform: "uppercase",
                textDecoration: "underline",
                cursor: "pointer",
                color: SCI.ink2,
              }}
            >
              {mode === "signin"
                ? "Need an account? Create one"
                : "Already have an account? Sign in"}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

function AuthInput({ label, value, onChange, type, placeholder }) {
  return (
    <div>
      <MonoLabel size={9} style={{ display: "block", marginBottom: 6 }}>
        {label}
      </MonoLabel>
      <div
        style={{
          height: 48,
          border: `1px solid ${SCI.border}`,
          background: SCI.paper,
          display: "flex",
          alignItems: "center",
          padding: "0 12px",
        }}
      >
        <input
          type={type}
          value={value}
          onChange={(e) => onChange(e.target.value)}
          placeholder={placeholder}
          autoComplete={type === "password" ? "current-password" : "email"}
          style={{
            width: "100%",
            border: "none",
            outline: "none",
            background: "none",
            fontFamily: SCI.body,
            fontSize: 14,
            color: SCI.ink,
          }}
        />
      </div>
    </div>
  );
}

function ObProgress({ step, total = 5, compact }) {
  return (
    <div
      style={{ display: "flex", alignItems: "center", gap: compact ? 10 : 18 }}
    >
      <MonoLabel size={10}>
        Step {String(step).padStart(2, "0")} / {String(total).padStart(2, "0")}
      </MonoLabel>
      <div style={{ display: "flex", gap: 4 }}>
        {Array.from({ length: total }).map((_, i) => (
          <div
            key={i}
            style={{
              width: compact ? 18 : 28,
              height: 4,
              background: i < step ? SCI.ink : SCI.tint,
            }}
          />
        ))}
      </div>
    </div>
  );
}

function LiveField({
  label,
  value,
  onChange,
  hint,
  mono,
  w,
  placeholder,
  type = "text",
  inputMode,
  error,
}) {
  return (
    <div style={{ width: w }}>
      <MonoLabel size={10} style={{ display: "block", marginBottom: 6 }}>
        {label}
      </MonoLabel>
      <div
        style={{
          height: 48,
          border: `1px solid ${error ? SCI.orange : SCI.border}`,
          display: "flex",
          alignItems: "center",
          padding: "0 14px",
          background: SCI.paper,
        }}
      >
        <input
          type={type}
          inputMode={inputMode}
          value={value}
          onChange={(e) => onChange(e.target.value)}
          placeholder={placeholder}
          style={{
            width: "100%",
            border: "none",
            outline: "none",
            background: "none",
            fontFamily: mono ? SCI.mono : SCI.body,
            fontSize: mono ? 14 : 15,
            color: SCI.ink,
          }}
        />
      </div>
      {hint && (
        <MonoLabel
          size={9}
          style={{ display: "block", marginTop: 6, color: SCI.ink3 }}
        >
          {hint}
        </MonoLabel>
      )}
      <InlineError message={error} />
    </div>
  );
}

function ObFrameDesktop({ step, title, kicker, lead, children, side }) {
  return (
    <div
      style={{
        width: 1400,
        background: SCI.paper,
        color: SCI.ink,
        fontFamily: SCI.body,
        border: `1px solid ${SCI.border}`,
      }}
    >
      <div
        style={{
          display: "grid",
          gridTemplateColumns: "1.05fr 1fr",
          minHeight: 640,
        }}
      >
        <div
          style={{
            padding: "56px 56px 40px",
            borderRight: `1px solid ${SCI.border}`,
            display: "flex",
            flexDirection: "column",
            justifyContent: "space-between",
          }}
        >
          <div>
            <MonoLabel>Question {step} of 5</MonoLabel>
            <h1
              style={{
                fontFamily: SCI.display,
                fontSize: 64,
                lineHeight: 0.96,
                letterSpacing: -1,
                fontWeight: 500,
                margin: "16px 0 16px",
              }}
            >
              {title}
            </h1>
            {lead ? (
              <div
                style={{
                  maxWidth: 460,
                  fontSize: 15,
                  lineHeight: 1.55,
                  color: SCI.ink2,
                }}
              >
                {lead}
              </div>
            ) : null}
          </div>
          <div style={{ marginTop: 40 }}>{children}</div>
        </div>
        <div style={{ background: SCI.tint2 }}>{side}</div>
      </div>
    </div>
  );
}

function ObStepMobileFrame({ step, kicker, title, children, footer }) {
  return (
    <div
      style={{
        width: 390,
        background: SCI.paper,
        color: SCI.ink,
        fontFamily: SCI.body,
        border: `1px solid ${SCI.border}`,
      }}
    >
      <div
        style={{
          padding: "12px 18px",
          borderBottom: `1px solid ${SCI.tint}`,
          display: "flex",
          justifyContent: "space-between",
          alignItems: "center",
        }}
      >
        <MonoLabel size={10}>{kicker}</MonoLabel>
        <ObProgress step={step} compact />
      </div>
      <div style={{ padding: "26px 20px 32px" }}>
        <MonoLabel>Question {step} of 5</MonoLabel>
        <h1
          style={{
            fontFamily: SCI.display,
            fontSize: 42,
            lineHeight: 0.95,
            letterSpacing: -0.5,
            fontWeight: 500,
            margin: "10px 0 18px",
          }}
        >
          {title}
        </h1>
        {children}
      </div>
      <div
        style={{
          borderTop: `1px solid ${SCI.border}`,
          padding: "16px 20px 20px",
          display: "flex",
          flexDirection: "column",
          gap: 10,
        }}
      >
        {footer}
      </div>
    </div>
  );
}

function Step1Desktop({ form, setForm, onNext, errors, busy }) {
  return (
    <ObFrameDesktop
      step={1}
      kicker="Your stars"
      title={
        <>
          Let's start with
          <br />
          <span style={{ fontStyle: "italic" }}>your stars.</span>
        </>
      }
      lead="The name you actually use, and the day you landed. Ten of your twelve readings start here."
      side={
        <OnboardingSideRows
          fig="02"
          label="Why we ask"
          rows={[
            ["Your name", "numerology listens to that one"],
            ["Your birthday", "seeds ten of twelve readings"],
            ["Location", "resolved next step"],
            ["Privacy", "delete anytime"],
          ]}
        />
      }
    >
      <div
        style={{
          display: "grid",
          gridTemplateColumns: "1fr 1fr",
          gap: 18,
          maxWidth: 560,
        }}
      >
        <LiveField
          label="Given name"
          value={form.givenName}
          onChange={(v) => setForm((s) => ({ ...s, givenName: v.toUpperCase() }))}
          hint="as on birth certificate"
          error={errors.givenName}
        />
        <LiveField
          label="Family name"
          value={form.familyName}
          onChange={(v) => setForm((s) => ({ ...s, familyName: v.toUpperCase() }))}
          hint="optional"
        />
        <div style={{ gridColumn: "1 / -1" }}>
          <LiveField
            label="Date of birth"
            value={form.birthDate}
            onChange={(v) =>
              setForm((s) => ({ ...s, birthDate: formatUsDateFromDigits(v) }))
            }
            mono
            placeholder="MM / DD / YYYY"
            hint="American format"
            error={errors.birthDate}
            w="100%"
          />
        </div>
      </div>
      <div
        style={{
          marginTop: 28,
          display: "flex",
          alignItems: "center",
          gap: 16,
        }}
      >
        <SciBtn primary size="lg" icon="→" onClick={onNext} disabled={busy}>
          Continue
        </SciBtn>
        <MonoLabel size={10} style={{ color: SCI.ink3 }}>
          ↵ ENTER TO CONTINUE
        </MonoLabel>
      </div>
    </ObFrameDesktop>
  );
}

function Step2Desktop({ form, setForm, onBack, onNext, busy }) {
  return (
    <ObFrameDesktop
      step={2}
      kicker="Your moment"
      title={
        <>
          What time did <span style={{ fontStyle: "italic" }}>you land?</span>
        </>
      }
      lead="Down to the minute if you can. This is how we know where every planet was standing to greet you."
      side={
        <OnboardingSideRows
          fig="03"
          label="Why it matters"
          rows={[
            ["Exact", "your Rising + full Human Design"],
            ["±15 min", "nearly complete"],
            ["±1 hour", "most of it"],
            ["Unknown", "ten of twelve readings still work"],
          ]}
        />
      }
    >
      <div
        style={{ display: "flex", gap: 18, alignItems: "end", maxWidth: 560 }}
      >
        <LiveField
          label="Hour"
          value={form.birthHour}
          onChange={(v) =>
            setForm((s) => ({
              ...s,
              birthHour: v.replace(/[^0-9]/g, "").slice(0, 2),
            }))
          }
          mono
          w={110}
          placeholder="3"
        />
        <div
          style={{
            fontFamily: SCI.display,
            fontSize: 40,
            paddingBottom: 10,
            color: SCI.ink3,
          }}
        >
          :
        </div>
        <LiveField
          label="Minute"
          value={form.birthMinute}
          onChange={(v) =>
            setForm((s) => ({
              ...s,
              birthMinute: v.replace(/[^0-9]/g, "").slice(0, 2),
            }))
          }
          mono
          w={110}
          placeholder="32"
        />
        <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
          <MonoLabel size={9}>Period</MonoLabel>
          <div style={{ display: "flex", gap: 4 }}>
            {["AM", "PM"].map((p) => (
              <button key={p} type="button"
                onClick={() => setForm((s) => ({ ...s, birthPeriod: p }))}
                style={{
                  width: 56, height: 44,
                  border: `1px solid ${SCI.border}`,
                  background: form.birthPeriod === p ? SCI.ink : SCI.paper,
                  color: form.birthPeriod === p ? SCI.paper : SCI.ink,
                  fontFamily: SCI.mono, fontSize: 13, letterSpacing: 1,
                  cursor: "pointer",
                }}>{p}</button>
            ))}
          </div>
        </div>
      </div>
      <div style={{ marginTop: 18, display: "flex", gap: 10 }}>
        {ACCURACY_OPTIONS.map((option) => (
          <button
            key={option}
            type="button"
            onClick={() =>
              setForm((s) => ({ ...s, birthTimeAccuracy: option }))
            }
            style={{
              border: "none",
              background: "none",
              padding: 0,
              cursor: "pointer",
            }}
          >
            <Pill dark={form.birthTimeAccuracy === option}>{option}</Pill>
          </button>
        ))}
      </div>
      <div
        style={{
          marginTop: 32,
          display: "flex",
          gap: 14,
          alignItems: "center",
        }}
      >
        <SciBtn size="lg" onClick={onBack} disabled={busy}>
          ← Back
        </SciBtn>
        <SciBtn primary size="lg" icon="→" onClick={onNext} disabled={busy}>
          Continue
        </SciBtn>
        <button
          type="button"
          onClick={() =>
            setForm((s) => ({
              ...s,
              birthHour: "",
              birthMinute: "",
              birthTimeAccuracy: "Unknown",
            }))
          }
          style={{
            marginLeft: 12,
            border: "none",
            background: "none",
            fontFamily: SCI.mono,
            fontSize: 11,
            color: SCI.ink3,
            textDecoration: "underline",
            cursor: "pointer",
          }}
        >
          I don't know my birth time →
        </button>
      </div>
    </ObFrameDesktop>
  );
}

function Step3Desktop({
  form,
  setForm,
  onBack,
  onNext,
  errors,
  locationOptions,
  onPickLocation,
  busy,
}) {
  return (
    <ObFrameDesktop
      step={3}
      kicker="Your place"
      title={
        <>
          Where did <span style={{ fontStyle: "italic" }}>you land?</span>
        </>
      }
      lead="The city that caught you first. We use it to map the sky at the exact moment you arrived."
      side={
        <OnboardingSideRows
          fig="04"
          label="Location preview"
          rows={[
            ["Latitude", form.latitude || "—"],
            ["Longitude", form.longitude || "—"],
            ["Timezone", form.timezone || "—"],
            ["Source", "Open-Meteo geocoder"],
          ]}
        />
      }
    >
      <div style={{ maxWidth: 560 }}>
        <LiveField
          label="Birth location"
          value={form.birthLocation}
          onChange={(v) => setForm((s) => ({ ...s, birthLocation: v }))}
          hint="cities only"
          error={errors.birthLocation}
          placeholder="Brooklyn, New York"
        />
        {locationOptions.length > 0 ? (
          <div style={{ border: `1px solid ${SCI.border}`, borderTop: "none" }}>
            {locationOptions.map((opt, i) => (
              <button
                key={`${opt.name}-${i}`}
                type="button"
                onClick={() => onPickLocation(opt)}
                style={{
                  width: "100%",
                  textAlign: "left",
                  border: "none",
                  borderBottom:
                    i < locationOptions.length - 1
                      ? `1px solid ${SCI.tint}`
                      : "none",
                  background: i === 0 ? SCI.tint2 : SCI.paper,
                  padding: "12px 16px",
                  cursor: "pointer",
                  fontFamily: SCI.body,
                  fontSize: 14,
                  color: SCI.ink,
                  display: "flex",
                  justifyContent: "space-between",
                  gap: 10,
                }}
              >
                <span>
                  {opt.name}, {opt.country}
                </span>
                <span
                  style={{
                    fontFamily: SCI.mono,
                    fontSize: 10,
                    color: SCI.ink3,
                  }}
                >
                  {opt.timezone || "tz"}
                </span>
              </button>
            ))}
          </div>
        ) : null}
      </div>
      <div style={{ marginTop: 28, display: "flex", gap: 14 }}>
        <SciBtn size="lg" onClick={onBack} disabled={busy}>
          ← Back
        </SciBtn>
        <SciBtn primary size="lg" icon="→" onClick={onNext} disabled={busy}>
          Continue
        </SciBtn>
      </div>
    </ObFrameDesktop>
  );
}

function EnneagramPicker({ value, onChange }) {
  return (
    <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 8, maxWidth: 480 }}>
      {ENNEAGRAM_TYPES.map(({ n, label }) => {
        const sel = String(value) === String(n);
        return (
          <button key={n} type="button" onClick={() => onChange(String(n))}
            style={{
              border: `1px solid ${SCI.border}`,
              background: sel ? SCI.ink : SCI.paper,
              color: sel ? SCI.paper : SCI.ink,
              padding: "12px 8px",
              cursor: "pointer",
              textAlign: "left",
            }}>
            <MonoLabel size={10} style={{ color: sel ? "rgba(var(--sci-paper-rgb),0.5)" : SCI.ink3 }}>TYPE {n}</MonoLabel>
            <div style={{ fontSize: 12, marginTop: 3 }}>{label}</div>
          </button>
        );
      })}
    </div>
  );
}

function MbtiQuiz({ answers, setAnswers }) {
  const handleAnswer = (i, val) => {
    const next = [...(answers || [])];
    next[i] = val;
    setAnswers(next);
  };
  const answered = (answers || []).filter(v => v !== undefined).length;
  return (
    <div>
      <MonoLabel size={10} style={{ color: SCI.ink3, marginBottom: 12 }}>
        {answered} / {MBTI_QUESTIONS.length} answered
      </MonoLabel>
      <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        {MBTI_QUESTIONS.map((q, i) => {
          const val = answers?.[i];
          return (
            <div key={i} style={{ border: `1px solid ${SCI.border}`, padding: "14px 16px" }}>
              <div style={{ fontSize: 14, lineHeight: 1.4, marginBottom: 10 }}>{q.q}</div>
              <div style={{ display: "flex", gap: 8 }}>
                {["Agree", "Not sure", "Disagree"].map((lbl, li) => {
                  const boolVal = li === 0 ? true : li === 2 ? false : null;
                  const selMatch = li === 0 ? val === true : li === 2 ? val === false : val === null && val !== undefined;
                  const sel = answers && i in answers && (
                    (li === 0 && answers[i] === true) ||
                    (li === 2 && answers[i] === false) ||
                    (li === 1 && answers[i] === null)
                  );
                  return (
                    <button key={lbl} type="button"
                      onClick={() => handleAnswer(i, boolVal)}
                      style={{
                        flex: 1,
                        padding: "8px 4px",
                        border: `1px solid ${SCI.border}`,
                        background: sel ? SCI.ink : SCI.paper,
                        color: sel ? SCI.paper : SCI.ink,
                        fontSize: 12,
                        cursor: "pointer",
                      }}>
                      {lbl}
                    </button>
                  );
                })}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function Step4Desktop({ form, setForm, onBack, onNext, busy }) {
  const knowsType = form.knowsType;

  const canContinue = knowsType === true
    ? true
    : knowsType === false
      ? (form.mbtiAnswers || []).filter(v => v !== undefined).length >= 12
      : false;

  const computedMbti = knowsType === false ? scoreMbti(form.mbtiAnswers) : form.mbtiType;

  return (
    <ObFrameDesktop
      step={4}
      kicker="Type intake"
      title={
        <>
          Know your<br />
          <span style={{ fontStyle: "italic" }}>type?</span>
        </>
      }
      lead={
        knowsType == null
          ? "Tell us whether you already know your personality type. We'll use it to sharpen your blueprint."
          : knowsType === true
            ? "Fill in what you know. Skip what you don't — both are optional."
            : "Answer 12 questions and we'll calculate your type."
      }
      side={
        <OnboardingSideRows
          fig="05"
          label="What we use this for"
          rows={[
            ["MBTI", computedMbti || "—"],
            ["Enneagram", form.enneagramType ? `Type ${form.enneagramType}` : "—"],
            ["Convergence", "cross-validated"],
            ["Privacy", "on-device only"],
          ]}
        />
      }
    >
      {knowsType == null && (
        <div style={{ display: "flex", flexDirection: "column", gap: 12, maxWidth: 480 }}>
          <button type="button"
            onClick={() => setForm(s => ({ ...s, knowsType: true }))}
            style={{ border: `1px solid ${SCI.border}`, background: SCI.paper, padding: "20px 24px", textAlign: "left", cursor: "pointer" }}>
            <div style={{ fontFamily: SCI.display, fontSize: 20, marginBottom: 4 }}>I know my type</div>
            <div style={{ fontSize: 13, color: SCI.ink3 }}>I've taken MBTI before — let me select my result directly.</div>
          </button>
          <button type="button"
            onClick={() => setForm(s => ({ ...s, knowsType: false }))}
            style={{ border: `1px solid ${SCI.border}`, background: SCI.paper, padding: "20px 24px", textAlign: "left", cursor: "pointer" }}>
            <div style={{ fontFamily: SCI.display, fontSize: 20, marginBottom: 4 }}>Help me find it</div>
            <div style={{ fontSize: 13, color: SCI.ink3 }}>Answer 12 quick questions and we'll calculate your type.</div>
          </button>
        </div>
      )}

      {knowsType === true && (
        <div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
          <div>
            <MonoLabel style={{ marginBottom: 10 }}>MBTI Type</MonoLabel>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 8, maxWidth: 480 }}>
              {MBTI_TYPES.map(t => {
                const sel = form.mbtiType === t;
                return (
                  <button key={t} type="button"
                    onClick={() => setForm(s => ({ ...s, mbtiType: t }))}
                    style={{
                      padding: "12px 8px",
                      border: `1px solid ${SCI.border}`,
                      background: sel ? SCI.ink : SCI.paper,
                      color: sel ? SCI.paper : SCI.ink,
                      fontFamily: SCI.mono,
                      fontSize: 13,
                      letterSpacing: 1,
                      cursor: "pointer",
                    }}>{t}</button>
                );
              })}
            </div>
          </div>
          <div>
            <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 10 }}>
              <MonoLabel>Enneagram Type</MonoLabel>
              <span style={{ fontFamily: SCI.mono, fontSize: 10, opacity: 0.5 }}>optional</span>
            </div>
            <EnneagramPicker value={form.enneagramType} onChange={v => setForm(s => ({ ...s, enneagramType: v }))} />
          </div>
        </div>
      )}

      {knowsType === false && (
        <div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
          <MbtiQuiz
            answers={form.mbtiAnswers}
            setAnswers={ans => setForm(s => ({ ...s, mbtiAnswers: ans }))}
          />
          {computedMbti && (
            <div>
              <MonoLabel style={{ marginBottom: 6 }}>Calculated type: <strong>{computedMbti}</strong></MonoLabel>
            </div>
          )}
          <div>
            <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 10 }}>
              <MonoLabel>Enneagram Type</MonoLabel>
              <span style={{ fontFamily: SCI.mono, fontSize: 10, opacity: 0.5 }}>optional</span>
            </div>
            <EnneagramPicker value={form.enneagramType} onChange={v => setForm(s => ({ ...s, enneagramType: v }))} />
          </div>
        </div>
      )}

      <div style={{ marginTop: 28, display: "flex", gap: 14, alignItems: "center" }}>
        <SciBtn size="lg" onClick={knowsType != null ? () => setForm(s => ({ ...s, knowsType: null })) : onBack} disabled={busy}>
          ← {knowsType != null ? "Back" : "Previous"}
        </SciBtn>
        <SciBtn primary size="lg" icon="→" onClick={onNext} disabled={busy || !canContinue}>
          Continue
        </SciBtn>
        {!canContinue && knowsType != null && (
          <MonoLabel size={10} style={{ color: SCI.ink3, marginLeft: 8 }}>
            {knowsType === false ? "Answer all 12 questions to continue" : ""}
          </MonoLabel>
        )}
      </div>
    </ObFrameDesktop>
  );
}

function Step5Desktop({ form, setForm, onBack, onNext, busy }) {
  return (
    <ObFrameDesktop
      step={5}
      kicker="Your chapter"
      title={
        <>
          Choose your
          <br />
          <span style={{ fontStyle: "italic" }}>chapter.</span>
        </>
      }
      lead="You can upgrade anytime. Your chart stays yours either way."
      side={
        <OnboardingSideRows
          fig="06"
          label="What you unlock"
          rows={
            _IS_AMARA_V ? [
              ["8 traditions · ~38 readings", "Included"],
              ["Long-form report", "Included"],
              ["Narrated audio (my voice)", form.tier === "daily_sign" ? "Upgrade" : "Included"],
              ["Chat with your atlas", form.tier === "whole_world" || form.tier === "atlas" ? "Included" : "Upgrade"],
            ] : [
              ["12 readings", "Included"],
              ["Long-form report", "Included"],
              ["Narrated audio", form.tier === "Glimpse" ? "No" : "Included"],
              ["Chat with your chart", form.tier === "Becoming" ? "Included" : "Upgrade"],
            ]
          }
        />
      }
    >
      <div
        style={{
          display: "grid",
          gridTemplateColumns: "1fr 1fr 1fr",
          gap: 10,
          maxWidth: 640,
        }}
      >
        {TIER_OPTIONS.map((tier, idx) => {
          const selected = form.tier === tier.name;
          return (
            <button
              key={tier.name}
              type="button"
              onClick={() => setForm((s) => ({ ...s, tier: tier.name }))}
              style={{
                padding: "18px 16px 20px",
                border: `1.5px solid ${selected ? SCI.border : SCI.tint}`,
                background: selected ? SCI.ink : SCI.paper,
                color: selected ? SCI.paper : SCI.ink,
                textAlign: "left",
                cursor: "pointer",
              }}
            >
              <div
                style={{
                  display: "flex",
                  justifyContent: "space-between",
                  alignItems: "center",
                  marginBottom: 10,
                }}
              >
                <MonoLabel
                  size={9}
                  style={{
                    color: selected ? "rgba(var(--sci-paper-rgb),0.6)" : SCI.ink3,
                  }}
                >
                  Tier 0{idx + 1}
                </MonoLabel>
                <div
                  style={{
                    width: 14,
                    height: 14,
                    borderRadius: 8,
                    border: `1.5px solid ${selected ? SCI.paper : SCI.ink3}`,
                    background: selected ? SCI.orange : "transparent",
                  }}
                />
              </div>
              <div
                style={{
                  fontFamily: SCI.display,
                  fontSize: 22,
                  lineHeight: 1.1,
                }}
              >
                {tier.label || tier.name}
              </div>
              <div
                style={{
                  fontFamily: SCI.display,
                  fontSize: 38,
                  lineHeight: 1,
                  margin: "10px 0 2px",
                  letterSpacing: -0.5,
                }}
              >
                {tier.price}
              </div>
              <MonoLabel
                size={10}
                style={{ color: selected ? "rgba(var(--sci-paper-rgb),0.6)" : SCI.ink3 }}
              >
                / {tier.per}
              </MonoLabel>
            </button>
          );
        })}
      </div>

      <div
        style={{
          marginTop: 28,
          display: "flex",
          gap: 14,
          alignItems: "center",
        }}
      >
        <SciBtn size="lg" onClick={onBack} disabled={busy}>
          ← Back
        </SciBtn>
        <SciBtn primary size="lg" icon="→" onClick={onNext} disabled={busy}>
          Continue to checkout
        </SciBtn>
        <MonoLabel size={10} style={{ color: SCI.ink3, marginLeft: 8 }}>
          {_IS_AMARA_V
            ? form.tier === "whole_world" ? "$129.00" : form.tier === "atlas" ? "$79.00" : form.tier === "love_union" ? "$39.00" : "$19.00 / mo"
            : form.tier === "Becoming" ? "$49.00" : "$0.00"}
        </MonoLabel>
      </div>
    </ObFrameDesktop>
  );
}

function OnboardingSideRows({ fig, label, rows }) {
  return (
    <div
      style={{
        padding: "56px 40px",
        display: "flex",
        flexDirection: "column",
        gap: 18,
      }}
    >
      <MonoLabel>
        FIG. {fig} · {label}
      </MonoLabel>
      <div
        style={{
          fontFamily: SCI.display,
          fontSize: 26,
          lineHeight: 1.15,
          letterSpacing: -0.2,
        }}
      >
        Every field has a purpose. Here are yours.
      </div>
      <div style={{ background: SCI.paper, border: `1px solid ${SCI.border}` }}>
        {rows.map(([key, value], idx) => (
          <div
            key={key}
            style={{
              display: "grid",
              gridTemplateColumns: "140px 1fr",
              gap: 12,
              padding: "12px 16px",
              borderBottom:
                idx < rows.length - 1 ? `1px solid ${SCI.tint}` : "none",
            }}
          >
            <MonoLabel size={10}>{key}</MonoLabel>
            <div style={{ fontSize: 13, lineHeight: 1.4 }}>{value}</div>
          </div>
        ))}
      </div>
      <div style={{ fontSize: 13, lineHeight: 1.5, color: SCI.ink2 }}>
        We preserve your latest state after every step in Supabase so the flow
        can resume without data loss.
      </div>
    </div>
  );
}

function MobileStep1({ form, setForm, onNext, errors, busy }) {
  return (
    <ObStepMobileFrame
      step={1}
      kicker="§ Your stars"
      title={
        <>
          Let's start with
          <br />
          <span style={{ fontStyle: "italic" }}>your stars.</span>
        </>
      }
      footer={
        <SciBtn
          primary
          size="lg"
          w="100%"
          icon="→"
          onClick={onNext}
          disabled={busy}
        >
          Continue
        </SciBtn>
      }
    >
      <LiveField
        label="Given name"
        value={form.givenName}
        onChange={(v) => setForm((s) => ({ ...s, givenName: v.toUpperCase() }))}
        error={errors.givenName}
      />
      <div style={{ height: 14 }} />
      <LiveField
        label="Family name"
        value={form.familyName}
        onChange={(v) => setForm((s) => ({ ...s, familyName: v.toUpperCase() }))}
      />
      <div style={{ height: 14 }} />
      <LiveField
        label="Date of birth"
        value={form.birthDate}
        onChange={(v) =>
          setForm((s) => ({ ...s, birthDate: formatUsDateFromDigits(v) }))
        }
        mono
        inputMode="numeric"
        placeholder="MM / DD / YYYY"
        error={errors.birthDate}
      />
    </ObStepMobileFrame>
  );
}

function MobileStep2({ form, setForm, onBack, onNext, busy }) {
  return (
    <ObStepMobileFrame
      step={2}
      kicker="§ Your moment"
      title={
        <>
          What time did you <span style={{ fontStyle: "italic" }}>land?</span>
        </>
      }
      footer={
        <>
          <SciBtn size="lg" w="100%" onClick={onBack} disabled={busy}>
            ← Back
          </SciBtn>
          <SciBtn
            primary
            size="lg"
            w="100%"
            icon="→"
            onClick={onNext}
            disabled={busy}
          >
            Continue
          </SciBtn>
        </>
      }
    >
      <div style={{ display: "flex", gap: 10, alignItems: "end" }}>
        <LiveField
          label="Hour"
          value={form.birthHour}
          onChange={(v) =>
            setForm((s) => ({
              ...s,
              birthHour: v.replace(/[^0-9]/g, "").slice(0, 2),
            }))
          }
          mono
          w={90}
          placeholder="3"
        />
        <div
          style={{
            fontFamily: SCI.display,
            fontSize: 36,
            paddingBottom: 6,
            color: SCI.ink3,
          }}
        >
          :
        </div>
        <LiveField
          label="Min"
          value={form.birthMinute}
          onChange={(v) =>
            setForm((s) => ({
              ...s,
              birthMinute: v.replace(/[^0-9]/g, "").slice(0, 2),
            }))
          }
          mono
          w={90}
          placeholder="45"
        />
        <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
          <MonoLabel size={9}>Period</MonoLabel>
          <div style={{ display: "flex", gap: 4 }}>
            {["AM", "PM"].map((p) => (
              <button key={p} type="button"
                onClick={() => setForm((s) => ({ ...s, birthPeriod: p }))}
                style={{
                  width: 48, height: 44,
                  border: `1px solid ${SCI.border}`,
                  background: form.birthPeriod === p ? SCI.ink : SCI.paper,
                  color: form.birthPeriod === p ? SCI.paper : SCI.ink,
                  fontFamily: SCI.mono, fontSize: 12, letterSpacing: 1,
                  cursor: "pointer",
                }}>{p}</button>
            ))}
          </div>
        </div>
      </div>
      <div style={{ marginTop: 14, display: "flex", gap: 6, flexWrap: "wrap" }}>
        {ACCURACY_OPTIONS.map((opt) => (
          <button
            key={opt}
            type="button"
            onClick={() => setForm((s) => ({ ...s, birthTimeAccuracy: opt }))}
            style={{
              border: "none",
              background: "none",
              padding: 0,
              cursor: "pointer",
            }}
          >
            <Pill dark={form.birthTimeAccuracy === opt}>{opt}</Pill>
          </button>
        ))}
      </div>
    </ObStepMobileFrame>
  );
}

function MobileStep3({
  form,
  setForm,
  onBack,
  onNext,
  errors,
  locationOptions,
  onPickLocation,
  busy,
}) {
  return (
    <ObStepMobileFrame
      step={3}
      kicker="§ Your place"
      title={
        <>
          Where did <span style={{ fontStyle: "italic" }}>you land?</span>
        </>
      }
      footer={
        <>
          <SciBtn size="lg" w="100%" onClick={onBack} disabled={busy}>
            ← Back
          </SciBtn>
          <SciBtn
            primary
            size="lg"
            w="100%"
            icon="→"
            onClick={onNext}
            disabled={busy}
          >
            Continue
          </SciBtn>
        </>
      }
    >
      <LiveField
        label="Birth location"
        value={form.birthLocation}
        onChange={(v) => setForm((s) => ({ ...s, birthLocation: v }))}
        error={errors.birthLocation}
        hint="cities only"
      />
      {locationOptions.length > 0 ? (
        <div style={{ border: `1px solid ${SCI.border}`, borderTop: "none" }}>
          {locationOptions.map((opt, i) => (
            <button
              key={`${opt.name}-${i}`}
              type="button"
              onClick={() => onPickLocation(opt)}
              style={{
                width: "100%",
                textAlign: "left",
                border: "none",
                borderBottom:
                  i < locationOptions.length - 1
                    ? `1px solid ${SCI.tint}`
                    : "none",
                background: i === 0 ? SCI.tint2 : SCI.paper,
                padding: "12px 14px",
                fontSize: 13,
                cursor: "pointer",
              }}
            >
              {opt.name}, {opt.country}
            </button>
          ))}
        </div>
      ) : null}
    </ObStepMobileFrame>
  );
}

function MobileStep4({ form, setForm, onBack, onNext, busy }) {
  const knowsType = form.knowsType;
  const computedMbti = knowsType === false ? scoreMbti(form.mbtiAnswers) : form.mbtiType;
  const canContinue = knowsType === true
    ? true
    : knowsType === false
      ? (form.mbtiAnswers || []).filter(v => v !== undefined).length >= 12
      : false;

  const handleBack = knowsType != null
    ? () => setForm(s => ({ ...s, knowsType: null }))
    : onBack;

  return (
    <ObStepMobileFrame
      step={4}
      kicker="§ Type intake"
      title={
        <>
          Know your <span style={{ fontStyle: "italic" }}>type?</span>
        </>
      }
      footer={
        <>
          <SciBtn size="lg" w="100%" onClick={handleBack} disabled={busy}>
            ← {knowsType != null ? "Back" : "Previous"}
          </SciBtn>
          <SciBtn primary size="lg" w="100%" icon="→" onClick={onNext} disabled={busy || !canContinue}>
            Continue
          </SciBtn>
        </>
      }
    >
      {knowsType == null && (
        <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
          <button type="button"
            onClick={() => setForm(s => ({ ...s, knowsType: true }))}
            style={{ border: `1px solid ${SCI.border}`, background: SCI.paper, padding: "18px 16px", textAlign: "left", cursor: "pointer" }}>
            <div style={{ fontFamily: SCI.display, fontSize: 18, marginBottom: 3 }}>I know my type</div>
            <div style={{ fontSize: 12, color: SCI.ink3 }}>Select MBTI + Enneagram directly.</div>
          </button>
          <button type="button"
            onClick={() => setForm(s => ({ ...s, knowsType: false }))}
            style={{ border: `1px solid ${SCI.border}`, background: SCI.paper, padding: "18px 16px", textAlign: "left", cursor: "pointer" }}>
            <div style={{ fontFamily: SCI.display, fontSize: 18, marginBottom: 3 }}>Help me find it</div>
            <div style={{ fontSize: 12, color: SCI.ink3 }}>Answer 12 quick questions.</div>
          </button>
        </div>
      )}

      {knowsType === true && (
        <div style={{ display: "flex", flexDirection: "column", gap: 20 }}>
          <div>
            <MonoLabel style={{ marginBottom: 8 }}>MBTI Type</MonoLabel>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 6 }}>
              {MBTI_TYPES.map(t => {
                const sel = form.mbtiType === t;
                return (
                  <button key={t} type="button"
                    onClick={() => setForm(s => ({ ...s, mbtiType: t }))}
                    style={{
                      padding: "10px 4px",
                      border: `1px solid ${SCI.border}`,
                      background: sel ? SCI.ink : SCI.paper,
                      color: sel ? SCI.paper : SCI.ink,
                      fontFamily: SCI.mono,
                      fontSize: 11,
                      letterSpacing: 1,
                      cursor: "pointer",
                    }}>{t}</button>
                );
              })}
            </div>
          </div>
          <div>
            <MonoLabel style={{ marginBottom: 8 }}>Enneagram Type</MonoLabel>
            <EnneagramPicker value={form.enneagramType} onChange={v => setForm(s => ({ ...s, enneagramType: v }))} />
          </div>
        </div>
      )}

      {knowsType === false && (
        <div style={{ display: "flex", flexDirection: "column", gap: 20 }}>
          <MbtiQuiz
            answers={form.mbtiAnswers}
            setAnswers={ans => setForm(s => ({ ...s, mbtiAnswers: ans }))}
          />
          {computedMbti && (
            <MonoLabel>Calculated: <strong>{computedMbti}</strong></MonoLabel>
          )}
          <div>
            <MonoLabel style={{ marginBottom: 8 }}>Enneagram Type</MonoLabel>
            <EnneagramPicker value={form.enneagramType} onChange={v => setForm(s => ({ ...s, enneagramType: v }))} />
          </div>
        </div>
      )}
    </ObStepMobileFrame>
  );
}

function MobileStep5({ form, setForm, onBack, onNext, busy }) {
  return (
    <ObStepMobileFrame
      step={5}
      kicker="§ Your chapter"
      title={
        <>
          Choose <span style={{ fontStyle: "italic" }}>depth</span>.
        </>
      }
      footer={
        <>
          <SciBtn size="lg" w="100%" onClick={onBack} disabled={busy}>
            ← Back
          </SciBtn>
          <SciBtn
            primary
            size="lg"
            w="100%"
            icon="→"
            onClick={onNext}
            disabled={busy}
          >
            Continue to checkout
          </SciBtn>
        </>
      }
    >
      <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        {TIER_OPTIONS.map((tier) => {
          const selected = form.tier === tier.name;
          return (
            <button
              key={tier.name}
              type="button"
              onClick={() => setForm((s) => ({ ...s, tier: tier.name }))}
              style={{
                padding: "14px 16px",
                border: `1.5px solid ${selected ? SCI.border : SCI.tint}`,
                background: selected ? SCI.ink : SCI.paper,
                color: selected ? SCI.paper : SCI.ink,
                display: "flex",
                justifyContent: "space-between",
                alignItems: "center",
                cursor: "pointer",
                textAlign: "left",
              }}
            >
              <div>
                <div
                  style={{
                    fontFamily: SCI.display,
                    fontSize: 20,
                    lineHeight: 1,
                  }}
                >
                  {tier.label || tier.name}
                </div>
                <MonoLabel
                  size={9}
                  style={{
                    color: selected ? "rgba(var(--sci-paper-rgb),0.6)" : SCI.ink3,
                    marginTop: 4,
                    display: "block",
                  }}
                >
                  / {tier.per}
                </MonoLabel>
              </div>
              <div style={{ fontFamily: SCI.display, fontSize: 26 }}>
                {tier.price}
              </div>
            </button>
          );
        })}
      </div>
    </ObStepMobileFrame>
  );
}

function buildInstrumentRows(progress) {
  const safe = Math.max(1, Math.min(100, Math.round(progress)));
  const doneCount = Math.floor(safe / (100 / INSTRUMENT_LABELS.length));
  const activeIdx = doneCount < INSTRUMENT_LABELS.length ? doneCount : -1;

  return INSTRUMENT_LABELS.map((name, index) => ({
    name,
    category:
      index <= 3 ? "Psychometric" : index === 4 ? "Synthetic" : "Archetypal",
    status:
      index < doneCount ? "done" : index === activeIdx ? "active" : "pending",
  }));
}

function ReportGenDesktop({ progress }) {
  const rows = buildInstrumentRows(progress);

  return (
    <div
      style={{
        width: 1400,
        background: SCI.paper,
        color: SCI.ink,
        fontFamily: SCI.body,
        border: `1px solid ${SCI.border}`,
      }}
    >
      <div
        style={{
          display: "grid",
          gridTemplateColumns: "1.1fr 1fr",
          minHeight: 720,
        }}
      >
        <div
          style={{ padding: "48px 56px", borderRight: `1px solid ${SCI.border}` }}
        >
          <MonoLabel>§ 01 · Instruments</MonoLabel>
          <h1
            style={{
              fontFamily: SCI.display,
              fontSize: 56,
              lineHeight: 0.95,
              letterSpacing: -0.8,
              fontWeight: 500,
              margin: "12px 0 12px",
            }}
          >
            Computing your
            <br />
            <span style={{ fontStyle: "italic" }}>blueprint</span>.
          </h1>
          <div
            style={{
              fontSize: 15,
              lineHeight: 1.55,
              color: SCI.ink2,
              marginBottom: 24,
              maxWidth: 520,
            }}
          >
            Each instrument runs independently, then we compute cross-instrument
            agreement and generate a single markdown report.
          </div>

          <div style={{ border: `1px solid ${SCI.border}`, marginTop: 6 }}>
            <div
              style={{
                display: "grid",
                gridTemplateColumns: "40px 1.4fr 1fr 80px 70px",
                padding: "10px 16px",
                borderBottom: `1px solid ${SCI.border}`,
                background: SCI.tint2,
              }}
            >
              {["№", "Instrument", "Category", "Time", "Status"].map((h) => (
                <MonoLabel key={h} size={9}>
                  {h}
                </MonoLabel>
              ))}
            </div>
            {rows.map((row, i) => (
              <div
                key={row.name}
                style={{
                  display: "grid",
                  gridTemplateColumns: "40px 1.4fr 1fr 80px 70px",
                  padding: "12px 16px",
                  borderBottom:
                    i < rows.length - 1 ? `1px solid ${SCI.tint}` : "none",
                  alignItems: "center",
                  background:
                    row.status === "active"
                      ? "rgba(var(--sci-orange-rgb),0.06)"
                      : SCI.paper,
                }}
              >
                <span
                  style={{
                    fontFamily: SCI.mono,
                    fontSize: 11,
                    color: SCI.ink3,
                  }}
                >
                  {String(i + 1).padStart(2, "0")}
                </span>
                <span style={{ fontSize: 14 }}>{row.name}</span>
                <MonoLabel size={10} style={{ color: SCI.ink3 }}>
                  {row.category}
                </MonoLabel>
                <span
                  style={{
                    fontFamily: SCI.mono,
                    fontSize: 11,
                    color: SCI.ink2,
                  }}
                >
                  {row.status === "pending" ? "—" : "live"}
                </span>
                <span
                  style={{
                    fontFamily: SCI.mono,
                    fontSize: 10,
                    letterSpacing: 0.5,
                    textTransform: "uppercase",
                    color:
                      row.status === "done"
                        ? SCI.ink
                        : row.status === "active"
                          ? SCI.orange
                          : SCI.ink3,
                  }}
                >
                  {row.status === "done"
                    ? "✓ done"
                    : row.status === "active"
                      ? "● running"
                      : "· pending"}
                </span>
              </div>
            ))}
          </div>
        </div>

        <div
          style={{
            padding: "48px 56px",
            background: SCI.tint2,
            display: "flex",
            flexDirection: "column",
            gap: 24,
          }}
        >
          <div>
            <MonoLabel>§ 02 · Progress</MonoLabel>
            <div
              style={{
                fontFamily: SCI.display,
                fontSize: 88,
                lineHeight: 0.95,
                letterSpacing: -1.5,
                fontWeight: 500,
                marginTop: 14,
              }}
            >
              {Math.round(progress)}
              <span
                style={{
                  color: SCI.orange,
                  fontSize: 40,
                  verticalAlign: "super",
                }}
              >
                %
              </span>
            </div>
            <div
              style={{
                marginTop: 14,
                background: SCI.paper,
                border: `1px solid ${SCI.border}`,
                padding: 4,
                height: 14,
              }}
            >
              <div
                style={{
                  width: `${Math.max(1, progress)}%`,
                  height: "100%",
                  background: SCI.ink,
                  position: "relative",
                }}
              >
                <div
                  style={{
                    position: "absolute",
                    right: 0,
                    top: -4,
                    bottom: -4,
                    width: 2,
                    background: SCI.orange,
                  }}
                />
              </div>
            </div>
            <div
              style={{
                display: "flex",
                justifyContent: "space-between",
                marginTop: 8,
                fontFamily: SCI.mono,
                fontSize: 10,
                color: SCI.ink3,
              }}
            >
              <span>00:00</span>
              <span>ELAPSED LIVE</span>
              <span>EDGE FUNCTION</span>
            </div>
          </div>

          <div
            style={{
              background: SCI.paper,
              border: `1px solid ${SCI.border}`,
              padding: 22,
            }}
          >
            <div
              style={{
                display: "flex",
                justifyContent: "space-between",
                alignItems: "center",
                marginBottom: 10,
              }}
            >
              <MonoLabel>Now computing</MonoLabel>
              <Pill dark>
                {rows.find((r) => r.status === "active")?.name || "Finalizing"}
              </Pill>
            </div>
            <div style={{ fontSize: 14, lineHeight: 1.55, color: SCI.ink2 }}>
              The report is generated by the Supabase edge function and
              persisted to `reports` on completion.
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

function ReportGenMobile({ progress }) {
  return (
    <div
      style={{
        width: 390,
        background: SCI.paper,
        color: SCI.ink,
        fontFamily: SCI.body,
        border: `1px solid ${SCI.border}`,
      }}
    >
      <div style={{ padding: "32px 20px 24px" }}>
        <MonoLabel>§ Compute</MonoLabel>
        <h1
          style={{
            fontFamily: SCI.display,
            fontSize: 40,
            lineHeight: 0.95,
            letterSpacing: -0.5,
            fontWeight: 500,
            margin: "10px 0 18px",
          }}
        >
          Computing your <span style={{ fontStyle: "italic" }}>blueprint</span>.
        </h1>
        <div
          style={{
            fontFamily: SCI.display,
            fontSize: 72,
            lineHeight: 0.95,
            letterSpacing: -1.2,
            fontWeight: 500,
          }}
        >
          {Math.round(progress)}
          <span
            style={{ color: SCI.orange, fontSize: 32, verticalAlign: "super" }}
          >
            %
          </span>
        </div>
        <div
          style={{
            height: 12,
            background: SCI.paper,
            border: `1px solid ${SCI.border}`,
            padding: 3,
            marginTop: 12,
          }}
        >
          <div
            style={{
              width: `${Math.max(1, progress)}%`,
              height: "100%",
              background: SCI.ink,
            }}
          />
        </div>
      </div>
    </div>
  );
}

function BecomingDesktop({
  report,
  parsed,
  form,
  onRegenerate,
  onSignOut,
  onNavigate,
}) {
  const createdLabel = report?.created_at
    ? new Date(report.created_at).toLocaleString()
    : "now";

  return (
    <div
      style={{
        width: 1400,
        background: SCI.paper,
        color: SCI.ink,
        fontFamily: SCI.body,
        border: `1px solid ${SCI.border}`,
      }}
    >
      <div
        style={{
          display: "grid",
          gridTemplateColumns: "240px 1fr",
          minHeight: 900,
        }}
      >
        <div
          style={{
            borderRight: `1px solid ${SCI.border}`,
            background: SCI.tint2,
            padding: "22px 0",
          }}
        >
          <div style={{ padding: "0 18px 12px" }}>
            <MonoLabel size={9}>
              Contents · {parsed.sections.length || 1}
            </MonoLabel>
          </div>
          {(parsed.sections.length
            ? parsed.sections
            : [{ heading: "Overview", paragraphs: [] }]
          ).map((section, i) => (
            <div
              key={`${section.heading}-${i}`}
              style={{
                padding: "9px 18px",
                background: i === 0 ? SCI.paper : "transparent",
                borderLeft:
                  i === 0 ? `2px solid ${SCI.orange}` : "2px solid transparent",
                fontSize: 12,
                display: "grid",
                gridTemplateColumns: "34px 1fr",
                gap: 6,
                alignItems: "center",
              }}
            >
              <span
                style={{ fontFamily: SCI.mono, fontSize: 10, color: SCI.ink3 }}
              >
                § {String(i + 1).padStart(2, "0")}
              </span>
              <span>{section.heading}</span>
            </div>
          ))}
        </div>

        <div>
          <div
            style={{
              padding: "56px 64px 40px",
              borderBottom: `1px solid ${SCI.border}`,
            }}
          >
            <MonoLabel>§ 00 · Executive summary</MonoLabel>
            <h1
              style={{
                fontFamily: SCI.display,
                fontSize: 96,
                lineHeight: 0.9,
                letterSpacing: -2,
                fontWeight: 500,
                margin: "16px 0 10px",
              }}
            >
              {parsed.title}
            </h1>
            <div
              style={{
                fontSize: 16,
                lineHeight: 1.55,
                color: SCI.ink2,
                maxWidth: 760,
              }}
            >
              {parsed.summary}
            </div>
            <div
              style={{
                display: "flex",
                gap: 6,
                marginTop: 18,
                flexWrap: "wrap",
              }}
            >
              <Pill dark>{form.tier}</Pill>
              <Pill>{form.birthTimeAccuracy}</Pill>
              <Pill>{form.birthLocation || "Location pending"}</Pill>
            </div>
          </div>

          <div style={{ padding: "32px 64px 36px" }}>
            {parsed.sections.map((section, idx) => (
              <div
                key={`${section.heading}-${idx}`}
                style={{
                  marginBottom: 32,
                  borderBottom:
                    idx < parsed.sections.length - 1
                      ? `1px solid ${SCI.tint}`
                      : "none",
                  paddingBottom: 24,
                }}
              >
                <div
                  style={{
                    display: "flex",
                    alignItems: "baseline",
                    gap: 10,
                    marginBottom: 10,
                  }}
                >
                  <MonoLabel>§ {String(idx + 1).padStart(2, "0")}</MonoLabel>
                  <div
                    style={{
                      fontFamily: SCI.display,
                      fontSize: 36,
                      lineHeight: 1,
                      letterSpacing: -0.5,
                    }}
                  >
                    {section.heading}
                  </div>
                </div>
                {section.paragraphs.map((paragraph, pIdx) => (
                  <p
                    key={pIdx}
                    style={{
                      margin: "0 0 12px",
                      fontSize: 15,
                      lineHeight: 1.7,
                      color: SCI.ink2,
                    }}
                  >
                    {paragraph}
                  </p>
                ))}
              </div>
            ))}

            {!parsed.sections.length ? (
              <div style={{ fontSize: 14, color: SCI.ink2 }}>
                Report body is empty. Try regenerating the report.
              </div>
            ) : null}
          </div>
        </div>
      </div>
    </div>
  );
}

function BecomingMobile({
  report,
  parsed,
  form,
  onRegenerate,
  onSignOut,
  onNavigate,
}) {
  return (
    <div
      style={{
        width: 390,
        background: SCI.paper,
        color: SCI.ink,
        fontFamily: SCI.body,
        border: `1px solid ${SCI.border}`,
      }}
    >
      <div style={{ padding: "24px 20px" }}>
        <MonoLabel>§ 00 · Overview</MonoLabel>
        <h1
          style={{
            fontFamily: SCI.display,
            fontSize: 42,
            lineHeight: 0.95,
            letterSpacing: -0.6,
            fontWeight: 500,
            margin: "10px 0 12px",
          }}
        >
          {parsed.title}
        </h1>
        <div
          style={{
            fontSize: 14,
            lineHeight: 1.55,
            color: SCI.ink2,
            marginBottom: 14,
          }}
        >
          {parsed.summary}
        </div>
        <div
          style={{
            display: "flex",
            gap: 6,
            flexWrap: "wrap",
            marginBottom: 18,
          }}
        >
          <Pill dark>{form.tier}</Pill>
          <Pill>{form.birthTimeAccuracy}</Pill>
        </div>
      </div>
      <div
        style={{ borderTop: `1px solid ${SCI.border}`, padding: "18px 20px 24px" }}
      >
        {parsed.sections.map((section, idx) => (
          <div key={`${section.heading}-${idx}`} style={{ marginBottom: 22 }}>
            <MonoLabel>§ {String(idx + 1).padStart(2, "0")}</MonoLabel>
            <div
              style={{
                fontFamily: SCI.display,
                fontSize: 24,
                lineHeight: 1.1,
                margin: "8px 0 10px",
              }}
            >
              {section.heading}
            </div>
            {section.paragraphs.map((paragraph, i) => (
              <p
                key={i}
                style={{
                  margin: "0 0 10px",
                  fontSize: 14,
                  lineHeight: 1.6,
                  color: SCI.ink2,
                }}
              >
                {paragraph}
              </p>
            ))}
          </div>
        ))}
      </div>
      <div
        style={{
          padding: "14px 20px",
          borderTop: `1px solid ${SCI.border}`,
          fontFamily: SCI.mono,
          fontSize: 9,
          color: SCI.ink3,
        }}
      >
        {report?.id ? `REPORT ID ${report.id}` : "REPORT READY"}
      </div>
    </div>
  );
}

function InstantDownloadsPage({
  onBack,
  onBuyPack,
  busy,
  onBegin,
  onSignIn,
  onSignUp,
  onNavigateSection,
  onNavigatePage,
  activeSection,
  user,
}) {
  const products = [
    {
      name: "Daily Archetype Forecast",
      format: "PDF + Mobile Cards",
      description:
        "A done-for-you daily guidance system with 31 archetype prompts and quick actions.",
      price: "$19",
    },
    {
      name: "30-Day Ritual Calendar",
      format: "Printable + Wallpaper",
      description:
        "A structured 30-day rhythm calendar for focus, restoration, and consistency.",
      price: "$24",
    },
    {
      name: "Relationship Energy",
      format: "Quick Reference PDF",
      description:
        "Fast reference for attachment signals, conflict loops, and reconnection prompts.",
      price: "$17",
    },
  ];

  return (
    <div
      style={{
        width: 1400,
        background: SCI.paper,
        color: SCI.ink,
        fontFamily: SCI.body,
        border: `1px solid ${SCI.border}`,
      }}
    >
      <div style={{ height: 4, background: SCI.orange }} />

      <div style={{ padding: "56px 56px 24px", borderBottom: `1px solid ${SCI.border}` }}>
        <MonoLabel>§ Instant Downloads</MonoLabel>
        <h1
          style={{
            fontFamily: SCI.display,
            fontSize: 68,
            lineHeight: 0.92,
            letterSpacing: -1,
            margin: "12px 0 16px",
            fontWeight: 500,
          }}
        >
          Immediate digital downloads.
        </h1>
        <div style={{ maxWidth: 900, fontSize: 16, lineHeight: 1.6, color: SCI.ink2 }}>
          Optional add-ons you can buy instantly. Your personalized reading is still the main Amara experience.
          Guidance for reflection only, not medical, legal, or mental-health advice.
        </div>
        <div style={{ marginTop: 16, display: "flex", gap: 12 }}>
          <SciBtn primary size="sm" icon="→" onClick={onBegin}>
            Start Free
          </SciBtn>
          <SciBtn size="sm" onClick={onBack}>
            ← Back
          </SciBtn>
        </div>
      </div>

      <div
        style={{
          borderTop: `1px solid ${SCI.border}`,
          display: "grid",
          gridTemplateColumns: "repeat(3, minmax(0,1fr))",
        }}
      >
        {products.map((product, i) => (
          <div
            key={product.name}
            style={{
              padding: "30px 56px 32px",
              borderRight: i < products.length - 1 ? `1px solid ${SCI.border}` : "none",
            }}
          >
            <MonoLabel size={9}>{product.format}</MonoLabel>
            <div style={{ marginTop: 8, fontFamily: SCI.display, fontSize: 34, lineHeight: 1.05 }}>
              {product.name}
            </div>
            <div style={{ marginTop: 10, fontSize: 14, lineHeight: 1.55, color: SCI.ink2 }}>
              {product.description}
            </div>
            <div style={{ marginTop: 16, display: "flex", alignItems: "center", justifyContent: "space-between" }}>
              <span style={{ fontFamily: SCI.display, fontSize: 34, color: SCI.orange }}>{product.price}</span>
              <SciBtn
                primary
                size="sm"
                icon="→"
                onClick={onBuyPack}
                disabled={busy}
                style={{ background: SCI.orange, borderColor: SCI.orange }}
              >
                Buy
              </SciBtn>
            </div>
          </div>
        ))}
      </div>

      <div style={{ height: 4, background: SCI.orange }} />

      {window.SciFooter ? (
        <window.SciFooter
          onNavigatePage={onNavigatePage}
          onNavigateSection={onNavigateSection}
        />
      ) : null}
    </div>
  );
}

const DRAFT_PAGES = {
  [ROUTES.RESEARCH]: {
    kicker: "Research",
    title: "Research Notes.",
    lead: "Draft research page. This section documents weighting, convergence scoring, validation assumptions, and limitations in plain language.",
    sections: [
      [
        "Method",
        "Convergence is scored by independent instrument agreement weighted by instrument reliability and normalized from 0.00 to 1.00.",
      ],
      [
        "Validation",
        "Psychometric instruments are treated as empirical measures. Archetypal instruments are interpreted as symbolic context and clearly labeled.",
      ],
      [
        "Roadmap",
        "This draft will be replaced with final long-form whitepaper copy and source citations.",
      ],
    ],
  },
  [ROUTES.ABOUT]: {
    kicker: "Company",
    title: "About Amara.",
    lead: "Draft company profile in the current design language. Final team, mission, and history copy can drop in without layout changes.",
    sections: [
      [
        "Mission",
        "Build a practical self-knowledge product that combines statistical personality models and archetypal systems transparently.",
      ],
      [
        "Principles",
        "Clarity over mystique, reproducibility over vague claims, and editorial interpretation that cites source systems.",
      ],
      ["Status", "Public draft content pending final legal and brand review."],
    ],
  },
  [ROUTES.PRESS]: {
    kicker: "Company",
    title: "Press.",
    lead: "Draft press page with reusable blocks for media kit, contact line, and approved boilerplate.",
    sections: [
      [
        "Media Kit",
        "Logos, screenshots, and approved product language are available upon request.",
      ],
      [
        "Boilerplate",
        "Amara builds multi-instrument personality reports that prioritize convergence over single-model certainty.",
      ],
      [
        "Contact",
        "Press inquiries currently route through the contact page while this page remains in draft.",
      ],
    ],
  },
  [ROUTES.CAREERS]: {
    kicker: "Company",
    title: "Careers.",
    lead: "Draft careers page. Position cards and interview process details can be populated from your ATS later with no design rewrite.",
    sections: [
      ["Open Roles", "No live listings are published in this draft."],
      [
        "How We Work",
        "Small interdisciplinary teams, strong writing culture, and evidence-backed product decisions.",
      ],
      [
        "Apply",
        "Use the contact page with role interest in the subject line until ATS integration is added.",
      ],
    ],
  },
  [ROUTES.CONTACT]: {
    kicker: "Company",
    title: "Contact.",
    lead: "Draft contact page with structured channels for support, press, and partnership requests.",
    sections: [
      [
        "Support",
        "Support requests are prioritized by account email and report ID when available.",
      ],
      [
        "Partnerships",
        "Research and enterprise partnership inquiries are reviewed weekly.",
      ],
      ["Response SLA", "Draft SLA is 1-2 business days for standard requests."],
    ],
  },
  [ROUTES.PRIVACY]: {
    kicker: "Legal",
    title: "Privacy Policy (Draft).",
    lead: "Draft legal copy placeholder. Final counsel-approved language can replace this content one-for-one.",
    sections: [
      [
        "Data Collected",
        "Account credentials, onboarding responses, birth data, and generated report metadata.",
      ],
      [
        "Use",
        "Used to generate, persist, and improve your report experience and account continuity.",
      ],
      [
        "Retention",
        "Retention defaults to active-account storage with user-initiated deletion support.",
      ],
    ],
  },
  [ROUTES.TERMS]: {
    kicker: "Legal",
    title: "Terms of Service (Draft).",
    lead: "Draft terms page in production route shape, preserving navigation and footer contract.",
    sections: [
      [
        "Use of Service",
        "Service is provided as informational guidance and not medical, legal, or financial advice.",
      ],
      [
        "Accounts",
        "Users are responsible for credentials and account activity.",
      ],
      [
        "Billing",
        "Paid features follow posted pricing and renewal terms at checkout.",
      ],
    ],
  },
  [ROUTES.DATA_DELETION]: {
    kicker: "Legal",
    title: "Data Deletion (Draft).",
    lead: "Draft deletion policy route with clear replacement target for final compliance copy.",
    sections: [
      [
        "Request",
        "Users can request deletion of profile, responses, and generated reports.",
      ],
      [
        "Processing",
        "Deletion requests are processed in queued batches with confirmation.",
      ],
      [
        "Exceptions",
        "Required billing records may be retained where legally necessary.",
      ],
    ],
  },
  [ROUTES.COOKIES]: {
    kicker: "Legal",
    title: "Cookies (Draft).",
    lead: "Draft cookies policy route. Final list of cookie categories and controls can be inserted without structural change.",
    sections: [
      [
        "Essential",
        "Session and authentication continuity cookies are used for account access.",
      ],
      [
        "Analytics",
        "Optional analytics categories are disabled in this draft.",
      ],
      [
        "Controls",
        "Cookie controls and consent tooling to be finalized with legal copy.",
      ],
    ],
  },
  [ROUTES.NEWSLETTER]: {
    kicker: "Follow",
    title: "Newsletter.",
    lead: "Draft newsletter destination in-site. This can later embed your actual form provider.",
    sections: [
      [
        "Cadence",
        "Weekly: product notes, research findings, and feature drops.",
      ],
      [
        "Topics",
        "Personality science, archetypal interpretation, and product methodology.",
      ],
      ["Status", "Draft endpoint pending live form integration."],
    ],
  },
  [ROUTES.SUBSTACK]: {
    kicker: "Follow",
    title: "Substack.",
    lead: "Draft Substack route keeps the footer link live until the external publication URL is finalized.",
    sections: [
      ["Publication", "Long-form essays and report breakdowns."],
      ["Archive", "Past notes and methodology updates will publish here."],
      ["Status", "Draft route pending external publication launch."],
    ],
  },
  [ROUTES.RSS]: {
    kicker: "Follow",
    title: "RSS Feed.",
    lead: "Draft RSS destination route. Final feed URL can be mapped directly once available.",
    sections: [
      [
        "Feed",
        "Primary feed includes research notes, product updates, and essays.",
      ],
      [
        "Format",
        "RSS/Atom delivery with canonical links and publish timestamps.",
      ],
      ["Status", "Draft route pending feed generation setup."],
    ],
  },
};

function DraftPage({ route, onNavigate }) {
  const page = DRAFT_PAGES[route] || {
    kicker: "Page",
    title: "Not Found.",
    lead: "This route is not mapped yet.",
    sections: [["Route", route]],
  };

  return (
    <div
      style={{
        width: 1400,
        background: SCI.paper,
        color: SCI.ink,
        fontFamily: SCI.body,
        border: `1px solid ${SCI.border}`,
      }}
    >
      <div style={{ padding: "56px 64px 52px" }}>
        <MonoLabel>§ {page.kicker} · draft</MonoLabel>
        <h1
          style={{
            fontFamily: SCI.display,
            fontSize: 82,
            lineHeight: 0.92,
            letterSpacing: -1.5,
            fontWeight: 500,
            margin: "14px 0 16px",
          }}
        >
          {page.title}
        </h1>
        <div
          style={{
            maxWidth: 760,
            fontSize: 16,
            lineHeight: 1.6,
            color: SCI.ink2,
          }}
        >
          {page.lead}
        </div>
      </div>

      <div style={{ borderTop: `1px solid ${SCI.border}` }}>
        {page.sections.map(([heading, body], idx) => (
          <div
            key={`${heading}-${idx}`}
            style={{
              display: "grid",
              gridTemplateColumns: "220px 1fr",
              gap: 20,
              padding: "22px 64px",
              borderBottom: `1px solid ${SCI.tint}`,
            }}
          >
            <MonoLabel>{heading}</MonoLabel>
            <div style={{ fontSize: 14, lineHeight: 1.6, color: SCI.ink2 }}>
              {body}
            </div>
          </div>
        ))}
      </div>

      <div
        style={{
          padding: "16px 64px",
          display: "flex",
          justifyContent: "space-between",
          fontFamily: SCI.mono,
          fontSize: 10,
          color: SCI.ink3,
          letterSpacing: 0.5,
        }}
      >
        <span>DRAFT CONTENT · TO BE REPLACED WITH FINAL COPY</span>
        <span>{route}</span>
      </div>
    </div>
  );
}

function App() {
  const isMobile = useIsMobile();

  const config = window.TAMASHI_CONFIG || {};
  const hasConfig = Boolean(config.supabaseUrl && config.supabaseAnonKey);

  const supabaseClient = React.useMemo(() => {
    if (!hasConfig || !window.supabase?.createClient) return null;
    return window.supabase.createClient(
      config.supabaseUrl,
      config.supabaseAnonKey,
    );
  }, [hasConfig, config.supabaseUrl, config.supabaseAnonKey]);

  const initialRouteContext = React.useMemo(
    () => parseRouteContext(window.location.pathname),
    [],
  );
  const [route, setRoute] = React.useState(initialRouteContext.route);
  const [variant, setVariant] = React.useState(initialRouteContext.variant);
  const [pendingRoute, setPendingRoute] = React.useState("");
  const [activeSection, setActiveSection] = React.useState(
    window.SCI_COPY?.navMethod || "Methodology",
  );
  const [pendingSectionId, setPendingSectionId] = React.useState("");

  const [user, setUser] = React.useState(null);
  const [flowStage, setFlowStage] = React.useState(STAGES.OB1);
  const [form, setForm] = React.useState(DEFAULT_FORM);
  const [report, setReport] = React.useState(null);
  const [progress, setProgress] = React.useState(4);
  const [busy, setBusy] = React.useState(false);

  const [authOpen, setAuthOpen] = React.useState(false);
  const [authMode, setAuthMode] = React.useState("signin");
  const [authEmail, setAuthEmail] = React.useState("");
  const [authPassword, setAuthPassword] = React.useState("");
  const [authLoading, setAuthLoading] = React.useState(false);
  const [authError, setAuthError] = React.useState("");

  const [banner, setBanner] = React.useState(null);
  const [stepErrors, setStepErrors] = React.useState({});
  const [locationOptions, setLocationOptions] = React.useState([]);

  const pendingRouteRef = React.useRef("");
  const debouncedLocation = useDebouncedValue(form.birthLocation, 350);
  const parsedReport = React.useMemo(
    () => parseReport(report?.report_markdown),
    [report],
  );

  const navigate = React.useCallback((target, opts = {}) => {
    const { replace = false, scroll = true } = opts;
    const nextLogical = normalizePathname(target);
    const nextActual = buildPathForVariant(nextLogical, variant);
    const currentActual = normalizePathname(window.location.pathname);
    if (nextActual === currentActual && !replace) {
      if (scroll) window.scrollTo({ top: 0, behavior: "smooth" });
      setRoute(nextLogical);
      return;
    }
    if (replace) window.history.replaceState({}, "", nextActual);
    else window.history.pushState({}, "", nextActual);
    setRoute(nextLogical);
    if (scroll)
      window.requestAnimationFrame(() =>
        window.scrollTo({ top: 0, behavior: "smooth" }),
      );
  }, [variant]);

  React.useEffect(() => {
    pendingRouteRef.current = pendingRoute;
  }, [pendingRoute]);

  React.useEffect(() => {
    const onPopState = () => {
      const parsed = parseRouteContext(window.location.pathname);
      setVariant(parsed.variant);
      setRoute(parsed.route);
    };
    window.addEventListener("popstate", onPopState);
    return () => window.removeEventListener("popstate", onPopState);
  }, []);

  React.useEffect(() => {
    if (typeof document === "undefined") return;
    document.documentElement.dataset.brandVariant =
      variant === "sensei" ? "sensei" : "classic";
  }, [variant]);


  React.useEffect(() => {
    if (!banner) return;
    const t = setTimeout(() => setBanner(null), 4500);
    return () => clearTimeout(t);
  }, [banner]);

  React.useEffect(() => {
    if (!supabaseClient) return;
    let ignore = false;

    supabaseClient.auth.getSession().then(({ data, error }) => {
      if (ignore) return;
      if (error) {
        setBanner({ kind: "error", message: error.message });
        return;
      }
      setUser(data.session?.user || null);
    });

    const { data: authSub } = supabaseClient.auth.onAuthStateChange(
      (_event, session) => {
        setUser(session?.user || null);

        if (session?.user) {
          setAuthOpen(false);
          setAuthError("");
          const target = pendingRouteRef.current;
          if (target) {
            setPendingRoute("");
            navigate(target, { replace: true });
          }
          // Don't navigate here - let user data load first, then useEffect below handles navigation
        } else {
          if (
            PROTECTED_ROUTES.has(parseRouteContext(window.location.pathname).route)
          ) {
            navigate(ROUTES.HOME, { replace: true, scroll: false });
          }
          setFlowStage(STAGES.OB1);
        }
      },
    );

    return () => {
      ignore = true;
      authSub?.subscription?.unsubscribe();
    };
  }, [navigate, supabaseClient]);

  // Handle auth callback from email confirmation link.
  // Supabase v2 PKCE flow sends ?code=...&type=signup in the query string.
  // Implicit flow sends #access_token=... in the hash fragment.
  // We handle both so the user gets logged in after confirming their email.
  React.useEffect(() => {
    if (!supabaseClient) return;

    const params = new URLSearchParams(window.location.search);
    const hashParams = new URLSearchParams(window.location.hash.substring(1));
    const code = params.get("code") || hashParams.get("code");
    const hasImplicitToken = hashParams.get("access_token");

    // PKCE flow: exchange code for session
    if (code) {
      supabaseClient.auth.exchangeCodeForSession(code).then(({ error }) => {
        if (error) {
          setBanner({ kind: "error", message: "Email confirmation failed. Please try again." });
        } else {
          setBanner({ kind: "success", message: "Email confirmed! You're signed in." });
        }
        // Clean the URL
        window.history.replaceState({}, "", window.location.pathname);
      });
      return;
    }

    // Implicit flow: Supabase JS SDK handles the hash automatically,
    // but we need to clean up the URL after getSession picks it up.
    if (hasImplicitToken) {
      // The onAuthStateChange handler above will pick up the session.
      // Just clean the URL so the hash doesn't persist.
      setTimeout(() => {
        window.history.replaceState({}, "", window.location.pathname);
      }, 500);
    }
  }, [supabaseClient]);

  React.useEffect(() => {
    if (!PROTECTED_ROUTES.has(route)) return;
    if (user) return;

    if (!hasConfig) {
      setBanner({
        kind: "error",
        message: "Missing Supabase config in tamashi-config.js.",
      });
      if (route !== ROUTES.HOME)
        navigate(ROUTES.HOME, { replace: true, scroll: false });
      return;
    }

    setPendingRoute((prev) => prev || route);
    setAuthMode("signin");
    setAuthOpen(true);
    if (route !== ROUTES.HOME)
      navigate(ROUTES.HOME, { replace: true, scroll: false });
  }, [hasConfig, navigate, route, user]);

  React.useEffect(() => {
    if (!supabaseClient || !user) return;

    let alive = true;
    (async () => {
      try {
        const [profileRes, sessionRes, reportRes] = await Promise.all([
          supabaseClient
            .from("user_profiles")
            .select("*")
            .eq("user_id", user.id)
            .maybeSingle(),
          supabaseClient
            .from("onboarding_sessions")
            .select("current_step, payload")
            .eq("user_id", user.id)
            .maybeSingle(),
          supabaseClient
            .from("reports")
            .select(
              "id, created_at, model, summary, report_markdown, input_payload",
            )
            .eq("user_id", user.id)
            .order("created_at", { ascending: false })
            .limit(1)
            .maybeSingle(),
        ]);

        if (!alive) return;
        const merged = normalizeForm(sessionRes.data?.payload, profileRes.data);
        setForm(merged);
        if (reportRes.data) setReport(reportRes.data);

        const persistedStage = sessionRes.data?.current_step;
        if (persistedStage === STAGES.GEN) {
          setFlowStage(reportRes.data ? STAGES.BLUEPRINT : STAGES.GEN);
        } else if (
          persistedStage &&
          Object.values(STAGES).includes(persistedStage)
        ) {
          setFlowStage(persistedStage);
        } else if (reportRes.data) {
          setFlowStage(STAGES.BLUEPRINT);
        } else {
          setFlowStage(STAGES.OB1);
        }
      } catch (error) {
        if (!alive) return;
        setBanner({ kind: "error", message: formatErrorMessage(error) });
      }
    })();

    return () => {
      alive = false;
    };
  }, [supabaseClient, user?.id]);

  // Auto-navigate after user data loads based on their progress
  React.useEffect(() => {
    // Only auto-navigate if user just logged in and we're still on HOME
    if (!user || route !== ROUTES.HOME) return;

    // User data has loaded, check their progress
    if (flowStage === STAGES.BLUEPRINT) {
      // User is fully onboarded, go to blueprint
      navigate(ROUTES.BLUEPRINT, { replace: true, scroll: false });
    } else if (flowStage === STAGES.GEN) {
      // User is generating report
      navigate(ROUTES.GENERATION, { replace: true, scroll: false });
    } else if (Object.values(STAGES).includes(flowStage) && flowStage !== STAGES.HOME) {
      // User is in onboarding, go to onboarding page
      navigate(ROUTES.ONBOARDING, { replace: true, scroll: false });
    }
  }, [user, flowStage, route, navigate]);

  React.useEffect(() => {
    if (
      route !== ROUTES.ONBOARDING ||
      flowStage !== STAGES.OB3 ||
      !debouncedLocation ||
      debouncedLocation.trim().length < 3
    ) {
      setLocationOptions([]);
      return;
    }

    let active = true;
    (async () => {
      try {
        const res = await fetch(
          `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(debouncedLocation)}&count=4&language=en&format=json`,
        );
        const json = await res.json();
        if (!active) return;
        const options = Array.isArray(json?.results)
          ? json.results.map((item) => ({
              name: item.name,
              country: item.country || item.admin1 || "Unknown",
              latitude: item.latitude,
              longitude: item.longitude,
              timezone: item.timezone,
            }))
          : [];
        setLocationOptions(options);
      } catch {
        if (!active) return;
        setLocationOptions([]);
      }
    })();

    return () => {
      active = false;
    };
  }, [debouncedLocation, flowStage, route]);

  React.useEffect(() => {
    if (route !== ROUTES.HOME || !pendingSectionId) return;
    const target = document.getElementById(pendingSectionId);
    if (target) {
      target.scrollIntoView({ behavior: "smooth", block: "start" });
      setPendingSectionId("");
    }
  }, [pendingSectionId, route]);

  React.useEffect(() => {
    if (route !== ROUTES.HOME) return;
    const navMethod = window.SCI_COPY?.navMethod || "Methodology";
    const navInstruments = window.SCI_COPY?.navInstruments || "Instruments";
    const navSample = window.SCI_COPY?.navSample || "Sample report";
    const navPricing = window.SCI_COPY?.navPricing || "Pricing";
    const mapping = [
      [navMethod, "section-methodology"],
      [navInstruments, "section-instruments"],
      [navSample, "section-sample"],
      [navPricing, "section-pricing"],
    ];

    const onScroll = () => {
      let current = navMethod;
      mapping.forEach(([label, id]) => {
        const el = document.getElementById(id);
        if (!el) return;
        const top = el.getBoundingClientRect().top;
        if (top <= 140) current = label;
      });
      setActiveSection(current);
    };

    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener("scroll", onScroll);
  }, [route]);

  React.useEffect(() => {
    if (route === ROUTES.GENERATION && report && !busy) {
      navigate(ROUTES.BLUEPRINT, { replace: true, scroll: false });
    }
  }, [busy, navigate, report, route]);

  const pickLocation = React.useCallback((location) => {
    setForm((prev) => ({
      ...prev,
      birthLocation: `${location.name}, ${location.country}`,
      latitude: String(location.latitude ?? ""),
      longitude: String(location.longitude ?? ""),
      timezone: location.timezone || "",
    }));
    setLocationOptions([]);
  }, []);

  const saveSessionState = React.useCallback(
    async (nextStage, nextForm) => {
      if (!supabaseClient || !user) return;
      const payload = buildReportInput(nextForm, user);
      const { error } = await supabaseClient
        .from("onboarding_sessions")
        .upsert({
          user_id: user.id,
          current_step: nextStage,
          payload,
        });
      if (error) throw error;
    },
    [supabaseClient, user],
  );

  const saveProfile = React.useCallback(
    async (currentForm) => {
      if (!supabaseClient || !user) return;

      const birthDateIso = parseUsDate(currentForm.birthDate)?.iso || null;
      const payload = buildReportInput(currentForm, user);
      const { error } = await supabaseClient.from("user_profiles").upsert({
        user_id: user.id,
        email: user.email || null,
        given_name: currentForm.givenName || null,
        family_name: currentForm.familyName || null,
        birth_date: birthDateIso,
        birth_time: buildBirthTime(currentForm),
        birth_time_accuracy: currentForm.birthTimeAccuracy || null,
        birth_location: currentForm.birthLocation || null,
        tier: currentForm.tier,
        add_ons: {
          relationship_pairing: Boolean(currentForm.relationshipPairing),
        },
        onboarding_payload: payload,
      });
      if (error) throw error;
    },
    [supabaseClient, user],
  );

  const startGeneration = React.useCallback(
    async (currentForm, fallbackStage = STAGES.OB5) => {
      if (!supabaseClient || !user) {
        throw new Error("Sign in first to generate a report.");
      }

      setBusy(true);
      setFlowStage(STAGES.GEN);
      setProgress(8);
      navigate(ROUTES.GENERATION);

      const ticker = setInterval(() => {
        setProgress((p) =>
          Math.min(94, p + Math.max(2, Math.round(Math.random() * 9))),
        );
      }, 900);

      try {
        await saveProfile(currentForm);
        await saveSessionState(STAGES.GEN, currentForm);

        const payload = buildReportInput(currentForm, user);
        const fnName = config.edgeFunctionName || "generate-report";
        const { data, error } = await supabaseClient.functions.invoke(fnName, {
          body: {
            input: payload,
            model: config.reportModel || "claude-opus-4-20250514",
          },
        });

        if (error) throw error;
        if (data?.error) throw new Error(data.error);

        setProgress(100);
        setReport(data);
        await saveSessionState(STAGES.BLUEPRINT, currentForm);
        setFlowStage(STAGES.BLUEPRINT);
        navigate(ROUTES.BLUEPRINT);
        setBanner({ kind: "success", message: "Report generated and saved." });
      } catch (error) {
        setFlowStage(fallbackStage);
        if (fallbackStage === STAGES.BLUEPRINT)
          navigate(ROUTES.BLUEPRINT, { replace: true });
        else navigate(ROUTES.ONBOARDING, { replace: true });
        throw error;
      } finally {
        clearInterval(ticker);
        setBusy(false);
      }
    },
    [
      config.edgeFunctionName,
      config.reportModel,
      navigate,
      saveProfile,
      saveSessionState,
      supabaseClient,
      user,
    ],
  );

  function validateStep(stageToValidate, currentForm) {
    const errors = {};
    if (stageToValidate === STAGES.OB1) {
      if (!String(currentForm.givenName || "").trim())
        errors.givenName = "Given name is required.";
      if (!parseUsDate(currentForm.birthDate))
        errors.birthDate = "Use MM / DD / YYYY.";
    }
    if (stageToValidate === STAGES.OB3) {
      if (!String(currentForm.birthLocation || "").trim())
        errors.birthLocation = "Birth location is required.";
    }
    return errors;
  }

  function openSignIn(targetRoute = "") {
    if (!hasConfig) {
      setBanner({
        kind: "error",
        message: "Missing Supabase config in tamashi-config.js.",
      });
      return;
    }
    setPendingRoute(targetRoute || "");
    setAuthMode("signin");
    setAuthOpen(true);
  }

  function beginFlow() {
    if (!hasConfig) {
      setBanner({
        kind: "error",
        message: "Missing Supabase config in tamashi-config.js.",
      });
      return;
    }
    if (!user) {
      openSignIn(ROUTES.ONBOARDING);
      return;
    }
    setFlowStage(STAGES.OB1);
    navigate(ROUTES.ONBOARDING);
  }

  async function continueFlow() {
    if (route !== ROUTES.ONBOARDING) return;
    if (!user) {
      openSignIn(ROUTES.ONBOARDING);
      return;
    }

    const stage = STEP_ORDER.includes(flowStage) ? flowStage : STAGES.OB1;
    const currentIndex = STEP_ORDER.indexOf(stage);
    if (currentIndex === -1) return;

    const errors = validateStep(stage, form);
    setStepErrors(errors);
    if (Object.keys(errors).length > 0) return;

    try {
      const nextStage = STEP_ORDER[currentIndex + 1] || STAGES.GEN;
      if (stage === STAGES.OB5) {
        await startGeneration(form, STAGES.OB5);
        return;
      }
      setStepErrors({});
      setFlowStage(nextStage);
      await saveSessionState(nextStage, form);
    } catch (error) {
      setBanner({ kind: "error", message: formatErrorMessage(error) });
    }
  }

  async function goBack() {
    const stage = STEP_ORDER.includes(flowStage) ? flowStage : STAGES.OB1;
    const currentIndex = STEP_ORDER.indexOf(stage);
    if (currentIndex <= 0) {
      navigate(ROUTES.HOME);
      return;
    }

    const prev = STEP_ORDER[currentIndex - 1];
    setFlowStage(prev);
    try {
      await saveSessionState(prev, form);
    } catch (error) {
      setBanner({ kind: "error", message: formatErrorMessage(error) });
    }
  }

  async function handleAuthSubmit() {
    if (!supabaseClient) {
      setAuthError("Supabase client is not configured.");
      return;
    }
    if (!authEmail || !authPassword) {
      setAuthError("Email and password are required.");
      return;
    }

    setAuthLoading(true);
    setAuthError("");
    try {
      if (authMode === "signin") {
        const { error } = await supabaseClient.auth.signInWithPassword({
          email: authEmail,
          password: authPassword,
        });
        if (error) throw error;
      } else {
        const { data, error } = await supabaseClient.auth.signUp({
          email: authEmail,
          password: authPassword,
        });
        if (error) throw error;
        if (!data.session) {
          setBanner({
            kind: "info",
            message:
              "We sent you a confirmation link. Check your email, then come back and sign in.",
          });
          setAuthMode("signin");
        }
      }
    } catch (error) {
      setAuthError(formatErrorMessage(error));
    } finally {
      setAuthLoading(false);
    }
  }

  async function handleSignOut() {
    if (!supabaseClient) return;
    const { error } = await supabaseClient.auth.signOut();
    if (error) {
      setBanner({ kind: "error", message: error.message });
      return;
    }
    setForm(DEFAULT_FORM);
    setReport(null);
    setFlowStage(STAGES.OB1);
    setPendingRoute("");
    navigate(ROUTES.HOME);
    setBanner({ kind: "info", message: "Signed out." });
  }

  async function handleRegenerate() {
    try {
      await startGeneration(form, STAGES.BLUEPRINT);
    } catch (error) {
      setBanner({ kind: "error", message: formatErrorMessage(error) });
    }
  }

  function handleNavigatePage(nextRoute) {
    navigate(nextRoute);
  }

  function handleNavigateSection(sectionId, label) {
    setActiveSection(label || window.SCI_COPY?.navMethod || "Methodology");
    setPendingSectionId(sectionId);
    if (route !== ROUTES.HOME) {
      navigate(ROUTES.HOME, { scroll: false });
    }
  }

  function handleHomeEnter() {
    if (!parseUsDate(form.birthDate)) {
      setBanner({ kind: "error", message: "Enter DOB as MM / DD / YYYY." });
      return;
    }
    beginFlow();
  }

  async function beginCheckout(tier) {
    if (!supabaseClient) {
      setBanner({ kind: "error", message: "Not connected to server." });
      return;
    }
    const fnName = config.checkoutFunctionName || "create-checkout-session";
    setBusy(true);
    try {
      const { data, error } = await supabaseClient.functions.invoke(fnName, {
        body: { tier: tier || form.tier || "blueprint" },
      });
      if (error) throw error;
      if (data?.url) {
        window.location.href = data.url;
      } else {
        throw new Error("No checkout URL returned.");
      }
    } catch (e) {
      setBanner({
        kind: "error",
        message: e?.message || "Checkout failed. Please try again.",
      });
    } finally {
      setBusy(false);
    }
  }

  function handleTierSelect(tierName) {
    setForm((prev) => ({ ...prev, tier: tierName }));
    if (!user) {
      openSignIn(ROUTES.CHECKOUT);
      return;
    }
    navigate(ROUTES.CHECKOUT);
  }

  function handleInstantDownload() {
    navigate(ROUTES.INSTANT_DOWNLOADS);
  }

  async function handleBuyInstantPack() {
    if (!user) {
      openSignIn(ROUTES.INSTANT_DOWNLOADS);
      return;
    }
    await beginCheckout("instant_downloads_starter_pack");
  }

  function handleWatchReportBuild() {
    if (busy || flowStage === STAGES.GEN) {
      navigate(ROUTES.GENERATION);
      return;
    }
    if (report) {
      navigate(ROUTES.BLUEPRINT);
      return;
    }
    startGeneration(form, STAGES.OB5).catch((error) => {
      setBanner({ kind: "error", message: formatErrorMessage(error) });
    });
  }

  let screen = null;
  const routeKnown = Object.values(ROUTES).includes(route);
  const activeRoute = routeKnown ? route : ROUTES.HOME;
  const stepStage = STEP_ORDER.includes(flowStage) ? flowStage : STAGES.OB1;

  if (activeRoute === ROUTES.HOME) {
    screen = isMobile ? (
      <HomeScienceHiFiMobile
        onBegin={beginFlow}
        onInstantDownload={handleInstantDownload}
        onSignIn={() => openSignIn("")}
        onSignUp={() => { setAuthMode("signup"); setAuthOpen(true); }}
        user={user}
        dobValue={form.birthDate}
        onDobChange={(v) =>
          setForm((s) => ({ ...s, birthDate: formatUsDateFromDigits(v) }))
        }
        onEnterDob={handleHomeEnter}
        onNavigatePage={handleNavigatePage}
      />
    ) : (
      <HomeScienceHiFiDesktop
        onBegin={beginFlow}
        onInstantDownload={handleInstantDownload}
        onSignIn={() => openSignIn("")}
        onSignUp={() => { setAuthMode("signup"); setAuthOpen(true); }}
        user={user}
        onNavigateSection={handleNavigateSection}
        onNavigatePage={handleNavigatePage}
        activeSection={activeSection}
        dobValue={form.birthDate}
        onDobChange={(v) =>
          setForm((s) => ({ ...s, birthDate: formatUsDateFromDigits(v) }))
        }
        onEnterDob={handleHomeEnter}
        onReadSample={() => handleNavigatePage(ROUTES.BLUEPRINT)}
        onSelectTier={handleTierSelect}
      />
    );
  } else if (activeRoute === ROUTES.INSTANT_DOWNLOADS) {
    screen = (
      <InstantDownloadsPage
        onBack={() => handleNavigatePage(ROUTES.HOME)}
        onBuyPack={handleBuyInstantPack}
        busy={busy}
        onBegin={beginFlow}
        onSignIn={() => openSignIn("")}
        onSignUp={() => { setAuthMode("signup"); setAuthOpen(true); }}
        onNavigateSection={handleNavigateSection}
        onNavigatePage={handleNavigatePage}
        activeSection={window.SCI_COPY?.navSample || "Instant Downloads"}
        user={user}
      />
    );
  } else if (activeRoute === ROUTES.ONBOARDING) {
      if (stepStage === STAGES.OB1) {
        screen = isMobile ? (
          <MobileStep1
            form={form}
            setForm={setForm}
            onNext={continueFlow}
            errors={stepErrors}
            busy={busy}
          />
        ) : (
          <Step1Desktop
            form={form}
            setForm={setForm}
            onNext={continueFlow}
            errors={stepErrors}
            busy={busy}
          />
        );
    } else if (stepStage === STAGES.OB2) {
      screen = isMobile ? (
        <MobileStep2
          form={form}
          setForm={setForm}
          onBack={goBack}
          onNext={continueFlow}
          busy={busy}
        />
      ) : (
        <Step2Desktop
          form={form}
          setForm={setForm}
          onBack={goBack}
          onNext={continueFlow}
          busy={busy}
        />
      );
    } else if (stepStage === STAGES.OB3) {
      screen = isMobile ? (
        <MobileStep3
          form={form}
          setForm={setForm}
          onBack={goBack}
          onNext={continueFlow}
          errors={stepErrors}
          locationOptions={locationOptions}
          onPickLocation={pickLocation}
          busy={busy}
        />
      ) : (
        <Step3Desktop
          form={form}
          setForm={setForm}
          onBack={goBack}
          onNext={continueFlow}
          errors={stepErrors}
          locationOptions={locationOptions}
          onPickLocation={pickLocation}
          busy={busy}
        />
      );
    } else if (stepStage === STAGES.OB4) {
      screen = isMobile ? (
        <MobileStep4
          form={form}
          setForm={setForm}
          onBack={goBack}
          onNext={continueFlow}
          busy={busy}
        />
      ) : (
        <Step4Desktop
          form={form}
          setForm={setForm}
          onBack={goBack}
          onNext={continueFlow}
          busy={busy}
        />
      );
    } else {
      screen = isMobile ? (
        <MobileStep5
          form={form}
          setForm={setForm}
          onBack={goBack}
          onNext={continueFlow}
          busy={busy}
        />
      ) : (
        <Step5Desktop
          form={form}
          setForm={setForm}
          onBack={goBack}
          onNext={continueFlow}
          busy={busy}
        />
      );
    }
  } else if (activeRoute === ROUTES.GENERATION) {
    screen = isMobile ? (
      <ReportGenMobile progress={progress} />
    ) : (
      <ReportGenDesktop progress={progress} />
    );
  } else if (activeRoute === ROUTES.BLUEPRINT) {
    screen = isMobile ? (
      <BecomingMobile
        report={report}
        parsed={parsedReport}
        form={form}
        onRegenerate={handleRegenerate}
        onSignOut={handleSignOut}
        onNavigate={handleNavigatePage}
      />
    ) : (
      <BecomingDesktop
        report={report}
        parsed={parsedReport}
        form={form}
        onRegenerate={handleRegenerate}
        onSignOut={handleSignOut}
        onNavigate={handleNavigatePage}
      />
    );
  } else if (activeRoute === ROUTES.DASHBOARD) {
    const Dashboard = isMobile
      ? window.SciDashboardMobile
      : window.SciDashboardDesktop;
    screen = Dashboard ? (
      <Dashboard
        onNavigate={handleNavigatePage}
        onUpgrade={() => handleNavigatePage(ROUTES.CHECKOUT)}
      />
    ) : (
      <DraftPage route={ROUTES.DASHBOARD} onNavigate={handleNavigatePage} />
    );
  } else if (activeRoute === ROUTES.CHAT) {
    const Chat = isMobile ? window.SciChatMobile : window.SciChatDesktop;
    const handleChatSend = async (message) => {
      if (!supabaseClient) throw new Error("Not connected");
      const fnName = config.chatFunctionName || "chat-message";
      const { data, error } = await supabaseClient.functions.invoke(fnName, {
        body: {
          message,
          model: config.chatModel || "claude-opus-4-20250514",
          userId: user?.id,
        },
      });
      if (error) throw error;
      return data;
    };
    screen = Chat ? (
      <Chat onSend={handleChatSend} />
    ) : (
      <DraftPage route={ROUTES.CHAT} onNavigate={handleNavigatePage} />
    );
  } else if (activeRoute === ROUTES.MAP) {
    const Map = isMobile ? window.SciMapMobile : window.SciMapDesktop;
    screen = Map ? (
      <Map />
    ) : (
      <DraftPage route={ROUTES.MAP} onNavigate={handleNavigatePage} />
    );
  } else if (activeRoute === ROUTES.CHECKOUT) {
    const Checkout = isMobile ? window.SciCheckoutMobile : window.SciCheckoutDesktop;
    const tierDisplay = {
      daily_sign: { name: "The Daily Sign", price: "$19" },
      love_union: { name: "Love & Union", price: "$39" },
      atlas: { name: "The Atlas", price: "$79" },
      whole_world: { name: "The Whole World", price: "$129" },
    };
    const td = tierDisplay[form.tier] || tierDisplay.atlas;
    screen = Checkout ? (
      <Checkout onPay={beginCheckout} busy={busy} tier={form.tier} tierName={td.name} tierPrice={td.price} />
    ) : (
      <DraftPage route={ROUTES.CHECKOUT} onNavigate={handleNavigatePage} />
    );
  } else if (activeRoute === ROUTES.CONFIRMATION) {
    const Confirm = isMobile
      ? window.SciConfirmMobile
      : window.SciConfirmDesktop;
    screen = Confirm ? (
      <Confirm
        onWatchReport={handleWatchReportBuild}
        onUpgrade={() => handleNavigatePage(ROUTES.CHECKOUT)}
      />
    ) : (
      <DraftPage route={ROUTES.CONFIRMATION} onNavigate={handleNavigatePage} />
    );
  } else if (activeRoute === ROUTES.NATAL) {
    const Natal = isMobile ? window.NatalChartMobile : window.NatalChartPage;
    screen = Natal ? (
      <Natal />
    ) : (
      <DraftPage route={ROUTES.NATAL} onNavigate={handleNavigatePage} />
    );
  } else if (activeRoute === ROUTES.SCROLLS) {
    const ScrollsPage = window.ScrollsPage;
    // ScrollsPage only works on V2 sensei variant, requires authentication
    screen = (ScrollsPage && variant === "sensei")
      ? React.createElement(ScrollsPage, { onNavigate: handleNavigatePage, user: user, onSignIn: () => openSignIn("") })
      : React.createElement(DraftPage, { route: ROUTES.SCROLLS, onNavigate: handleNavigatePage, lead: "Scrolls storefront — Sensei Lane 1 digital products. Coming soon on this variant." });
  } else if (activeRoute === ROUTES.METHODOLOGY) {
    const MethodologyPage = window.AmaraMethodologyPage;
    screen = MethodologyPage ? (
      <MethodologyPage />
    ) : (
      <DraftPage route={ROUTES.METHODOLOGY} onNavigate={handleNavigatePage} />
    );
  } else if (activeRoute === ROUTES.VEDIC) {
    const VedicPage = window.AmaraVedicMonographPage;
    screen = VedicPage ? (
      <VedicPage />
    ) : (
      <DraftPage route={ROUTES.VEDIC} onNavigate={handleNavigatePage} />
    );
  } else if (DRAFT_PAGES[activeRoute]) {
    screen = <DraftPage route={activeRoute} onNavigate={handleNavigatePage} />;
  } else {
    screen = <DraftPage route={activeRoute} onNavigate={handleNavigatePage} />;
  }

  return (
    <>
      <div style={{ minHeight: "100vh", background: SCI.paper }}>
        {/* Universal header removed for the Amara variant — every screen
           component already renders its own header. Showing both creates a
           double-header on every non-home route. Other variants (sensei /
           wanderer / classic) still get the universal header. */}
        {route !== ROUTES.HOME && !window.SCI_IS_AMARA && (
          <div style={{
            position: "sticky", top: 0, zIndex: 50,
            display: "flex", alignItems: "center",
            padding: "0 24px", height: 52,
            background: SCI.paper,
            borderBottom: `1px solid ${SCI.border}`,
            gap: 16,
          }}>
            <div
              onClick={() => navigate(ROUTES.HOME, { replace: false })}
              style={{ cursor: "pointer", display: "flex", alignItems: "center", gap: 8 }}
            >
              <div style={{
                width: 22, height: 22,
                background: SCI.orange,
                color: SCI.paper,
                display: "flex", alignItems: "center", justifyContent: "center",
                fontSize: 11, fontWeight: 700,
              }}>魂</div>
              <div style={{ fontFamily: SCI.display, fontSize: 18, fontWeight: 500, letterSpacing: -0.3 }}>Amara</div>
            </div>
            <div style={{ flex: 1 }} />
            {window.SciThemeToggle && React.createElement(window.SciThemeToggle)}
            {user && <div style={{ fontFamily: SCI.mono, fontSize: 11, opacity: 0.5, display: isMobile ? "none" : "block" }}>{user.email}</div>}
            {user && <button onClick={() => handleSignOut()} style={{ padding: "5px 14px", border: `1px solid ${SCI.border}`, background: "transparent", cursor: "pointer", fontFamily: SCI.mono, fontSize: 11, letterSpacing: 0.5, color: SCI.ink }}>Sign Out</button>}
          </div>
        )}
        <div style={{ display: "flex", justifyContent: "center" }}>
          {screen}
        </div>
      </div>

      <AuthModal
        open={authOpen}
        mode={authMode}
        email={authEmail}
        password={authPassword}
        loading={authLoading}
        error={authError}
        onEmail={setAuthEmail}
        onPassword={setAuthPassword}
        onMode={setAuthMode}
        onClose={() => setAuthOpen(false)}
        onSubmit={handleAuthSubmit}
      />

      {banner ? (
        <div style={{
          position: "fixed",
          bottom: 24,
          left: "50%",
          transform: "translateX(-50%)",
          zIndex: 60,
          background: banner.kind === "info" ? SCI.orange : banner.kind === "success" ? SCI.forest : "rgba(30,30,30,0.95)",
          color: SCI.paper,
          padding: "16px 28px",
          border: `1px solid ${banner.kind === "info" ? SCI.orange : banner.kind === "success" ? SCI.forest : SCI.border}`,
          fontFamily: SCI.mono,
          fontSize: 12,
          letterSpacing: 0.5,
          maxWidth: "min(480px, 90vw)",
          textAlign: "center",
          boxShadow: "0 4px 24px rgba(0,0,0,0.25)",
          animation: "sci-fade-up .3s ease both",
        }}>
          {banner.message}
        </div>
      ) : null}
    </>
  );
}

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