// AccioAdmit Resume Reviewer — original design restored
// Changes: API calls go through server, single rewrite per bullet
//
// GA4 key events (conversions): mark these manually in the GA4 property UI
// (Admin → Events → mark as key event) — this is not configurable from code.
//   payment_confirmed, email_entered, footer_cta_clicked

const { useState, useEffect } = React;

// ---------------------------------------------------------------------------
// Status normalization — single canonical place in the frontend.
// Maps any legacy label the API might return into one of the 4 valid statuses.
// ---------------------------------------------------------------------------
const STATUS_MAP = {
  strong: 'strong',
  good: 'good',
  improve: 'improve',
  rewrite: 'rewrite',
  'needs-work': 'improve',
  'needs work': 'improve',
  weak: 'rewrite',
  warning: 'improve',
  poor: 'rewrite',
  okay: 'good',
  ok: 'good',
  average: 'good',
};

function normalizeStatus(raw) {
  if (!raw) return 'improve';
  return STATUS_MAP[String(raw).toLowerCase().trim()] || 'improve';
}

// ---------------------------------------------------------------------------
// Pricing — single source of truth is client/pricing.js (also required
// server-side by create-payment-order.js), so the displayed price and the
// amount actually charged can never drift apart. Computed once at load time
// since it only needs to reflect "now" for the current page view.
// ---------------------------------------------------------------------------
const PRICING = window.AccioPricing.getPricing();
const PRICE_INR = PRICING.priceInr;
const PRICE_LABEL = `₹${PRICE_INR.toLocaleString('en-IN')}`;
const ORIGINAL_PRICE_LABEL = `₹${PRICING.originalPriceInr.toLocaleString('en-IN')}`;

// Shared price display: strikethrough original + discounted price during the
// launch offer window, plain price once it ends. Used in every paywall CTA.
const PriceTag = () => PRICING.isLaunchOffer ? (
  <>
    <s className="opacity-60 font-normal mr-1.5">{ORIGINAL_PRICE_LABEL}</s>
    {PRICE_LABEL}
  </>
) : PRICE_LABEL;

// ---------------------------------------------------------------------------
// Tracking context foundation — runs before React renders, sets up
// window.__accio for later analytics/attribution code to consume. This block
// only stores identifiers locally; nothing here transmits data anywhere.
// ---------------------------------------------------------------------------
(function initTrackingContext() {
  if (typeof window === 'undefined') return;
  const ctx = window.__accio = window.__accio || {};

  // Stable client_id across sessions
  let clientId = localStorage.getItem('accio_client_id');
  if (!clientId) {
    clientId = 'ac_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 10);
    localStorage.setItem('accio_client_id', clientId);
  }
  ctx.clientId = clientId;

  // Session ID (fresh per tab/session)
  let sessionId = sessionStorage.getItem('accio_session_id');
  if (!sessionId) {
    sessionId = 'as_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 8);
    sessionStorage.setItem('accio_session_id', sessionId);
  }
  ctx.sessionId = sessionId;

  // Capture attribution: UTMs + ad click IDs + referrer
  const params = new URLSearchParams(window.location.search);
  const attributionKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term', 'gclid', 'fbclid', 'msclkid'];
  const currentTouch = {};
  attributionKeys.forEach(k => {
    const v = params.get(k);
    if (v) currentTouch[k] = v;
  });

  // Last-touch (sessionStorage — this session's attribution)
  let lastTouchStored = {};
  try { lastTouchStored = JSON.parse(sessionStorage.getItem('accio_last_touch') || '{}'); } catch { /* corrupted value — treat as empty */ }
  const lastTouch = Object.keys(currentTouch).length > 0 ? currentTouch : lastTouchStored;
  if (Object.keys(currentTouch).length > 0) {
    sessionStorage.setItem('accio_last_touch', JSON.stringify(currentTouch));
  }

  // First-touch (localStorage, 90-day TTL — attribution of first-ever visit)
  const firstTouchRaw = localStorage.getItem('accio_first_touch');
  let firstTouch = {};
  if (firstTouchRaw) {
    try {
      const parsed = JSON.parse(firstTouchRaw);
      const ageMs = Date.now() - (parsed._ts || 0);
      if (ageMs < 90 * 24 * 60 * 60 * 1000) firstTouch = parsed;
      else localStorage.removeItem('accio_first_touch');
    } catch { localStorage.removeItem('accio_first_touch'); }
  }
  if (!firstTouch._ts && Object.keys(currentTouch).length > 0) {
    firstTouch = { ...currentTouch, _ts: Date.now(), referrer: document.referrer || '', landing_page: window.location.pathname + window.location.search };
    localStorage.setItem('accio_first_touch', JSON.stringify(firstTouch));
  }

  ctx.firstTouch = firstTouch;
  ctx.lastTouch = { ...lastTouch, referrer: document.referrer || '', landing_page: window.location.pathname + window.location.search };
})();

// Rewrites may contain fill-in slots like [your number] — the server strips any
// metric the AI invented and leaves a slot where the user's real number goes.
// Render those slots as visible amber chips instead of plain brackets.
// No `g` flag: String.split matches all occurrences anyway, and a global regex
// would make .test() stateful via lastIndex.
const REWRITE_SLOT_RX = /(\[[^\]]{1,40}\])/;

function hasRewriteSlots(bullet) {
  return REWRITE_SLOT_RX.test(`${bullet.rewrite || ''} ${bullet.rewriteWithMetrics || ''} ${bullet.rewriteWithoutMetrics || ''}`);
}

function renderRewriteText(text) {
  if (!text) return text;
  return String(text).split(REWRITE_SLOT_RX).map((part, i) =>
    /^\[[^\]]{1,40}\]$/.test(part)
      ? <span key={i} className="inline-block bg-amber-100 text-amber-800 border border-amber-300 rounded px-1.5 text-xs font-semibold align-baseline mx-0.5 whitespace-nowrap">{part.slice(1, -1)}</span>
      : part
  );
}

// Cashfree v3 SDK — loaded on demand the first time the user starts a payment,
// so the landing page carries no extra script weight.
let cashfreeSdkPromise = null;
function loadCashfreeSdk() {
  if (window.Cashfree) return Promise.resolve(window.Cashfree);
  if (!cashfreeSdkPromise) {
    cashfreeSdkPromise = new Promise((resolve, reject) => {
      const s = document.createElement('script');
      s.src = 'https://sdk.cashfree.com/js/v3/cashfree.js';
      s.onload = () => resolve(window.Cashfree);
      s.onerror = () => {
        cashfreeSdkPromise = null; // allow retry on next click
        reject(new Error('Could not load the payment window. Check your connection and try again.'));
      };
      document.head.appendChild(s);
    });
  }
  return cashfreeSdkPromise;
}

const TIPS = [
  { emoji: "📌", text: "AdComs spend under 2 minutes on a resume. Every bullet needs to pull its weight." },
  { emoji: "📊", text: "Quantified bullets get noticed. Think: how many, how much, by when, for whom." },
  { emoji: "🌍", text: "International experience is one of the most valued signals at programs like INSEAD and LBS." },
  { emoji: "🎯", text: "Your resume tells AdCom one thing: can this person lead and deliver results?" },
  { emoji: "✍️", text: "Start every bullet with a strong action verb. Led, Built, Drove, Spearheaded, Launched." },
  { emoji: "💡", text: "The Additional Info section is massively underused. It is your personality on paper." },
  { emoji: "🔗", text: "Every experience should connect to your MBA goal. AdCom is always asking: why now, why MBA?" },
  { emoji: "📋", text: "One page is the standard. European programs often accept two, but tight is always better." },
  { emoji: "🪄", text: "Even Hermione revised her essays three times before submitting. Your first draft is never your best." },
  { emoji: "🤝", text: "AdComs look for evidence of collaboration. Who did you work with, influence, or bring along?" },
  { emoji: "📈", text: "Show career progression. Each role should signal growing scope, responsibility, or impact." },
  { emoji: "🏆", text: "Awards and recognition are worth including. Being named by someone else is more credible than self-praise." },
  { emoji: "🗺️", text: "The Marauder's Map showed every path at once. Your resume should show a clear, deliberate one." },
  { emoji: "🚫", text: "Avoid jargon only insiders understand. If an AdCom reader has to Google it, you've already lost them." },
  { emoji: "💬", text: "The PAR method works: Project, Action, Result. Structure every bullet around what changed because of you." },
  { emoji: "🎓", text: "Extracurriculars matter more than people think. AdCom uses them to predict how you show up on campus." },
  { emoji: "🦁", text: "The Sorting Hat considers your whole character, not just your grades. So does every AdCom worth applying to." },
  { emoji: "📅", text: "Gaps in your timeline are fine. What matters is that you can speak confidently about them." },
  { emoji: "✨", text: "Accio is a summoning spell. A great resume summons the right opportunity directly to you." },
  { emoji: "🔁", text: "Your resume is a living document. Every school on your list may deserve a slightly different version." },
];

const SPARKLE_POSITIONS = [
  { left: '7%',  top: '30%', delay: '0s'    },
  { left: '20%', top: '68%', delay: '0.4s'  },
  { left: '35%', top: '12%', delay: '0.8s'  },
  { left: '50%', top: '74%', delay: '0.3s'  },
  { left: '63%', top: '22%', delay: '1.0s'  },
  { left: '76%', top: '58%', delay: '0.6s'  },
  { left: '88%', top: '14%', delay: '1.2s'  },
  { left: '93%', top: '80%', delay: '0.2s'  },
];

const FULL_REPORT_STEPS = [
  'Resume received',
  'Reviewing work experience, bullets, and impact',
  'Checking format, structure, dates, and sections',
  'Applying the MBA admissions lens',
  'Building your final report',
];

const FINAL_STEP_MESSAGES = [
  'Reviewing bullet strength and clarity',
  'Checking whether impact is measurable',
  'Looking for role hierarchy and timeline clarity',
  'Preparing your section-by-section feedback',
  'Building your final action items',
  'Finalising your report',
];

(function() {
  const style = document.createElement('style');
  style.textContent = `
    .paper-bg {
      background-color: #faf9f6;
      background-image: repeating-linear-gradient(transparent, transparent 27px, #d6d3cc 27px, #d6d3cc 28px);
      background-attachment: local;
    }
    .paper-bg::before {
      content: '';
      position: fixed;
      inset: 0;
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='300' height='300' filter='url(%23noise)' opacity='0.035'/%3E%3C/svg%3E");
      pointer-events: none;
      z-index: 0;
    }
    .btn-spin { animation: btn-spin 1s linear infinite; }
    @keyframes btn-spin { to { transform: rotate(360deg); } }
    .progress-bar { transition: width 1.2s ease-out; }
    .locked-blur { filter: blur(4px); user-select: none; pointer-events: none; }
    @keyframes sparkle-float {
      0%, 100% { transform: translateY(0px) scale(1); opacity: 0.75; }
      50% { transform: translateY(-7px) scale(1.15); opacity: 1; }
    }
    .sparkle-item { animation: sparkle-float 2.4s ease-in-out infinite; cursor: pointer; }
    @keyframes sparkle-pop {
      0% { transform: scale(1); }
      40% { transform: scale(1.5); opacity: 0.5; }
      100% { transform: scale(0); opacity: 0; }
    }
    .sparkle-pop { animation: sparkle-pop 0.3s ease-out forwards; }
    @keyframes fade-slide-in {
      0% { opacity: 0; transform: translateY(4px); }
      100% { opacity: 1; transform: translateY(0); }
    }
    .fade-slide-in { animation: fade-slide-in 0.4s ease-out forwards; }
  `;
  document.head.appendChild(style);
})();

async function extractTextFromPDF(file) {
  const arrayBuffer = await file.arrayBuffer();
  const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
  const pageTexts = [];
  let columnGapLines = 0;
  let multiItemLines = 0;

  for (let i = 1; i <= pdf.numPages; i++) {
    const page = await pdf.getPage(i);
    const content = await page.getTextContent();

    // Group items into lines by their Y position.
    // PDF coordinate origin is bottom-left, so items with the same Y are on the same line.
    // We round Y to the nearest 2 units to tolerate minor baseline shifts.
    const lineMap = new Map();
    for (const item of content.items) {
      if (!item.str || !item.str.trim()) continue;
      const transform = item.transform; // [scaleX, skewX, skewY, scaleY, x, y]
      const y = transform ? Math.round(transform[5] / 2) * 2 : 0;
      if (!lineMap.has(y)) lineMap.set(y, []);
      lineMap.get(y).push({ x: transform ? transform[4] : 0, str: item.str, width: item.width || 0 });
    }

    // Two-column detection: look at where lines START (min x per Y group).
    // In a single-column resume, almost all lines start near the left margin.
    // In a two-column layout, many lines start in the right half of the page.
    const viewport = page.getViewport({ scale: 1 });
    const pageWidth = viewport.width;
    for (const [, items] of lineMap) {
      const sorted = [...items].sort((a, b) => a.x - b.x);
      const lineStartX = sorted[0].x;
      multiItemLines++;
      if (lineStartX > pageWidth * 0.40) columnGapLines++; // line starts in right zone
    }

    // Sort lines top-to-bottom (PDF Y is bottom-up, so descending Y = top of page first)
    const sortedYs = [...lineMap.keys()].sort((a, b) => b - a);

    const lines = sortedYs.map(y => {
      const items = lineMap.get(y).sort((a, b) => a.x - b.x); // left-to-right
      // Join items, suppressing the space only when items are truly adjacent (gap < 1pt).
      // This fixes font-run boundary splits: "o" + "riginating", "D" + "eveloping", etc.
      let result = '';
      for (let i = 0; i < items.length; i++) {
        if (i === 0) {
          result = items[i].str;
        } else {
          const gap = items[i].x - (items[i - 1].x + items[i - 1].width);
          result += gap < 1 ? items[i].str : ' ' + items[i].str;
        }
      }
      return result.replace(/\s+/g, ' ').trim();
    }).filter(Boolean);

    pageTexts.push(lines.join('\n'));
  }

  const text = pageTexts.join('\n\n').trim();
  return { text };
}

async function extractTextFromDOCX(file) {
  const arrayBuffer = await file.arrayBuffer();
  const result = await mammoth.extractRawText({ arrayBuffer });
  return result.value;
}

// Normalize bullet characters and merge wrapped continuation lines.
// PDFs using the Symbol font encode bullets as U+F0B7 (private-use area), which
// PDF.js extracts faithfully but the AI does not recognise as a bullet marker.
// This function:
//   1. Replaces all known bullet glyphs with a standard •
//   2. Merges continuation lines (lines that don't start a new bullet, header, or date)
//      back into the bullet they belong to
//   3. Splits any bullet that still contains internal bullet markers (safety pass)
function normalizeBulletLines(text) {
  // Bullet characters: U+F0B7 Symbol-font, U+2022 •, U+25CF ●, U+25E6 ○,
  // U+25AA ▪, U+25A0 ■, U+2713 ✓, U+00B7 ·, U+2219 ∙
  const SYMBOL_BULLETS = /^[•●◦▪■✓·∙]\s*/;
  // Dash/asterisk bullets: only when followed by a space and a letter (avoids date ranges)
  const DASH_BULLET = /^[-–—\*]\s+(?=[A-Za-z])/;
  // Patterns that identify non-continuation lines (dates, year ranges)
  const DATE_LINE = /\b(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\w*[\s.]+\d{4}/i;
  const YEAR_RANGE = /\b\d{4}\s*[-–—]\s*(\d{4}|present)\b/i;

  const lines = text.split('\n');
  const result = [];

  for (const rawLine of lines) {
    const t = rawLine.trim();

    if (!t) {
      result.push('');
      continue;
    }

    if (SYMBOL_BULLETS.test(t)) {
      result.push('• ' + t.replace(SYMBOL_BULLETS, '').trim());
    } else if (DASH_BULLET.test(t) && !DATE_LINE.test(t) && !YEAR_RANGE.test(t)) {
      result.push('• ' + t.replace(DASH_BULLET, '').trim());
    } else {
      // Merge into the previous bullet only if the immediately preceding output
      // line is itself a bullet and this line is not a date/header line.
      const last = result.length > 0 ? result[result.length - 1] : null;
      if (last !== null && last.startsWith('• ') && !DATE_LINE.test(t) && !YEAR_RANGE.test(t)) {
        result[result.length - 1] = last.trimEnd() + ' ' + t;
      } else {
        result.push(t);
      }
    }
  }

  // Safety pass: if a bullet still contains internal bullet chars, split it.
  const INTERNAL_BULLETS = /[•●◦▪■✓]/g;
  const finalLines = [];
  for (const line of result) {
    if (line.startsWith('• ')) {
      const parts = line.slice(2).split(INTERNAL_BULLETS);
      if (parts.length > 1) {
        parts.forEach(p => { if (p.trim()) finalLines.push('• ' + p.trim()); });
        continue;
      }
      if (line.length > 500) {
        console.warn('[normalizeBullets] Possible merged bullet (length=' + line.length + '):', line.slice(0, 120) + '...');
      }
    }
    finalLines.push(line);
  }

  return finalLines.join('\n');
}

function ResumeReviewer({ initialAnalysisData = null, initialStage = 'upload', isSavedReport = false }) {
  const [stage, setStage] = useState(initialStage);
  const [progress, setProgress] = useState(0);
  const [analysisData, setAnalysisData] = useState(initialAnalysisData);
  const [error, setError] = useState(null);
  const [statusMessage, setStatusMessage] = useState('');
  const [slowNudge, setSlowNudge] = useState(false);
  const [copiedId, setCopiedId] = useState(null);
  const [geography, setGeography] = useState('');
  const [targetSchools, setTargetSchools] = useState('');
  const [tipIndex, setTipIndex] = useState(0);
  const [activeTab, setActiveTab] = useState('work');
  const [resumeText, setResumeText] = useState('');
  const [pendingFile, setPendingFile] = useState(null); // File object after selection, before analysis
  const [pendingError, setPendingError] = useState(null);
  const [email, setEmail] = useState('');
  const [emailError, setEmailError] = useState('');
  const [phone, setPhone] = useState('');
  const [couponCode, setCouponCode] = useState('');
  const [showCoupon, setShowCoupon] = useState(false);
  const [newsletterOptIn, setNewsletterOptIn] = useState(true);
  const [showPayment, setShowPayment] = useState(false);
  const [showConfirmReset, setShowConfirmReset] = useState(false);
  const [paymentState, setPaymentState] = useState('idle'); // idle | processing | done
  const [paymentError, setPaymentError] = useState(null);
  const [showRecovery, setShowRecovery] = useState(false);
  const [recoveryOrderId, setRecoveryOrderId] = useState('');
  const [recoveryEmail, setRecoveryEmail] = useState('');
  const [recoveryState, setRecoveryState] = useState('idle'); // idle | loading | expired | already_fulfilled | not_found | error
  const [isDraggingResume, setIsDraggingResume] = useState(false);
  const [loadingMode, setLoadingMode] = useState('preview'); // 'preview' | 'fullReport'
  const [sparkleVisible, setSparkleVisible] = useState(SPARKLE_POSITIONS.map(() => true));
  const [sparkleCollected, setSparkleCollected] = useState(0);
  const analyzeStartTime = React.useRef(0);
  const leadFired = React.useRef(false);
  const emailEnteredFired = React.useRef(false);
  const paywallSeenFired = React.useRef(false);
  const [reportUrl, setReportUrl] = useState(null);
  const [reportSaveState, setReportSaveState] = useState('idle'); // idle | saving | saved | error

  useEffect(() => {
    if (stage !== 'analyzing') return;
    const t = setInterval(() => setTipIndex(i => (i + 1) % TIPS.length), 3500);
    return () => clearInterval(t);
  }, [stage]);

  // Fires once when a preview (unpaid) report first renders — the paywall is
  // visible on that view. Uses the same `_scope` field the results block
  // itself uses to decide preview vs. paid, not a guessed heuristic.
  useEffect(() => {
    if (stage === 'results' && analysisData && !paywallSeenFired.current) {
      if (analysisData._scope === 'preview') {
        gtag_event('paywall_seen');
        paywallSeenFired.current = true;
      }
    }
  }, [stage, analysisData]);

  const clickSparkle = (i) => {
    setSparkleCollected(c => c + 1);
    setSparkleVisible(prev => {
      const next = [...prev];
      next[i] = false;
      if (next.every(v => !v)) {
        setTimeout(() => setSparkleVisible(SPARKLE_POSITIONS.map(() => true)), 1200);
      }
      return next;
    });
  };

  const copyText = (text, id) => {
    gtag_event('bullet_rewrite_copied', { text_length: (text || '').length });
    navigator.clipboard.writeText(text);
    setCopiedId(id);
    setTimeout(() => setCopiedId(null), 2000);
  };

  const gtag_event = (name, params = {}) => {
    if (typeof window !== 'undefined' && typeof window.gtag === 'function') {
      window.gtag('event', name, params);
    }
  };

  // Standard Meta Pixel event tracking — page/funnel-stage signals only.
  // Deliberately no Advanced Matching and no hashed email: nothing here ties
  // a real identity to a purchase. See privacy policy before changing that.
  const fbq_event = (eventName, params = {}) => {
    if (typeof window === 'undefined' || typeof window.fbq !== 'function') return;
    window.fbq('track', eventName, params);
  };

  // Ties a Clarity session recording to this user, for support/UX lookup —
  // this stays inside Microsoft Clarity's dashboard (accessible only to
  // AccioAdmit), not shared onward for ad targeting, unlike the Meta case
  // above. Still real PII though: see the privacy policy's Clarity note.
  const clarity_identify = (email) => {
    if (typeof window === 'undefined' || typeof window.clarity !== 'function') return;
    try {
      window.clarity('identify', email, window.__accio?.sessionId, window.__accio?.clientId);
      window.clarity('set', 'newsletter_opt_in', String(newsletterOptIn));
      if (geography) window.clarity('set', 'geography', geography);
      if (targetSchools) window.clarity('set', 'target_schools', targetSchools);
    } catch (e) { /* clarity failures should never break the app */ }
  };

  // `overrides` lets a caller (recovery flow) supply geography/targetSchools/
  // email explicitly instead of relying on component state, since a setState
  // call and an immediate analyzeResume() call in the same handler would
  // otherwise read the state from before that update ever committed.
  const analyzeResume = async (text, scope = 'preview', overrides = {}) => {
    analyzeStartTime.current = Date.now();
    gtag_event('analysis_started', { scope });
    setSlowNudge(false);
    // Only the preview (first, un-paid) step gets the "taking longer than
    // usual" nudge — the full-report step already sets multi-minute
    // expectations up front, so a 30s nudge there would read as alarming.
    const slowNudgeTimeout = scope === 'preview' ? setTimeout(() => setSlowNudge(true), 30000) : null;
    const stages = [
      { label: 'Reading resume', pct: 15 },
      { label: 'Identifying sections', pct: 35 },
      { label: 'Evaluating bullets', pct: 58 },
      { label: 'Generating rewrites', pct: 78 },
      { label: 'Finalising report', pct: 92 },
    ];
    const finalMessages = FINAL_STEP_MESSAGES;
    let stageIdx = 0, finalMsgIdx = 0;

    setProgress(stages[0].pct);
    setStatusMessage(stages[0].label);

    const interval = setInterval(() => {
      if (stageIdx < stages.length - 1) {
        stageIdx++;
        setStatusMessage(stages[stageIdx].label);
        setProgress(stages[stageIdx].pct);
      } else {
        finalMsgIdx = (finalMsgIdx + 1) % finalMessages.length;
        setStatusMessage(finalMessages[finalMsgIdx]);
      }
    }, 4500);

    try {
      const jobId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`;

      const response = await fetch('/api/analyze', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          jobId, resumeText: text,
          geography: overrides.geography ?? geography,
          targetSchools: overrides.targetSchools ?? targetSchools,
          scope,
          email: (overrides.email ?? email).trim(),
          newsletterOptIn,
          paymentOrderId: scope === 'full' ? pendingPaymentOrderId.current : undefined,
          client_id: window.__accio?.clientId || null,
          session_id: window.__accio?.sessionId || null,
          first_touch: window.__accio?.firstTouch || {},
          last_touch: window.__accio?.lastTouch || {},
        }),
      });

      if (!response.ok) {
        let errData;
        try { errData = await response.json(); } catch { /* ignore */ }
        throw new Error(errData?.error || `Server error (${response.status}). Please try again.`);
      }

      // Synchronous response (Express locally, or any 200 from the server).
      if (response.status === 200) {
        clearInterval(interval);
        if (slowNudgeTimeout) clearTimeout(slowNudgeTimeout);
        const data = await response.json();
        if (data.error) throw new Error(data.error);
        if (!data.workExperience || !data.bottomLine) throw new Error('Incomplete analysis received. Please try again.');
        const hasTwoColWarning = (data._parserMeta?.parsingWarnings || []).some(w => w.type === 'two-column-layout');
        const totalBulletsFound = (data.workExperience || []).reduce((n, r) => n + (r.bullets || []).length, 0);
        if (hasTwoColWarning && totalBulletsFound === 0) { setStage('two-column'); return; }
        setProgress(100);
        gtag_event(scope === 'full' ? 'full_report_complete' : 'preview_complete', {
          duration_ms: Date.now() - analyzeStartTime.current,
        });
        setTimeout(() => { setAnalysisData(data); setStage('results'); }, 500);
        if (scope === 'full') {
          setReportSaveState('saving');
          fetch('/api/save-paid-report', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ email: (overrides.email ?? email).trim(), resumeFilename: pendingFile?.name || null, reportJson: data, paymentOrderId: pendingPaymentOrderId.current, client_id: window.__accio?.clientId || null, first_touch: window.__accio?.firstTouch || {}, last_touch: window.__accio?.lastTouch || {} }),
          })
            .then(async r => {
              if (r.ok) return r.json();
              let errBody;
              try { errBody = await r.json(); } catch { /* ignore */ }
              throw new Error(errBody?.error ? `HTTP ${r.status}: ${errBody.error}` : `HTTP ${r.status}`);
            })
            .then(d => {
              if (d.reportUrl) {
                setReportUrl(d.reportUrl);
                // Update the address bar to the permanent report link so the
                // browser's Back button (and any bookmark) returns here, not
                // to whatever page was open before payment.
                try { window.history.pushState({}, '', d.reportUrl); } catch { /* history API unavailable — link still works via the copy button */ }
              }
              setReportSaveState('saved');
            })
            .catch(err => { console.error('[savePaidReport]', err); setReportSaveState('error'); });
        }
        return;
      }

      // 202 — background function kicked off. Poll Netlify Blobs for the result.
      const POLL_MS = 3000;
      const deadline = Date.now() + (scope === 'preview' ? 4 * 60 * 1000 : 10 * 60 * 1000);

      while (Date.now() < deadline) {
        await new Promise(r => setTimeout(r, POLL_MS));

        let pollData;
        try {
          const pollRes = await fetch(`/api/analyze-poll?jobId=${jobId}`);
          pollData = await pollRes.json();
        } catch {
          continue; // transient network issue — retry
        }

        if (pollData.status === 'error') throw new Error(pollData.error || 'Analysis failed. Please try again.');

        if (pollData.status === 'done') {
          clearInterval(interval);
          if (slowNudgeTimeout) clearTimeout(slowNudgeTimeout);
          const data = pollData.data;

          if (!data.workExperience || !data.bottomLine) throw new Error('Incomplete analysis received. Please try again.');

          // If server detected a two-column layout and found no bullets, redirect before showing results
          const hasTwoColWarning = (data._parserMeta?.parsingWarnings || []).some(w => w.type === 'two-column-layout');
          const totalBulletsFound = (data.workExperience || []).reduce((n, r) => n + (r.bullets || []).length, 0);
          if (hasTwoColWarning && totalBulletsFound === 0) {
            setStage('two-column');
            return;
          }

          setProgress(100);
          gtag_event(scope === 'full' ? 'full_report_complete' : 'preview_complete', {
            duration_ms: Date.now() - analyzeStartTime.current,
          });
          setTimeout(() => { setAnalysisData(data); setStage('results'); }, 500);
          if (scope === 'full') {
            setReportSaveState('saving');
            fetch('/api/save-paid-report', {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({ email: (overrides.email ?? email).trim(), resumeFilename: pendingFile?.name || null, reportJson: data, paymentOrderId: pendingPaymentOrderId.current, client_id: window.__accio?.clientId || null, first_touch: window.__accio?.firstTouch || {}, last_touch: window.__accio?.lastTouch || {} }),
            })
              .then(r => r.ok ? r.json() : Promise.reject(r.status))
              .then(d => {
                if (d.reportUrl) {
                  setReportUrl(d.reportUrl);
                  // Update the address bar to the permanent report link so the
                  // browser's Back button (and any bookmark) returns here, not
                  // to whatever page was open before payment.
                  try { window.history.pushState({}, '', d.reportUrl); } catch { /* history API unavailable — link still works via the copy button */ }
                }
                setReportSaveState('saved');
              })
              .catch(err => { console.error('[savePaidReport]', err); setReportSaveState('error'); });
          }
          return;
        }
        // status === 'pending' — keep polling
      }

      throw new Error('Analysis is taking longer than expected. Please try again.');
    } catch (err) {
      clearInterval(interval);
      if (slowNudgeTimeout) clearTimeout(slowNudgeTimeout);
      gtag_event('analysis_error', { scope, error: err.message });
      setError(err.message || 'Something went wrong. Please try again.');
      setStage('error');
    }
  };

  // Step 1 — read file, store text, show CTA
  const processSelectedFile = async (file) => {
    if (!file) return;
    setPendingFile(file);
    setPendingError(null);
    setResumeText('');
    try {
      let text = '';
      const name = file.name.toLowerCase();
      if (file.type === 'application/pdf' || name.endsWith('.pdf')) {
        const result = await extractTextFromPDF(file);
        text = result.text;
      } else if (name.endsWith('.docx')) {
        text = await extractTextFromDOCX(file);
      } else if (name.endsWith('.doc')) {
        try { text = await extractTextFromDOCX(file); } catch { text = await file.text(); }
      } else {
        text = await file.text();
      }
      text = normalizeBulletLines(text.trim());
      if (text.length < 50) throw new Error('EMPTY');
      if (text.length > 60000) throw new Error('TOO_LONG');
      const words = text.split(/\s+/).filter(w => w.length > 2);
      if (words.length < 20) throw new Error('SHORT');
      setResumeText(text);
      gtag_event('resume_uploaded', { file_type: file.name.split('.').pop().toLowerCase() });
      fbq_event('ViewContent', { content_name: 'Resume Upload', content_category: 'Tool' });
    } catch (err) {
      let msg = 'Could not read this file. Try saving as PDF and uploading again.';
      if (err.message === 'EMPTY') msg = 'The file appears to be empty. Please check and try again.';
      if (err.message === 'SHORT') msg = 'Not enough readable text found. Make sure this is a text-based resume, not a scanned image.';
      if (err.message === 'TOO_LONG') msg = 'This file has too much text to be a resume. Please upload just your 1-2 page resume, not a portfolio or combined document.';
      gtag_event('file_upload_error', { reason: err.message || 'unknown', file_type: file?.name?.split('.').pop()?.toLowerCase() || 'unknown' });
      setPendingError(msg);
      setPendingFile(null);
    }
  };

  const handleFileSelect = async (e) => {
    await processSelectedFile(e.target.files?.[0]);
  };

  const handleResumeDragOver = (e) => {
    e.preventDefault();
    setIsDraggingResume(true);
  };

  const handleResumeDragLeave = (e) => {
    e.preventDefault();
    if (e.currentTarget.contains(e.relatedTarget)) return;
    setIsDraggingResume(false);
  };

  const handleResumeDrop = async (e) => {
    e.preventDefault();
    setIsDraggingResume(false);
    await processSelectedFile(e.dataTransfer.files?.[0]);
  };

  // Step 2 — user clicks Analyse (after email is entered)
  const handleAnalyse = async () => {
    if (!resumeText) return;
    const trimmedEmail = email.trim();
    if (!trimmedEmail) {
      setEmailError('Please enter your email to continue.');
      return;
    }
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmedEmail)) {
      setEmailError('Please enter a valid email address.');
      return;
    }
    setEmailError('');
    clarity_identify(email.trim());
    if (!leadFired.current) {
      fbq_event('Lead', { content_name: 'Email Captured' });
      leadFired.current = true;
    }
    if (!emailEnteredFired.current) {
      gtag_event('email_entered', { newsletter_opt_in: newsletterOptIn });
      emailEnteredFired.current = true;
    }
    setLoadingMode('preview');
    setStage('analyzing');
    setProgress(10);
    setStatusMessage('Reading your file...');
    await analyzeResume(resumeText);
  };

  // Stores the Cashfree order ID after create-payment-order succeeds.
  // Passed to save-paid-report so the server can verify payment status before saving.
  const pendingPaymentOrderId = React.useRef(null);

  // Poll payment-status until the webhook (or direct gateway check) confirms.
  const waitForPaymentConfirmation = async (orderId, timeoutMs = 90 * 1000) => {
    const deadline = Date.now() + timeoutMs;
    while (Date.now() < deadline) {
      try {
        const res = await fetch(`/api/payment-status?orderId=${encodeURIComponent(orderId)}`);
        const data = await res.json();
        if (data.status === 'paid') return 'paid';
        if (data.status === 'failed') return 'failed';
      } catch { /* transient network issue — keep polling */ }
      await new Promise(r => setTimeout(r, 2500));
    }
    return 'timeout';
  };

  const startFullAnalysis = async () => {
    setPaymentState('done');
    await new Promise(r => setTimeout(r, 600));
    setShowPayment(false);
    setPaymentState('idle');
    setLoadingMode('fullReport');
    setSparkleVisible(SPARKLE_POSITIONS.map(() => true));
    setSparkleCollected(0);
    setStage('analyzing');
    setProgress(10);
    setStatusMessage('Reading your file...');
    await analyzeResume(resumeText, 'full');
  };

  const handlePayment = async () => {
    gtag_event('get_full_report_clicked');
    fbq_event('AddToCart', { content_name: 'Full Resume Report' });
    setPaymentError(null);
    setPaymentState('processing');

    // A previous attempt may have paid but timed out before confirmation —
    // re-check that order first so the user is never charged twice.
    if (pendingPaymentOrderId.current) {
      const prior = await waitForPaymentConfirmation(pendingPaymentOrderId.current, 5000);
      if (prior === 'paid') {
        // No order data fetched on this fast path — PRICE_INR matches the
        // actual price from pricing.js, not a guess.
        gtag_event('payment_confirmed', { via: 'prior_order', value: PRICE_INR, currency: 'INR', transaction_id: pendingPaymentOrderId.current });
        fbq_event('Purchase', { value: PRICE_INR, currency: 'INR', content_name: 'Full Resume Report' });
        if (typeof window.clarity === 'function') { try { window.clarity('set', 'paid', 'true'); window.clarity('event', 'purchase'); } catch {} }
        await startFullAnalysis();
        return;
      }
    }

    const trimmedCoupon = couponCode.trim();
    const phoneDigits = phone.replace(/\D/g, '');
    if (!trimmedCoupon && !/^[6-9]\d{9}$/.test(phoneDigits)) {
      setPaymentState('idle');
      setPaymentError('Please enter a valid 10-digit mobile number.');
      return;
    }

    // 1. Create the order server-side
    let orderRes;
    try {
      orderRes = await fetch('/api/create-payment-order', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email: email.trim(), phone: phoneDigits, couponCode: trimmedCoupon }),
      });
    } catch {
      setPaymentState('idle');
      setPaymentError('Could not reach the payment service. Check your connection and try again.');
      return;
    }

    // No payment gateway deployed (local dev / free beta) — unlock directly.
    if (orderRes.status === 404) {
      await startFullAnalysis();
      return;
    }

    let orderData = null;
    try { orderData = await orderRes.json(); } catch { /* handled below */ }

    // Coupon fully waived payment — skip Cashfree entirely.
    if (orderRes.ok && orderData?.free) {
      pendingPaymentOrderId.current = orderData.orderId;
      gtag_event('payment_confirmed', { via: 'coupon', value: 0, currency: 'INR', transaction_id: orderData.orderId });
      await startFullAnalysis();
      return;
    }

    if (!orderRes.ok || !orderData?.paymentSessionId) {
      setPaymentState('idle');
      setPaymentError(orderData?.error || 'Could not start the payment. Please try again.');
      return;
    }

    // 2. Open the Cashfree checkout in a modal
    try {
      const CashfreeCtor = await loadCashfreeSdk();
      fbq_event('InitiateCheckout', { value: orderData?.amount || PRICE_INR, currency: orderData?.currency || 'INR', content_name: 'Full Resume Report' });
      const cashfree = CashfreeCtor({ mode: orderData.mode === 'production' ? 'production' : 'sandbox' });
      const result = await cashfree.checkout({
        paymentSessionId: orderData.paymentSessionId,
        redirectTarget: '_modal',
      });
      if (result?.error) {
        // User closed the checkout or the payment was declined
        gtag_event('payment_modal_dismissed');
        setPaymentState('idle');
        setPaymentError('The payment was not completed. No worries — you can try again whenever you are ready.');
        return;
      }
    } catch (err) {
      setPaymentState('idle');
      setPaymentError(err.message || 'The payment window could not be opened. Please try again.');
      return;
    }

    // 3. Confirm server-side before unlocking
    const outcome = await waitForPaymentConfirmation(orderData.orderId);
    if (outcome === 'failed') {
      gtag_event('payment_failed', { reason: 'gateway_declined' });
      setPaymentState('idle');
      setPaymentError('The payment did not go through and you have not been charged. Please try again.');
      return;
    }
    if (outcome === 'timeout') {
      gtag_event('payment_failed', { reason: 'confirmation_timeout' });
      // Keep the orderId — if the money did leave their account, the retry
      // path above will find the paid order and unlock without a second charge.
      pendingPaymentOrderId.current = orderData.orderId;
      setPaymentState('idle');
      setPaymentError('We could not confirm the payment yet. If money was deducted, wait a minute and press the button again — we will find your payment and unlock without charging you twice.');
      return;
    }

    pendingPaymentOrderId.current = orderData.orderId;
    gtag_event('payment_confirmed', { value: orderData.amount || PRICE_INR, currency: orderData.currency || 'INR', transaction_id: pendingPaymentOrderId.current });
    fbq_event('Purchase', { value: orderData.amount || PRICE_INR, currency: orderData.currency || 'INR', content_name: 'Full Resume Report' });
    if (typeof window.clarity === 'function') { try { window.clarity('set', 'paid', 'true'); window.clarity('event', 'purchase'); } catch {} }
    await startFullAnalysis();
  };

  // Recovers a paid full report after a closed tab or a failed attempt, using
  // the order reference from the payment confirmation email. If the resume
  // buffer is still alive, this goes straight to the report with no re-upload
  // and no new charge. If it's expired, it sets up state so the normal upload
  // flow below will recognize the order as already paid and skip payment.
  const handleRecoverOrder = async () => {
    setRecoveryState('loading');
    try {
      const res = await fetch('/api/recover-paid-order', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ orderId: recoveryOrderId.trim(), email: recoveryEmail.trim() }),
      });
      const data = await res.json().catch(() => ({}));

      // The endpoint uses HTTP status meaningfully (404 = not_found is a real,
      // expected outcome here, not a failure) — read data.status first and
      // only fall back to a generic error when the response has none at all.
      const KNOWN_STATUSES = ['recoverable', 'expired', 'already_fulfilled', 'not_found'];
      if (!KNOWN_STATUSES.includes(data.status)) { setRecoveryState('error'); return; }

      if (data.status === 'recoverable') {
        gtag_event('paid_order_recovered');
        pendingPaymentOrderId.current = recoveryOrderId.trim();
        setShowRecovery(false);
        setRecoveryState('idle');
        setLoadingMode('fullReport');
        setSparkleVisible(SPARKLE_POSITIONS.map(() => true));
        setSparkleCollected(0);
        setStage('analyzing');
        setProgress(10);
        setStatusMessage('Reading your file...');
        await analyzeResume(data.resumeText, 'full', {
          email: recoveryEmail.trim(),
          geography: data.geography || '',
          targetSchools: data.targetSchools || '',
        });
        return;
      }

      if (data.status === 'expired') {
        // Buffer's gone, but the order is genuinely paid — remember it so the
        // normal upload-then-pay flow recognizes that and skips charging again.
        pendingPaymentOrderId.current = recoveryOrderId.trim();
        setEmail(recoveryEmail.trim());
      }
      setRecoveryState(data.status || 'error');
    } catch {
      setRecoveryState('error');
    }
  };

  const RecoveryModal = () => (
    <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/40">
      <div className="bg-white rounded-2xl shadow-2xl max-w-sm w-full px-6 py-6">
        <div className="flex items-center justify-between mb-1">
          <h2 className="text-lg font-bold text-gray-900">Recover your report</h2>
          <button onClick={() => { setShowRecovery(false); setRecoveryState('idle'); }} className="text-gray-400 hover:text-gray-600 text-xl leading-none">×</button>
        </div>
        <p className="text-sm text-gray-500 leading-relaxed mb-5">Already paid but didn't get your report? Enter the order reference from your payment confirmation email.</p>

        {recoveryState === 'expired' && (
          <div className="bg-amber-50 border border-amber-200 rounded-lg p-3 mb-4 text-sm text-amber-800 leading-relaxed">
            Your recovery window has passed, but your order is confirmed paid. Upload your resume below and continue as normal — you will not be charged again.
          </div>
        )}
        {recoveryState === 'already_fulfilled' && (
          <div className="bg-blue-50 border border-blue-200 rounded-lg p-3 mb-4 text-sm text-blue-800 leading-relaxed">
            You already have a report. Check your inbox for an email from AccioAdmit, or email <a href="mailto:workwithaccioadmit@gmail.com" className="underline">workwithaccioadmit@gmail.com</a> if you can't find it.
          </div>
        )}
        {recoveryState === 'not_found' && (
          <div className="bg-red-50 border border-red-200 rounded-lg p-3 mb-4 text-sm text-red-700 leading-relaxed">
            We couldn't find a paid order matching those details. Double-check the order reference, or email <a href="mailto:workwithaccioadmit@gmail.com" className="underline">workwithaccioadmit@gmail.com</a> for help.
          </div>
        )}
        {recoveryState === 'error' && (
          <div className="bg-red-50 border border-red-200 rounded-lg p-3 mb-4 text-sm text-red-700 leading-relaxed">
            Something went wrong. Please try again in a moment.
          </div>
        )}

        {(recoveryState === 'expired' || recoveryState === 'already_fulfilled') ? (
          <button onClick={() => { setShowRecovery(false); setRecoveryState('idle'); }}
            className="w-full py-3 rounded-xl text-sm font-bold bg-blue-600 hover:bg-blue-700 text-white transition">
            Got it
          </button>
        ) : (
          <>
            <input
              type="text"
              value={recoveryOrderId}
              onChange={e => setRecoveryOrderId(e.target.value)}
              placeholder="Order reference (e.g. accio-...)"
              className="w-full px-3 py-2.5 rounded-lg border border-gray-300 text-sm mb-3 focus:outline-none focus:ring-2 focus:ring-blue-400"
            />
            <input
              type="email"
              value={recoveryEmail}
              onChange={e => setRecoveryEmail(e.target.value)}
              placeholder="you@example.com"
              className="w-full px-3 py-2.5 rounded-lg border border-gray-300 text-sm mb-4 focus:outline-none focus:ring-2 focus:ring-blue-400"
            />
            <button
              onClick={handleRecoverOrder}
              disabled={recoveryState === 'loading' || !recoveryOrderId.trim() || !recoveryEmail.trim()}
              className="w-full py-3 rounded-xl text-sm font-bold bg-blue-600 hover:bg-blue-700 text-white transition disabled:opacity-60">
              {recoveryState === 'loading' ? 'Looking up your order...' : 'Recover my report'}
            </button>
          </>
        )}
      </div>
    </div>
  );

  // ── UPLOAD ────────────────────────────────────────────
  if (stage === 'upload') return (
    <div className="min-h-screen bg-gradient-to-br from-blue-900 via-blue-800 to-blue-950">
      {showRecovery && <RecoveryModal />}

      {/* Nav */}
      <div className="px-6 py-4 flex items-center justify-between max-w-6xl mx-auto border-b border-white/10">
        <div className="flex items-center gap-2">
          <span className="text-yellow-400 text-xl">✨</span>
          <span className="text-lg font-bold text-white">Accio <span className="text-yellow-400">Admit</span></span>
        </div>
        <div className="flex items-center gap-6">
          <a href="https://accioadmit.com/" target="_blank" rel="noopener noreferrer" className="text-blue-300 hover:text-white text-sm transition hidden sm:block">Who we are</a>
          <a href="https://accios-newsletter.beehiiv.com/" target="_blank" rel="noopener noreferrer" className="text-blue-300 hover:text-white text-sm transition hidden sm:block">Newsletter</a>
          <a href="http://cal.com/accio-admit/30min" target="_blank" rel="noopener noreferrer" className="bg-white/10 hover:bg-white/20 text-white text-sm font-medium px-4 py-1.5 rounded-full transition border border-white/20">Chat with us</a>
          <button onClick={() => setShowRecovery(true)} className="text-blue-400/70 hover:text-blue-300 text-xs transition hidden sm:block underline decoration-dotted">Already paid?</button>
        </div>
      </div>

      {/* Mobile-only nudge toward desktop for the editing experience */}
      <div className="sm:hidden max-w-6xl mx-auto px-6 pt-6">
        <div className="flex items-start gap-2.5 bg-yellow-400/10 border border-yellow-400/25 rounded-xl px-4 py-3">
          <span className="text-base flex-shrink-0">💻</span>
          <p className="text-xs text-blue-100 leading-relaxed">This works best on a laptop or desktop, so you can copy the rewrites straight into your resume as you go. On your phone? No worries, if you buy the full report we'll email you a link so you can open it later on your computer.</p>
        </div>
      </div>

      {/* Two-column hero */}
      <div className="max-w-6xl mx-auto px-6 pt-12 pb-10 grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">

        {/* Left — headline + trust */}
        <div>
          <div className="inline-block bg-yellow-400/15 border border-yellow-400/30 text-yellow-300 text-xs font-semibold px-4 py-1.5 rounded-full mb-6 uppercase tracking-widest">
            Free instant preview
          </div>
          <h1 className="text-4xl sm:text-5xl font-bold text-white leading-tight mb-5">
            Is your resume ready for a top MBA?
          </h1>
          <p className="text-blue-200 text-lg leading-relaxed mb-6">
            Upload your resume and see exactly which bullets are working, which ones need improvement, and how to rewrite them for a stronger MBA application.
          </p>

          {/* Founder trust signal */}
          <div className="flex items-start gap-3 bg-white/8 border border-white/10 rounded-xl px-4 py-3 mb-8">
            <span className="text-yellow-400 text-lg mt-0.5">🎓</span>
            <p className="text-sm text-blue-200 leading-relaxed">
              Built by <span className="text-white font-semibold">Oxford Saïd</span> and <span className="text-white font-semibold">IE Business School</span> MBA alumni with hands-on MBA admissions experience.
            </p>
          </div>

          {/* School logos strip */}
          <div className="space-y-2">
            <p className="text-xs text-blue-400 uppercase tracking-widest font-semibold">Used by applicants to</p>
            <div className="flex flex-wrap gap-2">
              {[
                { name: 'INSEAD', domain: 'insead.edu' },
                { name: 'LBS', domain: 'london.edu' },
                { name: 'Oxford Saïd', domain: 'sbs.ox.ac.uk' },
                { name: 'HEC Paris', domain: 'hec.edu' },
                { name: 'Cambridge Judge', domain: 'jbs.cam.ac.uk' },
                { name: 'IESE', domain: 'iese.edu' },
              ].map(({ name, domain }) => (
                <span key={name} className="flex items-center gap-1.5 bg-white/8 border border-white/10 text-xs font-semibold text-white px-3 py-1.5 rounded-lg">
                  <img src={`https://www.google.com/s2/favicons?domain=${domain}&sz=16`} alt="" className="w-4 h-4 rounded-sm flex-shrink-0" onError={e => { e.target.style.display='none'; }} />
                  {name}
                </span>
              ))}
            </div>
          </div>
        </div>

        {/* Right — upload card (the main event) */}
        <div className="bg-white/10 backdrop-blur-lg rounded-3xl shadow-2xl border border-white/20 overflow-hidden">

          {!pendingFile ? (
            /* Step 1: drop zone */
            <label
              className="block cursor-pointer"
              onDragOver={handleResumeDragOver}
              onDragLeave={handleResumeDragLeave}
              onDrop={handleResumeDrop}
            >
              <input type="file" accept=".pdf,.docx,.doc,.txt" onChange={handleFileSelect} className="hidden" />
              <div className={`p-8 text-center transition-all group ${isDraggingResume ? 'bg-white/10' : 'hover:bg-white/5'}`}>
                <div className={`w-16 h-16 bg-yellow-400/10 border-2 border-dashed rounded-2xl flex items-center justify-center mx-auto mb-5 transition-all ${isDraggingResume ? 'border-yellow-300 scale-105 bg-yellow-400/20' : 'border-yellow-400/50 group-hover:border-yellow-400'}`}>
                  <span className="text-3xl">📄</span>
                </div>
                <p className="text-white text-lg font-bold mb-1">{isDraggingResume ? 'Drop your resume here' : 'Drag your resume here'}</p>
                <p className="text-blue-300 text-sm mb-1">or choose a PDF or Word file</p>
                <p className="text-blue-400/60 text-xs mb-6">Works best with single-column PDF or Word resumes. Canva-style templates may not parse cleanly.</p>
                <div className="bg-gradient-to-r from-yellow-500 to-yellow-400 text-blue-900 px-8 py-3 rounded-full font-bold text-sm hover:from-yellow-400 hover:to-yellow-300 transition-all shadow-lg inline-block">
                  Choose File
                </div>
              </div>
            </label>
          ) : null}
          {!pendingFile && (
            <div className="px-8 pb-5 text-center">
              <p className="text-blue-400/50 text-xs">Your resume stays private. <a href="/privacy.html" className="underline hover:text-blue-300">See how we handle your data</a>.</p>
            </div>
          )}
          {pendingFile && (
            /* Step 2: file received — email capture */
            <div className="p-8">

              {/* Header */}
              <div className="mb-5">
                <div className="flex items-center gap-2 mb-1">
                  <span className="text-green-400 text-xl">✓</span>
                  <h3 className="text-white text-lg font-bold">Resume uploaded</h3>
                </div>
                <p className="text-blue-300 text-sm">Where should we send your preview?</p>
              </div>

              {/* File chip */}
              <div className="flex items-center gap-3 bg-green-500/10 border border-green-400/30 rounded-xl px-4 py-2.5 mb-5">
                <span className="text-blue-300 text-base">📄</span>
                <span className="text-sm text-white font-medium truncate flex-1">{pendingFile.name}</span>
                <label className="text-xs text-blue-300 hover:text-white cursor-pointer transition flex-shrink-0">
                  <input type="file" accept=".pdf,.docx,.doc,.txt" onChange={handleFileSelect} className="hidden" />
                  Change
                </label>
              </div>

              {/* Email input */}
              <div className="mb-5">
                <input
                  type="email"
                  value={email}
                  onChange={e => { setEmail(e.target.value); setEmailError(''); }}
                  onKeyDown={e => e.key === 'Enter' && handleAnalyse()}
                  placeholder="you@example.com"
                  data-clarity-mask="True"
                  className="w-full px-4 py-3 rounded-xl border border-blue-400/30 bg-white/10 text-white text-sm placeholder-blue-400/60 focus:outline-none focus:ring-2 focus:ring-yellow-400"
                  autoFocus
                />
                {emailError && <p className="text-red-400 text-xs mt-1.5">{emailError}</p>}
                {pendingError && <p className="text-red-400 text-xs mt-1.5">{pendingError}</p>}
                <p className="text-blue-400/55 text-xs mt-2">We'll use this to save your report link and send useful MBA resume guidance. No spam.</p>
              </div>

              {/* Newsletter opt-in */}
              <label className="flex items-start gap-3 mb-5 cursor-pointer group">
                <input
                  type="checkbox"
                  checked={newsletterOptIn}
                  onChange={e => setNewsletterOptIn(e.target.checked)}
                  className="mt-0.5 w-4 h-4 rounded accent-yellow-400 flex-shrink-0 cursor-pointer"
                />
                <span className="text-xs text-blue-200 leading-relaxed group-hover:text-white transition">
                  Subscribe me to weekly insider tips on European MBAs, post-MBA jobs in Europe, and application strategy.
                </span>
              </label>

              {/* CTA */}
              <button
                onClick={handleAnalyse}
                disabled={!resumeText}
                className={`w-full py-3.5 rounded-xl font-bold text-sm transition shadow-lg ${resumeText ? 'bg-gradient-to-r from-yellow-500 to-yellow-400 text-blue-900 hover:from-yellow-400 hover:to-yellow-300' : 'bg-white/10 text-blue-400 cursor-wait'}`}>
                {resumeText ? 'Get my free preview →' : 'Reading file...'}
              </button>

              <p className="text-center text-blue-400/50 text-xs mt-4">Your resume stays private. <a href="/privacy.html" className="underline hover:text-blue-300">See how we handle your data</a>.</p>
            </div>
          )}
        </div>
      </div>

      {/* Below fold */}
      <div className="max-w-6xl mx-auto px-6 pb-16 space-y-8">

        {/* Deliverables strip */}
        <div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
          {[
            { icon: '✦', label: 'Bullet scoring', desc: 'Every bullet rated Strong, Good, Needs Work, or Rewrite. With the exact reason why.' },
            { icon: '✎', label: 'Rewrites included', desc: 'Weak bullets rewritten with a stronger verb, clearer scope, and sharper consequence.' },
            { icon: '◉', label: 'AdCom lens', desc: 'What a top European MBA admissions reader actually infers when they see your resume.' },
          ].map(({ icon, label, desc }) => (
            <div key={label} className="bg-white/6 border border-white/10 rounded-2xl px-5 py-4">
              <div className="text-yellow-400 text-base mb-2">{icon}</div>
              <div className="text-white font-semibold text-sm mb-1">{label}</div>
              <div className="text-blue-300 text-xs leading-relaxed">{desc}</div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );

  // ── ERROR ─────────────────────────────────────────────
  if (stage === 'error') return (
    <div className="min-h-screen bg-gradient-to-br from-blue-900 via-blue-800 to-blue-950 flex items-center justify-center p-4">
      <div className="max-w-lg w-full bg-white/10 backdrop-blur-lg rounded-3xl p-8 shadow-2xl border border-white/20">
        <div className="text-center">
          <div className="text-5xl mb-6">⚠️</div>
          <h2 className="text-2xl font-bold text-white mb-4">Analysis Failed</h2>
          <p className="text-blue-200 mb-6">{error}</p>
          {loadingMode === 'fullReport' && (
            <div className="bg-green-500/10 border border-green-400/30 rounded-lg p-4 mb-6 text-left">
              <p className="text-green-200 text-sm font-semibold mb-1">✓ Your payment went through. You have not been charged again.</p>
              <p className="text-green-100/80 text-sm leading-relaxed">
                We've emailed your payment confirmation{pendingPaymentOrderId.current ? ` (order ${pendingPaymentOrderId.current})` : ''}. Hit Try Again below, and if it still doesn't work, just reply to that email with your order reference and we'll sort it out.
              </p>
            </div>
          )}
          <div className="bg-white/10 rounded-lg p-4 mb-6 text-left">
            <p className="text-blue-100 text-sm font-semibold mb-3">💡 Things to try:</p>
            <div className="space-y-2 text-blue-200 text-sm">
              <div className="flex items-start gap-2">
                <span className="text-yellow-400">1.</span>
                <span><strong>Try again:</strong> Hit the button below — occasional server timeouts usually resolve on retry.</span>
              </div>
              <div className="flex items-start gap-2">
                <span className="text-yellow-400">2.</span>
                <span><strong>Check your file:</strong> Make sure it's a text-based PDF or DOCX, not a scanned image or designed template.</span>
              </div>
            </div>
          </div>
          {resumeText ? (
            <div className="flex flex-col items-center gap-3">
              <button onClick={() => {
                setError(null);
                const scope = loadingMode === 'fullReport' ? 'full' : 'preview';
                setStage('analyzing');
                setProgress(10);
                setStatusMessage('Reading your file...');
                analyzeResume(resumeText, scope);
              }} className="bg-gradient-to-r from-yellow-500 to-yellow-600 text-blue-900 px-8 py-3 rounded-full font-semibold hover:from-yellow-400 hover:to-yellow-500 transition-all shadow-lg">
                Try Again
              </button>
              <button onClick={() => { setStage('upload'); setError(null); }}
                className="text-blue-300 hover:text-white text-sm underline transition">
                Upload a different file
              </button>
            </div>
          ) : (
            <button onClick={() => { setStage('upload'); setError(null); }}
              className="bg-gradient-to-r from-yellow-500 to-yellow-600 text-blue-900 px-8 py-3 rounded-full font-semibold hover:from-yellow-400 hover:to-yellow-500 transition-all shadow-lg">
              Try Again
            </button>
          )}
        </div>
      </div>
    </div>
  );

  // ── TWO-COLUMN LAYOUT ─────────────────────────────────
  if (stage === 'two-column') return (
    <div className="min-h-screen bg-gradient-to-br from-blue-900 via-blue-800 to-blue-950 flex items-center justify-center p-4">
      <div className="max-w-lg w-full bg-white/10 backdrop-blur-lg rounded-3xl p-8 shadow-2xl border border-white/20">
        <div className="text-center mb-6">
          <div className="text-5xl mb-4">😅</div>
          <h2 className="text-2xl font-bold text-white mb-3">I can't read this template</h2>
          <p className="text-blue-200 leading-relaxed">Your resume appears to use a multi-column or designed template. Canva, Zety, Novoresume, and similar tools produce PDFs that scramble when I try to extract the text. I'd rather tell you now than give you broken feedback.</p>
        </div>

        <div className="bg-yellow-400/10 border border-yellow-400/30 rounded-xl p-4 mb-5">
          <p className="text-yellow-200 text-sm font-semibold mb-1">💡 MBA tip while you're here</p>
          <p className="text-yellow-100/80 text-sm leading-relaxed">For top European MBA programs, a clean single-column resume is strongly recommended. Designed templates with sidebars and colour blocks can actually hurt readability with admissions committees. Switching to a plain format is a win either way.</p>
        </div>

        <div className="bg-white/10 rounded-xl p-4 mb-5">
          <p className="text-blue-100 text-sm font-semibold mb-3">What you can do</p>
          <div className="space-y-3 text-sm text-blue-200">
            <div className="flex items-start gap-2">
              <span className="text-yellow-400 flex-shrink-0">1.</span>
              <span>Copy your work experience bullets into one of the clean templates below, export as PDF, and upload again. Takes about 5 minutes.</span>
            </div>
            <div className="flex items-start gap-2">
              <span className="text-yellow-400 flex-shrink-0">2.</span>
              <span>Or paste just your work experience bullets directly into a plain .txt file and upload that. I can still review the bullets.</span>
            </div>
          </div>
        </div>

        <div className="bg-white/10 rounded-xl p-4 mb-6">
          <p className="text-blue-100 text-sm font-semibold mb-3">Clean templates to use</p>
          <div className="space-y-2">
            {[
              { label: 'AccioAdmit Clean Template 1', url: 'https://docs.google.com/document/d/1dwgPbbHWEHoHXKu1UyZ2KBU-qYxai4VF/edit#heading=h.4akjfb5wijsf' },
              { label: 'AccioAdmit Clean Template 2', url: 'https://docs.google.com/document/d/1PMBvoxGZRgM0FM9Ny66T_JwcRw58EeML/edit#heading=h.gjdgxs' },
            ].map((t, i) => (
              <a key={i} href={t.url} target="_blank" rel="noopener noreferrer"
                className="flex items-center gap-2 text-sm text-yellow-300 hover:text-yellow-200 transition">
                <span>→</span><span className="hover:underline">{t.label}</span>
              </a>
            ))}
          </div>
        </div>

        <button onClick={() => setStage('upload')}
          className="w-full bg-gradient-to-r from-yellow-500 to-yellow-600 text-blue-900 py-3 rounded-full font-semibold hover:from-yellow-400 hover:to-yellow-500 transition-all shadow-lg">
          Upload a different file
        </button>
      </div>
    </div>
  );

  // ── ANALYZING ─────────────────────────────────────────
  if (stage === 'analyzing') {
    // ── PREVIEW loading (unchanged) ──
    if (loadingMode === 'preview') return (
      <div className="min-h-screen bg-gradient-to-br from-blue-900 via-blue-800 to-blue-950 flex items-center justify-center p-4">
        <div className="max-w-md w-full">
          <div className="text-center mb-8">
            <div className="flex items-center justify-center gap-2 mb-2">
              <span className="text-yellow-400 text-2xl">✨</span>
              <span className="text-xl font-bold text-white">Accio <span className="text-yellow-400">Admit</span></span>
            </div>
          </div>
          <div className="bg-white/10 backdrop-blur-lg rounded-3xl p-8 shadow-2xl border border-white/20">
            <div className="text-center mb-7">
              <div className="text-3xl mb-3">🔍</div>
              <h2 className="text-lg font-semibold text-white mb-1">Analyzing your resume</h2>
              <p className="text-blue-300 text-sm">This usually takes 20 to 40 seconds.</p>
            </div>
            <div className="mb-7">
              <div className="flex justify-between items-center mb-2">
                <span className="text-xs text-blue-300 font-medium">{statusMessage}</span>
                <span className="text-xs font-bold text-white tabular-nums">{progress}%</span>
              </div>
              <div className="w-full bg-white/15 rounded-full h-2.5 overflow-hidden">
                <div className="h-full rounded-full progress-bar" style={{ width: `${progress}%`, background: 'linear-gradient(90deg, #facc15, #fbbf24)' }} />
              </div>
              <div className="flex justify-between mt-2.5 px-0.5">
                {[15, 35, 58, 78, 92].map(p => (
                  <div key={p} className="w-1.5 h-1.5 rounded-full transition-all duration-500"
                    style={{ background: progress >= p ? '#facc15' : 'rgba(255,255,255,0.25)' }} />
                ))}
              </div>
            </div>
            <div className="bg-white/5 border border-white/10 rounded-2xl p-5 min-h-20 flex items-center">
              <div key={tipIndex} className="text-center w-full">
                <div className="text-xl mb-2">{TIPS[tipIndex].emoji}</div>
                <p className="text-blue-100 text-sm leading-relaxed">{TIPS[tipIndex].text}</p>
              </div>
            </div>
            <p className="text-center text-blue-500 text-xs mt-3">{tipIndex + 1} / {TIPS.length}</p>
            {slowNudge && (
              <div className="mt-4 bg-yellow-400/10 border border-yellow-400/25 rounded-xl px-4 py-3 text-center fade-slide-in">
                <p className="text-yellow-200 text-xs leading-relaxed">This is taking a bit longer than usual, likely because your resume is on the longer side. Hang tight, it's still working.</p>
              </div>
            )}
          </div>
        </div>
      </div>
    );

    // ── FULL REPORT loading (redesigned) ──
    const activeStep = progress < 25 ? 0 : progress < 48 ? 1 : progress < 68 ? 2 : progress < 85 ? 3 : 4;
    const isLastStep = activeStep === 4;

    const readyTime = (() => {
      const ready = new Date((analyzeStartTime.current || Date.now()) + 4 * 60 * 1000);
      const h = ready.getHours(), m = ready.getMinutes();
      const ampm = h >= 12 ? 'pm' : 'am';
      const h12 = h % 12 || 12;
      return `${h12}:${String(m).padStart(2, '0')} ${ampm}`;
    })();

    return (
      <div className="min-h-screen bg-gradient-to-br from-blue-900 via-blue-800 to-blue-950 flex items-center justify-center p-4">
        <div className="max-w-md w-full">

          {/* Logo */}
          <div className="text-center mb-6">
            <div className="flex items-center justify-center gap-2">
              <span className="text-yellow-400 text-2xl">✨</span>
              <span className="text-xl font-bold text-white">Accio <span className="text-yellow-400">Admit</span></span>
            </div>
          </div>

          <div className="bg-white/10 backdrop-blur-lg rounded-3xl p-8 shadow-2xl border border-white/20 space-y-6">

            {/* Header */}
            <div className="text-center">
              <div className="inline-flex items-center gap-2 bg-green-500/15 border border-green-400/25 rounded-full px-4 py-1.5 mb-4">
                <div className="w-2 h-2 rounded-full bg-green-400 flex-shrink-0" />
                <span className="text-green-300 text-xs font-semibold">Payment confirmed. Your report is being generated.</span>
              </div>
              <h2 className="text-lg font-semibold text-white mb-1">Building your full report</h2>
              <p className="text-blue-300 text-sm">Your full report usually takes 3 to 5 minutes.</p>
              <p className="text-blue-400 text-xs mt-1">Estimated ready time: <span className="font-semibold text-blue-300">{readyTime}</span></p>
            </div>

            {/* Step-based progress */}
            <div className="space-y-2">
              {FULL_REPORT_STEPS.map((label, i) => {
                const isDone   = i < activeStep;
                const isActive = i === activeStep;
                return (
                  <div key={i} className={`flex items-center gap-3 px-4 py-2.5 rounded-xl transition-all duration-500 ${isActive ? 'bg-yellow-400/10 border border-yellow-400/30' : isDone ? 'bg-white/5 border border-transparent' : 'opacity-25'}`}>
                    <div className="flex-shrink-0 w-5 h-5 flex items-center justify-center">
                      {isDone ? (
                        <div className="w-5 h-5 rounded-full bg-green-500 flex items-center justify-center">
                          <svg className="w-3 h-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}><path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7"/></svg>
                        </div>
                      ) : isActive ? (
                        <div className="w-5 h-5 rounded-full border-2 border-yellow-400 border-t-transparent btn-spin" />
                      ) : (
                        <div className="w-5 h-5 rounded-full border border-white/20" />
                      )}
                    </div>
                    <span className={`text-sm ${isDone ? 'text-green-300' : isActive ? 'text-yellow-200 font-medium' : 'text-blue-400'}`}>
                      {label}
                    </span>
                  </div>
                );
              })}
            </div>

            {/* Rotating sub-message on final step */}
            {isLastStep && (
              <div className="text-center -mt-2">
                <p key={statusMessage} className="text-blue-400 text-xs fade-slide-in">{statusMessage}…</p>
              </div>
            )}

            {/* Progress bar */}
            <div className="w-full bg-white/15 rounded-full h-1.5 overflow-hidden">
              <div className="h-full rounded-full progress-bar" style={{ width: `${Math.min(progress, 97)}%`, background: 'linear-gradient(90deg, #4ade80, #facc15)' }} />
            </div>

            {/* Tip card */}
            <div className="bg-white/5 border border-white/10 rounded-2xl p-5">
              <p className="text-blue-400 text-xs font-semibold uppercase tracking-wider mb-3">While we build your report</p>
              <div key={tipIndex} className="text-center">
                <div className="text-2xl mb-2">{TIPS[tipIndex].emoji}</div>
                <p className="text-blue-100 text-sm leading-relaxed">{TIPS[tipIndex].text}</p>
              </div>
              <p className="text-center text-blue-500 text-xs mt-3">MBA resume tip {tipIndex + 1} / {TIPS.length}</p>
            </div>

            {/* Bottom reassurance */}
            <div className="bg-blue-950/50 border border-white/10 rounded-2xl px-5 py-4 text-center">
              <p className="text-blue-200 text-sm leading-relaxed">Almost there. Keep this tab open and your report will load automatically when ready.</p>
            </div>

          </div>
        </div>
      </div>
    );
  }

  // ── RESULTS ───────────────────────────────────────────
  if (stage === 'results' && analysisData) {

  const isPreview = analysisData._scope === 'preview';

  // Count weak bullets for teaser copy
  const allBullets = (analysisData.workExperience || []).flatMap(r => r.bullets || []);
  const weakCount = allBullets.filter(b => ['improve','rewrite'].includes(normalizeStatus(b.status))).length;
  const totalBullets = allBullets.length;
  const solidBulletCount = allBullets.filter(b => ['strong','good'].includes(normalizeStatus(b.status))).length;

  // Count format issues for tab badge
  const formatChecklist = analysisData.format?.checklist || [];
  const formatIssueCount = formatChecklist.filter(i => i.status !== 'pass').length;
  const topFormatFixes = (analysisData.format?.topFixes || [])
    .filter(fix => !/\blinkedin\b/i.test(`${fix?.title || ''} ${fix?.whyItMatters || ''} ${fix?.action || ''}`));
  const firstWeakBullet = allBullets.find(b => ['improve','rewrite'].includes(normalizeStatus(b.status)));
  const bottomLineSummary = analysisData.bottomLine?.summary || '';
  const fallbackStrength = analysisData.bottomLine?.strengths?.[0] || '';
  const fallbackConcern = analysisData.bottomLine?.concerns?.[0] || '';
  const summaryCopyRaw = bottomLineSummary || fallbackConcern || fallbackStrength || 'Your resume has enough signal to be worth improving, but the strongest version will need sharper proof and cleaner prioritisation.';
  const candidateFirstName = analysisData.candidateName ? String(analysisData.candidateName).split(' ')[0] : null;
  const recentRole = analysisData.workExperience?.[0];
  const recentTitle = recentRole?.title || '';
  const recentCompany = recentRole?.company || '';
  const weakRatio = totalBullets > 0 ? weakCount / totalBullets : 0;
  const overallVerdict = weakRatio >= 0.6
    ? 'Needs sharper proof'
    : weakRatio >= 0.25 || formatIssueCount > 2
      ? 'Promising, needs tightening'
      : 'Strong starting point';
  const biggestOpportunity = weakCount > 0
    ? 'Turn responsibilities into outcomes. AdCom needs to see what changed because of your work, not only what you handled.'
    : formatIssueCount > 0
      ? 'Clean up presentation signals so the reader can trust the detail quickly.'
      : 'Use the full review to pressure-test the rest of the resume before submitting.';
  const quickStats = [
    { label: 'Bullets analyzed', value: totalBullets || '0', tone: 'blue' },
    { label: 'Missing outcomes', value: weakCount, tone: weakCount > 0 ? 'amber' : 'green' },
    { label: 'Format flags', value: formatIssueCount, tone: formatIssueCount > 0 ? 'amber' : 'green' },
    { label: 'Strong bullets', value: totalBullets ? `${solidBulletCount}/${totalBullets}` : 'N/A', tone: 'green' },
  ];
  const topIssues = [
    weakCount > 0 ? `${weakCount} bullet${weakCount === 1 ? '' : 's'} need clearer outcome, ownership, or scale.` : null,
    topFormatFixes[0]?.action || topFormatFixes[0]?.whyItMatters || null,
    firstWeakBullet?.issues?.[0] || null,
    fallbackConcern || null,
  ]
    .filter(Boolean)
    .filter((item, idx, arr) => arr.indexOf(item) === idx)
    .slice(0, 3);

  // Badge counts for tabs
  const workBadgeCount = weakCount > 0 ? weakCount : null;
  const eduSuggestions = analysisData.education?.suggestions || [];
  const eduBadgeCount = !isPreview && eduSuggestions.length > 0 ? eduSuggestions.length : null;
  const addInfoSuggestions = analysisData.additionalInfo?.suggestions || [];
  const addInfoBadgeCount = !isPreview && addInfoSuggestions.length > 0 ? addInfoSuggestions.length : null;
  const lensConcernCount = (() => {
    if (isPreview) return null;
    const roles = analysisData.workExperience || [];
    const allB = roles.flatMap(r => r.bullets || []);
    const totalB = allB.length;
    const strongRatio = totalB > 0 ? allB.filter(b => ['strong','good'].includes((b.status||'').toLowerCase())).length / totalB : 0;
    let c = 0;
    const MBA_RX = /\b(mba|international mba|pgdm|pgp|mim|msc management|pgpm)\b/i;
    if (MBA_RX.test(analysisData.education?.content || '')) c++;
    if (strongRatio < 0.3) c++;
    const qRx = /\b\d[\d,.]*\s*(%|million|billion|k\b|USD|EUR|INR|GBP|revenue|growth)/i;
    if (allB.filter(b => qRx.test(b.original||'')).length < 2) c++;
    const lRx = /\b(led|managed|supervised|mentored|directed|oversaw|head of|vp|founder|co-founder)\b/i;
    if (allB.filter(b => lRx.test(b.original||'')).length < 2) c++;
    return c > 0 ? c : null;
  })();

  // Tab definitions
  const TABS = [
    { id: 'work',      label: 'Work Experience',    locked: false,      badge: workBadgeCount },
    { id: 'format',    label: 'Format & Structure', locked: false,      badge: formatIssueCount > 0 ? formatIssueCount : null },
    { id: 'education', label: 'Education',          locked: isPreview,  badge: eduBadgeCount },
    { id: 'addinfo',   label: 'Additional Info',    locked: isPreview,  badge: addInfoBadgeCount },
    { id: 'lens',      label: 'MBA Lens',           locked: isPreview,  badge: lensConcernCount },
  ];

  const resetResults = () => {
    setStage('upload');
    setAnalysisData(null);
    setActiveTab('work');
  };

  const ConfirmResetModal = () => (
    <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/40">
      <div className="bg-white rounded-2xl shadow-2xl max-w-sm w-full px-6 py-6">
        <h2 className="text-lg font-bold text-gray-900 mb-2">Start over?</h2>
        <p className="text-sm text-gray-500 leading-relaxed mb-6">This will clear your current resume report and uploaded details from this page. You will need to upload a resume again.</p>
        <div className="flex gap-3 justify-end">
          <button
            onClick={() => setShowConfirmReset(false)}
            className="px-4 py-2 rounded-lg text-sm font-medium text-gray-600 hover:bg-gray-100 transition">
            Cancel
          </button>
          <button
            onClick={() => { setShowConfirmReset(false); resetResults(); }}
            className="px-4 py-2 rounded-lg text-sm font-bold bg-blue-600 text-white hover:bg-blue-700 transition">
            Yes, start over
          </button>
        </div>
      </div>
    </div>
  );

  // ── PAYMENT MODAL ──
  // ── PAYWALL ICON HELPER ──
  // Inline lucide-style SVG icons — no build step needed
  // Strip internal AI labels and bullet-number references from feedback sentences
  const sanitizeBulletRefs = (text) => {
    if (!text) return '';
    return text
      .replace(/^(LEAD|KEEP|EXPAND|CUT|STRONG|WEAK)\s*:\s*/i, '')
      .replace(/\bItem.by.item assessment\s*:\s*/gi, '')
      .replace(/\bBullets?\s+\d+(?:[,\s]+(?:and\s+)?\d+)*(?:\s*(?:to|through|[-])\s*\d+)?\b/gi, '')
      .replace(/\s*—\s*/g, ', ')
      .replace(/\s+/g, ' ')
      .trim();
  };

  const Pi = ({ id, cls }) => {
    const p = {
      fileSearch:     <><path d="M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v3"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><circle cx="5.5" cy="17.5" r="2.5"/><path d="M7.25 19.25 9 21"/></>,
      pencilLine:     <><path d="M12 20h9"/><path d="M16.376 3.622a1 1 0 0 1 3.002 3.002L7.368 18.635a2 2 0 0 1-.855.506l-2.872.838a.5.5 0 0 1-.62-.62l.838-2.872a2 2 0 0 1 .506-.854z"/></>,
      eye:            <><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"/><circle cx="12" cy="12" r="3"/></>,
      graduationCap:  <><path d="M22 10v6M2 10l10-5 10 5-10 5z"/><path d="M6 12v5c3 3 9 3 12 0v-5"/></>,
      sparkles:       <><path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/><path d="M20 3v4M22 5h-4M4 17v2M5 18H3"/></>,
      compass:        <><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></>,
      layoutTemplate: <><rect width="18" height="7" x="3" y="3" rx="1"/><rect width="9" height="7" x="3" y="14" rx="1"/><rect width="5" height="7" x="16" y="14" rx="1"/></>,
      trendingUp:     <><polyline points="22 7 13.5 15.5 8.5 10.5 2 17"/><polyline points="16 7 22 7 22 13"/></>,
      glasses:        <><circle cx="6" cy="15" r="4"/><circle cx="18" cy="15" r="4"/><path d="M14 15a2 2 0 0 0-2-2 2 2 0 0 0-2 2"/><path d="M2.5 13 5 7c.7-1.3 1.4-2 3-2"/><path d="M21.5 13 19 7c-.7-1.3-1.4-2-3-2"/></>,
      calendarDays:   <><rect width="18" height="18" x="3" y="4" rx="2"/><line x1="16" x2="16" y1="2" y2="6"/><line x1="8" x2="8" y1="2" y2="6"/><line x1="3" x2="21" y1="10" y2="10"/><path d="M8 14h.01M12 14h.01M16 14h.01M8 18h.01M12 18h.01M16 18h.01"/></>,
      listTree:       <><path d="M21 12h-8"/><path d="M21 6H8"/><path d="M21 18h-8"/><path d="M3 6v4c0 1.1.9 2 2 2h3"/><path d="M3 10v6c0 1.1.9 2 2 2h3"/></>,
      listChecks:     <><path d="m3 17 2 2 4-4"/><path d="m3 7 2 2 4-4"/><path d="M13 6h8M13 12h8M13 18h8"/></>,
      userCircle:     <><circle cx="12" cy="12" r="10"/><circle cx="12" cy="10" r="3"/><path d="M7 20.662V19a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v1.662"/></>,
      building2:      <><path d="M6 22V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v18Z"/><path d="M6 12H4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2"/><path d="M18 9h2a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-2"/><path d="M10 6h4M10 10h4M10 14h4M10 18h4"/></>,
      badgeCheck:     <><path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"/><path d="m9 12 2 2 4-4"/></>,
      circleHelp:     <><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></>,
      listFilter:     <><path d="M3 6h18M7 12h10M11 18h2"/></>,
      users:          <><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></>,
      globe2:         <><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></>,
      layers:         <><path d="m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z"/><path d="m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65"/><path d="m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65"/></>,
      usersRound:     <><path d="M18 21a8 8 0 0 0-16 0"/><circle cx="10" cy="8" r="5"/><path d="M22 20c0-3.37-2-6.5-4-8a5 5 0 0 0-.45-8.3"/></>,
      clock:          <><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></>,
      alertTriangle:  <><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4M12 17h.01"/></>,
      lock:           <><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></>,
      award:          <><circle cx="12" cy="8" r="6"/><path d="M15.477 12.89 17 22l-5-3-5 3 1.523-9.11"/></>,
      gitBranch:      <><line x1="6" x2="6" y1="3" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><circle cx="6" cy="6" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></>,
    };
    return (
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" className={cls || 'w-4 h-4'}>
        {p[id]}
      </svg>
    );
  };

  // ── Shared: AccioAdmit consulting CTA, appended to the end of every tab ──
  const StrategyCTA = () => (
    <div className="bg-gradient-to-br from-blue-700 to-blue-800 rounded-2xl px-6 py-6">
      <div className="text-[10px] font-semibold text-blue-300 uppercase tracking-widest mb-3">AccioAdmit</div>
      <p className="text-base font-bold text-white mb-2 leading-snug">Want help turning this into a full MBA application strategy?</p>
      <p className="text-sm text-blue-200 leading-relaxed mb-5">
        Your resume is only the first read. Your essays, school choices, recommendations, and interview stories need to tell the same story clearly. AccioAdmit works with applicants targeting top European MBA programs to build a focused, authentic application strategy from school selection to final submission.
      </p>
      <a
        href="https://cal.com/accio-admit/30min"
        target="_blank"
        rel="noopener noreferrer"
        className="inline-block bg-yellow-400 text-blue-900 text-sm font-bold px-5 py-2.5 rounded-xl hover:bg-yellow-300 transition"
      >
        Book a free strategy call
      </a>
      <p className="text-xs text-blue-300 mt-4 leading-relaxed">
        For applicants targeting European MBA programs including INSEAD, LBS, Oxford Said, HEC Paris, IESE, IE Business School, ESADE, and Cambridge Judge.
      </p>
    </div>
  );

  // ── TAB: WORK EXPERIENCE ──
  const WorkTab = () => {
    if (analysisData._warning && /bullet count mismatch/i.test(analysisData._warning)) {
      console.warn('[WorkTab]', analysisData._warning);
    }
    return (
    <div className="space-y-6">
      {analysisData._warning && !/bullet count mismatch/i.test(analysisData._warning) && (
        <div className="bg-yellow-50 border border-yellow-300 rounded-xl p-4 flex items-start gap-3">
          <span className="text-yellow-500">⚠</span>
          <p className="text-sm text-yellow-800">{analysisData._warning}</p>
        </div>
      )}


      {(isPreview ? (analysisData.workExperience || []).slice(0, 1) : (analysisData.workExperience || [])).map((job, jobIdx) => (
        <div key={jobIdx} className="mb-6 last:mb-0">
          <div className="bg-gray-50 rounded-lg p-4 mb-3">
            <div className="font-bold text-gray-800">{job.company}</div>
            <div className="text-gray-600">{job.title}</div>
            <div className="text-sm text-gray-500">{job.dates}</div>
          </div>
          <div className="space-y-4 pl-4">
            {(isPreview ? (job.bullets || []).slice(0, 3) : (job.bullets || [])).map((bullet, bulletIdx) => {
              const s = normalizeStatus(bullet.status);
              const isStrong = s === 'strong', isGood = s === 'good', isImprove = s === 'improve';
              const statusIcon = isStrong || isGood ? '✓' : isImprove ? '⚠' : '✕';
              const statusColor = isStrong ? 'text-green-700' : isGood ? 'text-green-600' : isImprove ? 'text-amber-500' : 'text-red-500';
              const statusBg = isStrong || isGood ? 'bg-green-50 border-green-200' : isImprove ? 'bg-amber-50 border-amber-200' : 'bg-red-50 border-red-200';
              const statusLabel = isStrong ? 'Strong' : isGood ? 'Good' : isImprove ? 'Needs work' : 'Rewrite';
              const hasMetrics = !!bullet.rewriteWithMetrics;
              const hasWithout = !!bullet.rewriteWithoutMetrics;
              const hasSingleRewrite = !!(bullet.rewrite && !hasMetrics && !hasWithout);
              const hasAnyRewrite = hasMetrics || hasWithout || hasSingleRewrite;
              const hasSlots = hasRewriteSlots(bullet);
              const copyKey = `${jobIdx}-${bulletIdx}`;

              return (
                <div key={bulletIdx} className="bg-white border border-gray-200 rounded-xl overflow-hidden shadow-sm">
                  <div className={`flex items-start gap-2 px-4 py-2.5 border-b ${statusBg}`}>
                    <span className={`text-sm font-bold mt-0.5 flex-shrink-0 ${statusColor}`}>{statusIcon}</span>
                    <span className={`text-xs font-semibold uppercase tracking-wide mt-0.5 flex-shrink-0 ${statusColor}`}>{statusLabel}</span>
                    <span className="text-xs text-gray-500 ml-1 leading-relaxed">{bullet.feedback}</span>
                  </div>
                  <div className={`grid ${hasAnyRewrite ? 'grid-cols-1 sm:grid-cols-2 divide-y sm:divide-y-0 sm:divide-x divide-gray-100' : 'grid-cols-1'}`}>
                    <div className="p-4">
                      <div className="text-xs font-semibold text-gray-400 uppercase tracking-wide mb-2">Your version</div>
                      <p className="text-sm text-gray-700 leading-relaxed mb-3" data-clarity-mask="True">"{bullet.original}"</p>
                      {bullet.issues?.length > 0 && (
                        <div className="flex flex-col gap-1.5 mb-3">
                          {bullet.issues.map((issue, i) => (
                            <div key={i} className="flex items-center gap-1.5 text-xs text-red-700 bg-red-50 border border-red-100 rounded-md px-2.5 py-1 w-fit max-w-full">
                              <span className="text-red-500 font-bold flex-shrink-0">✕</span>
                              <span>{issue}</span>
                            </div>
                          ))}
                        </div>
                      )}
                      {bullet.mbaLens && (
                        <div className="mt-2 pt-3 border-t border-gray-100">
                          <div className="text-xs text-blue-500 font-medium mb-1">AdCom reads this as:</div>
                          <div className="text-xs text-gray-500 italic">{bullet.mbaLens}</div>
                        </div>
                      )}
                    </div>
                    {hasAnyRewrite && (
                      <div className="p-4 bg-gray-50">
                        <div className="text-xs font-semibold text-gray-400 uppercase tracking-wide mb-3">Suggested rewrites</div>
                        {hasMetrics && (
                          <div className="mb-4">
                            <span className="inline-block text-xs font-semibold bg-blue-100 text-blue-700 px-2 py-0.5 rounded-full mb-2">With metrics</span>
                            <p className="text-sm text-gray-800 leading-relaxed mb-1.5">"{renderRewriteText(bullet.rewriteWithMetrics)}"</p>
                            <button onClick={() => copyText(bullet.rewriteWithMetrics, `${copyKey}-m`)}
                              className={`flex items-center gap-1 text-xs font-medium transition-colors ${copiedId === `${copyKey}-m` ? 'text-green-600' : 'text-blue-600 hover:text-blue-800'}`}>
                              📋 {copiedId === `${copyKey}-m` ? '✓ Copied!' : 'Copy'}
                            </button>
                          </div>
                        )}
                        {hasWithout && (
                          <div className={hasMetrics ? 'pt-3 border-t border-gray-200' : ''}>
                            <span className="inline-block text-xs font-semibold bg-gray-200 text-gray-600 px-2 py-0.5 rounded-full mb-2">Without metrics</span>
                            <p className="text-sm text-gray-800 leading-relaxed mb-1.5">"{renderRewriteText(bullet.rewriteWithoutMetrics)}"</p>
                            <button onClick={() => copyText(bullet.rewriteWithoutMetrics, `${copyKey}-w`)}
                              className={`flex items-center gap-1 text-xs font-medium transition-colors ${copiedId === `${copyKey}-w` ? 'text-green-600' : 'text-blue-600 hover:text-blue-800'}`}>
                              📋 {copiedId === `${copyKey}-w` ? '✓ Copied!' : 'Copy'}
                            </button>
                          </div>
                        )}
                        {hasSingleRewrite && (
                          <div>
                            <p className="text-sm text-gray-800 leading-relaxed mb-1.5">"{renderRewriteText(bullet.rewrite)}"</p>
                            <button onClick={() => copyText(bullet.rewrite, copyKey)}
                              className={`flex items-center gap-1 text-xs font-medium transition-colors ${copiedId === copyKey ? 'text-green-600' : 'text-blue-600 hover:text-blue-800'}`}>
                              📋 {copiedId === copyKey ? '✓ Copied!' : 'Copy'}
                            </button>
                          </div>
                        )}
                        {hasSlots && (
                          <p className="text-[11px] text-gray-400 mt-3 pt-2 border-t border-gray-200 leading-relaxed">
                            We never invent your numbers. Slots show where yours go.
                          </p>
                        )}
                      </div>
                    )}
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      ))}

      {/* Paywall break — preview mode only */}
      {isPreview && (
        <div className="rounded-2xl border border-gray-200 overflow-hidden shadow-sm">

          {/* Blurred continuation teaser */}
          <div className="relative select-none pointer-events-none">
            <div className="locked-blur px-5 py-5 space-y-3 bg-white">
              <div className="h-3 bg-gray-200 rounded w-3/4" />
              <div className="h-3 bg-gray-100 rounded w-full" />
              <div className="h-3 bg-gray-100 rounded w-5/6" />
              <div className="h-3 bg-gray-200 rounded w-2/3 mt-1" />
              <div className="h-3 bg-gray-100 rounded w-full" />
              <div className="h-3 bg-gray-100 rounded w-4/5" />
            </div>
            <div className="absolute inset-0 bg-gradient-to-b from-transparent via-white/70 to-white flex items-center justify-center">
              <div className="text-center">
                <div className="flex justify-center mb-1.5">
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" className="w-4 h-4 text-gray-400">
                    <rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>
                  </svg>
                </div>
                <p className="text-xs font-semibold text-gray-500">Full report continues here</p>
                <p className="text-[11px] text-gray-400 mt-0.5">Unlock to review every remaining role and bullet.</p>
              </div>
            </div>
          </div>

          {/* Main upsell body */}
          <div className="bg-white px-6 pt-6 pb-10">

            {/* 1. Label + Headline + Body */}
            <div className="text-center mb-8">
              <div className="inline-flex items-center gap-1.5 bg-amber-50 border border-amber-200 text-amber-700 text-[10px] font-bold px-3 py-1 rounded-full mb-5 uppercase tracking-widest">
                Preview only — one role shown
              </div>
              <h3 className="text-2xl font-bold text-gray-900 leading-tight mb-4 max-w-xl mx-auto">
                You've only seen one role. The bigger risk is the pattern across the rest of your resume.
              </h3>
              <p className="text-sm text-gray-600 leading-relaxed max-w-lg mx-auto mb-3">
                Resume writing has one major blind spot: you know what you meant, but AdCom only sees what is written.
              </p>
              <p className="text-sm text-gray-600 leading-relaxed max-w-lg mx-auto">
                In this preview, we've already shown how one role can read as vague, generic, or unclear even when the experience itself is strong. The full report shows whether this is happening across every role, bullet, and section before you submit.
              </p>
            </div>

            {/* 2. What you unlock */}
            <div className="mb-9">
              <p className="text-[11px] font-bold text-gray-400 uppercase tracking-widest text-center mb-5">What you unlock</p>
              <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
                {[
                  { iconId: 'fileSearch',     title: 'Every bullet, every role',        body: 'See where your experience sounds strong, vague, inflated, under-owned, or unclear.',                                       featured: true },
                  { iconId: 'pencilLine',     title: 'Rewrites where needed',           body: 'Get stronger bullet versions with clearer ownership, sharper verbs, and better impact.',                                   featured: false },
                  { iconId: 'eye',            title: 'AdCom interpretation',            body: 'Understand what an MBA admissions reader is actually inferring from your resume.',                                          featured: false },
                  { iconId: 'graduationCap',  title: 'Education section decoded',       body: 'See how your academics are being read and what questions they may raise.',                                                  featured: false },
                  { iconId: 'sparkles',       title: 'Additional Info review',          body: 'Find what is missing beyond work experience — languages, certifications, leadership, and differentiators.',                 featured: false },
                  { iconId: 'compass',        title: 'MBA Lens on your story',          body: 'See whether your career arc supports an MBA application or creates doubts.',                                                featured: false },
                  { iconId: 'layoutTemplate', title: 'Format and structure',            body: 'Catch the issues that slow AdCom down before they even absorb your achievements.',                                          featured: false },
                  { iconId: 'gitBranch',      title: 'Pattern across the full resume',  body: 'Spot repeated weak phrasing, unclear progression, missing metrics, and scattered positioning.',                            featured: false },
                ].map((item, i) => (
                  <div key={i} className={`flex items-start gap-3 rounded-xl px-4 py-4 border ${
                    item.featured ? 'bg-blue-50 border-blue-200' : 'bg-gray-50 border-gray-100'
                  }`}>
                    <div className={`flex-shrink-0 w-8 h-8 rounded-lg flex items-center justify-center ${item.featured ? 'bg-blue-100' : 'bg-gray-200/60'}`}>
                      <Pi id={item.iconId} cls={`w-4 h-4 ${item.featured ? 'text-blue-600' : 'text-gray-500'}`} />
                    </div>
                    <div>
                      <p className={`text-sm font-semibold leading-snug mb-0.5 ${item.featured ? 'text-blue-800' : 'text-gray-800'}`}>{item.title}</p>
                      <p className={`text-xs leading-relaxed ${item.featured ? 'text-blue-600' : 'text-gray-500'}`}>{item.body}</p>
                    </div>
                  </div>
                ))}
              </div>
            </div>

            {/* 3. Trust strip */}
            <div className="rounded-xl bg-gray-50 border border-gray-100 px-5 py-4 mb-8">
              <div className="flex flex-col sm:flex-row sm:items-center sm:justify-center sm:gap-8 gap-3">
                {[
                  { iconId: 'award',    text: 'Built by Oxford Saïd and IE Business School MBA alumni' },
                  { iconId: 'building2', text: 'Used by applicants targeting INSEAD, LBS, Oxford, HEC Paris, IESE, Cambridge Judge, IE, and ESADE' },
                  { iconId: 'lock',     text: 'One-time payment. Instant unlock.' },
                ].map((item, i) => (
                  <div key={i} className="flex items-start gap-2 sm:items-center">
                    <Pi id={item.iconId} cls="w-4 h-4 text-gray-400 flex-shrink-0 mt-0.5 sm:mt-0" />
                    <p className="text-xs text-gray-500 leading-snug">{item.text}</p>
                  </div>
                ))}
              </div>
            </div>

            {/* 4. CTA block */}
            <div className="max-w-md mx-auto text-center">
              <p className="text-base font-bold text-gray-900 mb-2">Unlock your full MBA resume report</p>
              <p className="text-sm text-gray-500 leading-relaxed mb-5 max-w-sm mx-auto">
                Get the complete section-by-section review, bullet-level feedback, AdCom interpretation, and rewrites where your resume needs them most.
              </p>
              <button
                onClick={() => setShowPayment(true)}
                className="w-full bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-500 hover:to-blue-600 text-white font-bold text-base px-6 py-4 rounded-xl transition-all shadow-md hover:shadow-lg mb-3">
                Get the full report — <PriceTag />
              </button>
              {PRICING.isLaunchOffer && (
                <p className="text-xs text-amber-600 font-medium mb-1">This price is available while we're in beta, through Oct 15.</p>
              )}
              <p className="text-xs text-gray-400 leading-relaxed">One-time payment. Instant unlock. See your full resume through an AdCom lens before you submit.</p>
            </div>

          </div>
        </div>
      )}
    </div>
  );
  };

  // ── TAB: FORMAT ──
  const FormatTab = () => {
    const { format } = analysisData;
    const isPreview = analysisData._scope === 'preview';
    if (!format) return <div className="text-sm text-gray-500 py-8 text-center">No format data available.</div>;

    const [openAccordions, setOpenAccordions] = React.useState({});
    const toggleAccordion = (key) => setOpenAccordions(prev => ({ ...prev, [key]: !prev[key] }));

    const isMinorContactFix = (fix) => {
      const text = `${fix?.title || ''} ${fix?.whyItMatters || ''} ${fix?.action || ''}`;
      return /\blinkedin\b/i.test(text);
    };
    const fixes = Array.isArray(format.topFixes)
      ? format.topFixes.filter(fix => !isMinorContactFix(fix))
      : [];
    const checklist = Array.isArray(format.checklist) ? format.checklist : [];

    const checklistMap = {};
    for (const item of checklist) { checklistMap[item.label] = item; }

    // ── One-page length estimate ──
    // We removed guessing the ORIGINAL resume's page count (unobservable from
    // extracted text). This is different: it's a character count of content
    // we generated ourselves (rewrites, or originals where no rewrite exists),
    // so it's actually knowable. ~3,800 characters is a rough approximation
    // of what fits one page at a standard 10-11pt resume font with normal
    // margins — an approximation, not a substitute for opening the doc, but
    // useful as a directional flag when rewrites have pushed the content long.
    const ONE_PAGE_CHAR_BUDGET = 3800;
    let projectedLength = 0;
    const bulletLengths = [];
    if (!isPreview) {
      (analysisData.workExperience || []).forEach((role, roleIdx) => {
        projectedLength += (role.company || '').length + (role.title || '').length + (role.dates || '').length;
        (role.bullets || []).forEach((bullet, bulletIdx) => {
          const text = bullet.rewrite || bullet.original || '';
          projectedLength += text.length;
          bulletLengths.push({ roleIdx, bulletIdx, roleCompany: role.company || 'this role', text });
        });
      });
      projectedLength += (analysisData.education?.content || '').length;
      projectedLength += (analysisData.additionalInfo?.content || '').length;
    }
    const isOverOnePage = !isPreview && projectedLength > ONE_PAGE_CHAR_BUDGET;
    // Prioritize trimming older roles first (higher roleIdx, since resumes run
    // reverse-chronological), then the longest bullets within those roles —
    // cutting length from early-career work costs less signal than cutting
    // from the current role.
    const trimCandidates = isOverOnePage
      ? [...bulletLengths].sort((a, b) => (b.roleIdx - a.roleIdx) || (b.text.length - a.text.length)).slice(0, 4)
      : [];
    // Grouped by role so the same role name isn't repeated once per bullet —
    // a role with 3 flagged bullets gets one heading, not 3 identical labels.
    const trimGroups = [];
    for (const c of trimCandidates) {
      let group = trimGroups.find(g => g.roleCompany === c.roleCompany);
      if (!group) { group = { roleCompany: c.roleCompany, items: [] }; trimGroups.push(group); }
      group.items.push(c);
    }

    // Score computed from readability items only
    const READABILITY_LABELS = isPreview
      ? ['Section order', 'Verb tense', 'Grammar and typos']
      : ['Font and readability', 'Bullet formatting', 'Bullet density', 'Consistency', 'Margins and spacing'];
    const STRUCTURE_LABELS = isPreview
      ? ['No objective statement']
      : ['Layout', 'Section order', 'Section headers', 'Dates and locations', 'Work experience structure'];
    const DETAIL_LABELS = isPreview
      ? ['LinkedIn']
      : ['Name and contact details', 'Education format', 'Additional Info format'];

    const readabilityItems = READABILITY_LABELS.map(l => checklistMap[l]).filter(Boolean);
    const readabilityPass = readabilityItems.filter(i => i.status === 'pass').length;
    const score = readabilityItems.length > 0 ? Math.round((readabilityPass / readabilityItems.length) * 100) : 0;
    const scoreColor = score >= 80 ? '#22c55e' : score >= 55 ? '#f59e0b' : '#ef4444';
    const scoreLabel = score >= 80 ? 'Strong' : score >= 55 ? 'Decent' : 'Needs work';
    const scoreContextLine = score >= 80
      ? 'Easy to scan. Format supports a fast AdCom read.'
      : score >= 55
      ? 'Readable, but some friction in the reading flow.'
      : 'Format issues slow the reader before content is absorbed.';

    const allCounts = checklist.reduce((acc, item) => {
      if (item.status === 'pass') acc.pass++;
      else if (item.status === 'missing') acc.missing++;
      else acc.needsWork++;
      return acc;
    }, { pass: 0, needsWork: 0, missing: 0 });

    const gaugeR = 54, gaugeCx = 70, gaugeCy = 66;
    const arcLen = Math.PI * gaugeR;
    const dashOffset = arcLen * (1 - score / 100);

    // Build AdCom format read paragraphs from summary + topFix why-it-matters
    const summaryText = (format.summary || '').trim();
    const issueWhys = fixes.slice(0, 4).map(f => f.whyItMatters).filter(Boolean);
    const para1 = [summaryText, issueWhys[0]].filter(Boolean).join(' ');
    const para2 = issueWhys.slice(1).join(' ').trim();
    const adcomParagraphs = [para1, para2].filter(s => s.trim().length > 0);

    // Priority chip for fix cards
    const getFixPriority = (fix) => {
      const text = `${fix.title || ''} ${fix.whyItMatters || ''}`.toLowerCase();
      if (/\b(critical|adcom|cannot|missing|incorrect|confus|hard to|wrong|fails|no |absent)\b/.test(text))
        return { label: 'High', cls: 'bg-amber-100 text-amber-700' };
      if (/\b(inconsistent|needs|weak|limited|unclear|reduce|improve|fix)\b/.test(text))
        return { label: 'Medium', cls: 'bg-blue-100 text-blue-700' };
      return { label: 'Low', cls: 'bg-gray-100 text-gray-500' };
    };

    const statusIcon = (status) => {
      if (status === 'pass') return (
        <div className="w-5 h-5 rounded-full bg-green-100 flex items-center justify-center flex-shrink-0 mt-0.5">
          <svg className="w-2.5 h-2.5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3.5} d="M5 13l4 4L19 7" />
          </svg>
        </div>
      );
      if (status === 'missing') return (
        <div className="w-5 h-5 rounded-full bg-red-100 flex items-center justify-center flex-shrink-0 mt-0.5">
          <svg className="w-2.5 h-2.5 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3.5} d="M6 18L18 6M6 6l12 12" />
          </svg>
        </div>
      );
      return (
        <div className="w-5 h-5 rounded-full bg-amber-100 flex items-center justify-center flex-shrink-0 mt-0.5">
          <svg className="w-2.5 h-2.5 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3.5} d="M12 9v4m0 4h.01" />
          </svg>
        </div>
      );
    };

    const noteColor = (status) => {
      if (status === 'missing') return 'text-red-500';
      if (status === 'needs_work') return 'text-amber-600';
      return 'text-gray-400';
    };

    const paidFormatCards = [
      { title: 'Visual Review', preview: 'Color use, spacing, page count, font size, margins, and whether the resume feels crowded when viewed as a document.' },
      { title: 'Education Format Review', preview: 'Whether university, degree, graduation year, scores, distinctions, and leadership details are cleanly presented.' },
      { title: 'Additional Info Format Review', preview: 'Whether languages, interests, certifications, test scores, and community work are crisp, useful, and scannable.' },
      { title: 'Overall Readability Feedback', preview: 'A full-document read on scanability, hierarchy, density, consistency, and whether AdCom can absorb it quickly.' },
    ];

    const accordionGroups = [
      { key: 'readability', label: 'Readability', labels: READABILITY_LABELS },
      { key: 'structure',   label: 'Structure',   labels: STRUCTURE_LABELS   },
      { key: 'details',     label: 'Sections & Details', labels: DETAIL_LABELS },
    ];

    return (
      <div className="space-y-5">

        {/* TOP: 2/3 AdCom read + 1/3 score — stacks on mobile */}
        <div className="grid grid-cols-1 lg:grid-cols-3 gap-4 items-start">

          {/* LEFT 2/3: AdCom format read */}
          <div className="lg:col-span-2 bg-white rounded-2xl border border-gray-100 shadow-sm px-6 py-5">
            <div className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest mb-3">
              How AdCom will experience your resume's format
            </div>
            {adcomParagraphs.length > 0 ? (
              <div className="space-y-3">
                {adcomParagraphs.map((p, i) => (
                  <p key={i} className="text-sm text-gray-700 leading-relaxed">{p}</p>
                ))}
              </div>
            ) : (
              <p className="text-sm text-gray-400 italic">No format summary available.</p>
            )}
          </div>

          {/* RIGHT 1/3: Readability score */}
          <div className="bg-white rounded-2xl border border-gray-100 shadow-sm px-5 py-5">
            <div className="flex items-center justify-between mb-2">
              <div className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest">Readability</div>
              <div className="flex items-center gap-1">
                {allCounts.pass > 0      && <span className="text-[10px] px-1.5 py-0.5 rounded-full bg-green-50 text-green-700 font-medium">{allCounts.pass}✓</span>}
                {allCounts.needsWork > 0 && <span className="text-[10px] px-1.5 py-0.5 rounded-full bg-amber-50 text-amber-700 font-medium">{allCounts.needsWork}!</span>}
                {allCounts.missing > 0   && <span className="text-[10px] px-1.5 py-0.5 rounded-full bg-red-50 text-red-700 font-medium">{allCounts.missing}✕</span>}
              </div>
            </div>
            <div className="flex flex-col items-center">
              <svg width="140" height="80" viewBox="0 0 140 80" aria-label={`Readability score: ${score}`}>
                <path d={`M ${gaugeCx - gaugeR} ${gaugeCy} A ${gaugeR} ${gaugeR} 0 0 1 ${gaugeCx + gaugeR} ${gaugeCy}`}
                  fill="none" stroke="#f3f4f6" strokeWidth="10" strokeLinecap="round" />
                <path d={`M ${gaugeCx - gaugeR} ${gaugeCy} A ${gaugeR} ${gaugeR} 0 0 1 ${gaugeCx + gaugeR} ${gaugeCy}`}
                  fill="none" stroke={scoreColor} strokeWidth="10" strokeLinecap="round"
                  strokeDasharray={`${arcLen} ${arcLen}`} strokeDashoffset={dashOffset} />
                <text x={gaugeCx} y={gaugeCy - 10} textAnchor="middle"
                  fill="#111827" fontSize="28" fontWeight="700" fontFamily="system-ui, -apple-system, sans-serif">{score}</text>
                <text x={gaugeCx} y={gaugeCy + 8} textAnchor="middle"
                  fill={scoreColor} fontSize="11" fontWeight="600" fontFamily="system-ui, -apple-system, sans-serif">{scoreLabel}</text>
              </svg>
              <p className="text-[10px] text-gray-400 text-center -mt-1 leading-relaxed px-1">{scoreContextLine}</p>
            </div>
          </div>
        </div>

        {/* LENGTH WARNING — rewrites pushed content past one page */}
        {isOverOnePage && (
          <div className="bg-amber-50 border border-amber-200 rounded-2xl px-5 py-5">
            <div className="text-[10px] font-semibold text-amber-700 uppercase tracking-widest mb-2">Heads up: this will likely run past one page</div>
            <p className="text-sm text-amber-900 leading-relaxed mb-3">
              If you apply the rewrites and section edits suggested in this report as-is, the resume will likely exceed one page. A resume that runs long defeats the purpose of tightening these bullets — it needs to come back down to one page before you submit. This is an estimate based on the length of the suggested content, not the original file, so treat it as directional.
            </p>
            {trimGroups.length > 0 && (
              <>
                <p className="text-xs font-semibold text-amber-800 mb-2">Start trimming here — the longest suggested bullets in your earliest roles:</p>
                <div className="space-y-3">
                  {trimGroups.map((g, gi) => (
                    <div key={gi}>
                      <p className="text-[11px] font-semibold text-amber-700 uppercase tracking-wide mb-1.5">{g.roleCompany}</p>
                      <ul className="space-y-1.5">
                        {g.items.map((c, i) => (
                          <li key={i} className="text-xs text-amber-800 bg-amber-100/60 rounded-lg px-3 py-2">
                            "{c.text.length > 140 ? c.text.slice(0, 140) + '…' : c.text}"
                          </li>
                        ))}
                      </ul>
                    </div>
                  ))}
                </div>
              </>
            )}
          </div>
        )}

        {/* FIX BEFORE SUBMITTING */}
        {fixes.length > 0 && (
          <div>
            <div className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest mb-3">Fix before submitting</div>
            <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
              {fixes.map((fix, i) => {
                const priority = getFixPriority(fix);
                return (
                  <div key={i} className="bg-white rounded-2xl border border-gray-100 shadow-sm px-5 py-4">
                    <div className="flex items-start justify-between gap-2 mb-2">
                      <div className="text-sm font-semibold text-gray-900 leading-snug">{fix.title}</div>
                      <span className={`flex-shrink-0 text-[10px] font-semibold px-2 py-0.5 rounded-full ${priority.cls}`}>{priority.label}</span>
                    </div>
                    {fix.whyItMatters && (
                      <p className="text-xs text-gray-500 leading-relaxed mb-2">{fix.whyItMatters}</p>
                    )}
                    {fix.action && (
                      <div className="bg-gray-50 rounded-lg px-3 py-2 mt-1">
                        <div className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest mb-0.5">What to do</div>
                        <p className="text-xs text-gray-700 leading-relaxed">{fix.action}</p>
                      </div>
                    )}
                  </div>
                );
              })}
            </div>
          </div>
        )}

        {/* DETAILED CHECKS — ACCORDIONS */}
        <div>
          <div className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest mb-3">Detailed checks</div>
          <div className="space-y-2">
            {accordionGroups.map((group) => {
              const items = group.labels.map(l => checklistMap[l]).filter(Boolean);
              if (items.length === 0) return null;
              const issues = items.filter(it => it.status !== 'pass' && !/^linkedin$/i.test(it.label)).length;
              const hasMissing   = items.some(it => it.status === 'missing' && !/^linkedin$/i.test(it.label));
              const hasNeedsWork = items.some(it => it.status === 'needs_work' && !/^linkedin$/i.test(it.label));
              const severityCls   = hasMissing ? 'bg-red-50 text-red-600' : hasNeedsWork ? 'bg-amber-50 text-amber-600' : 'bg-green-50 text-green-700';
              const severityLabel = hasMissing ? 'Fix needed' : hasNeedsWork ? 'Review' : 'All clear';
              const isOpen = !!openAccordions[group.key];
              return (
                <div key={group.key} className="bg-white rounded-xl border border-gray-100 shadow-sm overflow-hidden">
                  <button
                    onClick={() => toggleAccordion(group.key)}
                    className="w-full flex items-center justify-between px-5 py-4 text-left"
                    aria-expanded={isOpen}
                  >
                    <div className="flex items-center gap-3">
                      <span className="text-sm font-semibold text-gray-800">{group.label}</span>
                      {issues > 0 && (
                        <span className="text-xs text-gray-400">{issues} {issues === 1 ? 'issue' : 'issues'}</span>
                      )}
                    </div>
                    <div className="flex items-center gap-2 flex-shrink-0">
                      <span className={`text-[10px] font-semibold px-2 py-0.5 rounded-full ${severityCls}`}>{severityLabel}</span>
                      <svg
                        className="w-4 h-4 text-gray-400 flex-shrink-0"
                        style={{ transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform 0.2s' }}
                        fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}
                      >
                        <path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
                      </svg>
                    </div>
                  </button>
                  {isOpen && (
                    <div className="divide-y divide-gray-50 border-t border-gray-100">
                      {items.map((item, ii) => {
                        const isLinkedInItem = /^linkedin$/i.test(item.label);
                        return (
                          <div key={ii} className="flex items-start gap-3 px-5 py-3.5">
                            {isLinkedInItem
                              ? <div className="w-5 h-5 rounded-full bg-blue-50 flex items-center justify-center flex-shrink-0 mt-0.5">
                                  <svg className="w-2.5 h-2.5 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M13 16h-1v-4h-1m1-4h.01" />
                                  </svg>
                                </div>
                              : statusIcon(item.status)
                            }
                            <div className="flex-1 min-w-0">
                              <div className={`text-xs font-semibold ${isLinkedInItem ? 'text-gray-500' : item.status === 'pass' ? 'text-gray-500' : 'text-gray-800'}`}>
                                {isLinkedInItem ? 'LinkedIn not shown' : item.label}
                              </div>
                              <p className={`text-xs leading-relaxed mt-0.5 break-words ${isLinkedInItem ? 'text-blue-400' : noteColor(item.status)}`}>
                                {isLinkedInItem
                                  ? 'Optional, but helpful. Add your LinkedIn URL if it is polished and consistent with your resume.'
                                  : item.note}
                              </p>
                            </div>
                          </div>
                        );
                      })}
                    </div>
                  )}
                </div>
              );
            })}
          </div>
        </div>

        {/* LOCKED PAID FORMAT REVIEW (preview mode) */}
        {isPreview && (
          <div className="rounded-2xl border border-gray-200 overflow-hidden shadow-sm bg-white">

            {/* Label + Headline + Body */}
            <div className="px-6 pt-7 pb-6 text-center border-b border-gray-100">
              <div className="inline-flex items-center gap-1.5 bg-amber-50 border border-amber-200 text-amber-700 text-[10px] font-bold px-3 py-1 rounded-full mb-5 uppercase tracking-widest">
                Format preview — deeper checks locked
              </div>
              <h3 className="text-xl font-bold text-gray-900 leading-tight mb-4 max-w-lg mx-auto">
                Your score shows the surface. The full review shows what is slowing the reader down.
              </h3>
              <p className="text-sm text-gray-600 leading-relaxed max-w-lg mx-auto">
                Format is not cosmetic in an MBA resume. It controls how quickly AdCom understands your roles, dates, progression, and impact. The paid format review shows the exact structure issues that may be making strong experience harder to read.
              </p>
            </div>

            {/* Value cards */}
            <div className="px-6 py-6">
              <p className="text-[11px] font-bold text-gray-400 uppercase tracking-widest text-center mb-5">What you unlock</p>
              <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
                {[
                  { iconId: 'glasses',        title: 'Readability friction',          body: 'See where dense bullets, spacing, or formatting slow the reader down.' },
                  { iconId: 'calendarDays',   title: 'Date and timeline clarity',      body: 'Catch inconsistent dates, missing months, unclear overlaps, or confusing chronology.' },
                  { iconId: 'listTree',       title: 'Section order and hierarchy',    body: 'See whether your resume leads with the right information in the right order.' },
                  { iconId: 'listChecks',     title: 'Bullet density and scanability', body: 'Understand where bullets feel crowded, repetitive, or hard to absorb quickly.' },
                  { iconId: 'userCircle',     title: 'Contact and profile details',    body: 'Check whether key details like LinkedIn, location, email, and headers are clean.' },
                  { iconId: 'eye',            title: 'AdCom format interpretation',   body: 'Understand how the resume feels before the reader even judges your achievements.' },
                ].map((card, i) => (
                  <div key={i} className="flex items-start gap-3 bg-gray-50 border border-gray-100 rounded-xl px-4 py-4">
                    <div className="flex-shrink-0 w-8 h-8 rounded-lg bg-gray-200/60 flex items-center justify-center">
                      <Pi id={card.iconId} cls="w-4 h-4 text-gray-500" />
                    </div>
                    <div className="flex-1 min-w-0">
                      <div className="flex items-center justify-between gap-2 mb-0.5">
                        <p className="text-sm font-semibold text-gray-800 leading-snug">{card.title}</p>
                        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-3.5 h-3.5 text-gray-300 flex-shrink-0">
                          <rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
                          <path d="M7 11V7a5 5 0 0 1 10 0v4"/>
                        </svg>
                      </div>
                      <p className="text-xs text-gray-500 leading-relaxed">{card.body}</p>
                    </div>
                  </div>
                ))}
              </div>
            </div>

            {/* CTA block */}
            <div className="px-6 pb-8 text-center">
              <p className="text-base font-bold text-gray-900 mb-2">Unlock your full MBA resume report</p>
              <p className="text-sm text-gray-500 leading-relaxed mb-5 max-w-sm mx-auto">
                Get the full format review plus every role, every bullet, education section, additional info, and MBA lens.
              </p>
              <div className="max-w-sm mx-auto">
                <button
                  onClick={() => setShowPayment(true)}
                  className="w-full bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-500 hover:to-blue-600 text-white font-bold text-base px-6 py-4 rounded-xl transition-all shadow-md hover:shadow-lg mb-3">
                  Unlock the full report — <PriceTag />
                </button>
                {PRICING.isLaunchOffer && (
                  <p className="text-xs text-amber-600 font-medium mb-1">This price is available while we're in beta, through Oct 15.</p>
                )}
                <p className="text-xs text-gray-400 leading-relaxed">One-time payment. Instant access. See your full resume through an AdCom lens before you submit.</p>
              </div>
            </div>

          </div>
        )}

      </div>
    );
  };

  // ── TAB: LOCKED — Education (dedicated premium upsell) ──
  const EducationLockedTab = () => (
    <div className="rounded-2xl border border-gray-200 overflow-hidden shadow-sm bg-white">

      {/* Label + Headline + Body */}
      <div className="px-6 pt-7 pb-6 text-center border-b border-gray-100">
        <div className="inline-flex items-center gap-1.5 bg-amber-50 border border-amber-200 text-amber-700 text-[10px] font-bold px-3 py-1 rounded-full mb-5 uppercase tracking-widest">
          Academic signal check
        </div>
        <h3 className="text-xl font-bold text-gray-900 leading-tight mb-4 max-w-lg mx-auto">
          Your education section is not just a list of schools. It shapes how AdCom reads your readiness.
        </h3>
        <p className="text-sm text-gray-600 leading-relaxed max-w-lg mx-auto">
          AdCom does not read education only for names and dates. They use it to judge academic readiness, rigor, credibility, and whether your path makes sense for an MBA now. The full report shows what your education section strengthens, what it leaves unclear, and what questions it may raise.
        </p>
      </div>

      {/* Value cards */}
      <div className="px-6 py-6">
        <p className="text-[11px] font-bold text-gray-400 uppercase tracking-widest text-center mb-5">What you'll unlock</p>
        <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
          {[
            { iconId: 'graduationCap', title: 'Degree-by-degree read',     body: 'See how each academic credential strengthens, weakens, or complicates your profile.' },
            { iconId: 'building2',     title: 'Institution interpretation', body: 'Understand how your schools and programs may land for European MBA readers.' },
            { iconId: 'badgeCheck',    title: 'Academic proof gaps',        body: 'Find out whether GPA, rank, honors, awards, thesis, coursework, or distinctions are missing.' },
            { iconId: 'circleHelp',    title: 'AdCom questions',            body: 'Catch timeline, rigor, or "why another MBA now?" concerns before you submit.' },
          ].map((card, i) => (
            <div key={i} className="flex items-start gap-3 bg-gray-50 border border-gray-100 rounded-xl px-4 py-4">
              <div className="flex-shrink-0 w-8 h-8 rounded-lg bg-gray-200/60 flex items-center justify-center">
                <Pi id={card.iconId} cls="w-4 h-4 text-gray-500" />
              </div>
              <div className="flex-1 min-w-0">
                <div className="flex items-center justify-between gap-2 mb-0.5">
                  <p className="text-sm font-semibold text-gray-800 leading-snug">{card.title}</p>
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-3.5 h-3.5 text-gray-300 flex-shrink-0">
                    <rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
                    <path d="M7 11V7a5 5 0 0 1 10 0v4"/>
                  </svg>
                </div>
                <p className="text-xs text-gray-500 leading-relaxed">{card.body}</p>
              </div>
            </div>
          ))}
        </div>

        {/* Closing value line */}
        <p className="text-sm text-gray-500 text-center italic mt-5 max-w-md mx-auto leading-relaxed">
          A weak education section is not always a red flag. But an underused one is a missed opportunity.
        </p>
      </div>

      {/* CTA block */}
      <div className="px-6 pb-8 text-center border-t border-gray-100 pt-6">
        <p className="text-base font-bold text-gray-900 mb-2">Unlock your full MBA resume report</p>
        <p className="text-sm text-gray-500 leading-relaxed mb-5 max-w-sm mx-auto">
          Get the full education read plus every role, every bullet, format review, additional info, and MBA lens.
        </p>
        <div className="max-w-sm mx-auto">
          <button
            onClick={() => setShowPayment(true)}
            className="w-full bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-500 hover:to-blue-600 text-white font-bold text-base px-6 py-4 rounded-xl transition-all shadow-md hover:shadow-lg mb-3">
            Unlock the full report — <PriceTag />
          </button>
          {PRICING.isLaunchOffer && (
            <p className="text-xs text-amber-600 font-medium mb-1">This price is available while we're in beta, through Oct 15.</p>
          )}
          <p className="text-xs text-gray-400 leading-relaxed">One-time payment. Instant access.</p>
        </div>
      </div>

    </div>
  );

  // ── TAB: LOCKED — Additional Info (dedicated premium upsell) ──
  const AdditionalInfoLockedTab = () => (
    <div className="rounded-2xl border border-gray-200 overflow-hidden shadow-sm bg-white">

      {/* Label + Headline + Body */}
      <div className="px-6 pt-7 pb-6 text-center border-b border-gray-100">
        <div className="inline-flex items-center gap-1.5 bg-amber-50 border border-amber-200 text-amber-700 text-[10px] font-bold px-3 py-1 rounded-full mb-5 uppercase tracking-widest">
          Quiet differentiator
        </div>
        <h3 className="text-xl font-bold text-gray-900 leading-tight mb-4 max-w-lg mx-auto">
          The section that makes your profile feel more complete or more one-dimensional
        </h3>
        <p className="text-sm text-gray-600 leading-relaxed max-w-lg mx-auto">
          AdCom does not read Additional Information as filler. They use it to understand whether your profile has depth beyond work experience, whether you bring international readiness, and whether there is leadership, curiosity, or substance that does not show up in your job titles alone.
        </p>
      </div>

      {/* Value cards */}
      <div className="px-6 py-6">
        <p className="text-[11px] font-bold text-gray-400 uppercase tracking-widest text-center mb-5">What you'll unlock</p>
        <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
          {[
            { iconId: 'listFilter',  title: 'Keep / Cut / Lead recommendations',      body: 'See which entries strengthen your profile, which feel like filler, and what should come first.' },
            { iconId: 'badgeCheck',  title: 'Certifications that actually add signal', body: 'Understand which credentials help and which ones do not meaningfully strengthen the profile.' },
            { iconId: 'users',       title: 'Leadership and depth beyond work',        body: 'See whether extracurriculars, mentoring, volunteering, or community work add credibility.' },
            { iconId: 'globe2',      title: 'Languages and international signal',      body: 'See whether language ability or global exposure should be surfaced more clearly for European MBA applications.' },
            { iconId: 'layers',      title: 'Best framing for this section',           body: 'Learn the strongest order, grouping, and positioning for Additional Information.' },
          ].map((card, i) => (
            <div key={i} className={`flex items-start gap-3 rounded-xl px-4 py-4 border ${i === 0 ? 'bg-blue-50 border-blue-200 sm:col-span-2' : 'bg-gray-50 border-gray-100'}`}>
              <div className={`flex-shrink-0 w-8 h-8 rounded-lg flex items-center justify-center ${i === 0 ? 'bg-blue-100' : 'bg-gray-200/60'}`}>
                <Pi id={card.iconId} cls={`w-4 h-4 ${i === 0 ? 'text-blue-600' : 'text-gray-500'}`} />
              </div>
              <div className="flex-1 min-w-0">
                <div className="flex items-center justify-between gap-2 mb-0.5">
                  <p className={`text-sm font-semibold leading-snug ${i === 0 ? 'text-blue-800' : 'text-gray-800'}`}>{card.title}</p>
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-3.5 h-3.5 text-gray-300 flex-shrink-0">
                    <rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
                    <path d="M7 11V7a5 5 0 0 1 10 0v4"/>
                  </svg>
                </div>
                <p className={`text-xs leading-relaxed ${i === 0 ? 'text-blue-600' : 'text-gray-500'}`}>{card.body}</p>
              </div>
            </div>
          ))}
        </div>

        {/* Closing value line */}
        <p className="text-sm text-gray-500 text-center italic mt-5 max-w-md mx-auto leading-relaxed">
          Don't let this section become filler. Use it to add depth, credibility, and differentiation.
        </p>
      </div>

      {/* CTA block */}
      <div className="px-6 pb-8 text-center border-t border-gray-100 pt-6">
        <p className="text-base font-bold text-gray-900 mb-2">Unlock your full MBA resume report</p>
        <p className="text-sm text-gray-500 leading-relaxed mb-5 max-w-sm mx-auto">
          Get the full Additional Info read plus every role, every bullet, education section, format review, and MBA lens.
        </p>
        <div className="max-w-sm mx-auto">
          <button
            onClick={() => setShowPayment(true)}
            className="w-full bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-500 hover:to-blue-600 text-white font-bold text-base px-6 py-4 rounded-xl transition-all shadow-md hover:shadow-lg mb-3">
            Unlock the full report — <PriceTag />
          </button>
          {PRICING.isLaunchOffer && (
            <p className="text-xs text-amber-600 font-medium mb-1">This price is available while we're in beta, through Oct 15.</p>
          )}
          <p className="text-xs text-gray-400 leading-relaxed">One-time payment. Instant access.</p>
        </div>
      </div>

    </div>
  );

  // ── TAB: LOCKED — MBA Lens (dedicated premium upsell) ──
  const LensLockedTab = () => (
    <div className="rounded-2xl border border-gray-200 overflow-hidden shadow-sm bg-white">

      {/* Label + Headline + Body */}
      <div className="px-6 pt-7 pb-6 text-center border-b border-gray-100">
        <div className="inline-flex items-center gap-1.5 bg-amber-50 border border-amber-200 text-amber-700 text-[10px] font-bold px-3 py-1 rounded-full mb-5 uppercase tracking-widest">
          Strategic candidacy read
        </div>
        <h3 className="text-xl font-bold text-gray-900 leading-tight mb-4 max-w-lg mx-auto">
          Your resume should not just show experience. It should explain why an MBA makes sense now.
        </h3>
        <p className="text-sm text-gray-600 leading-relaxed max-w-lg mx-auto">
          AdCom is not only checking what you did. They are asking whether your career arc, seniority, leadership, goals, and timing make a believable case for business school. The MBA Lens shows whether your resume is building that case, or quietly working against it.
        </p>
      </div>

      {/* Value cards */}
      <div className="px-6 py-6">
        <p className="text-[11px] font-bold text-gray-400 uppercase tracking-widest text-center mb-5">What you'll unlock</p>
        <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
          {[
            { iconId: 'trendingUp',     title: 'Career arc clarity',              body: 'See whether your roles show progression, direction, and readiness for an MBA.',                                    featured: true },
            { iconId: 'usersRound',     title: 'Seniority and leadership signal', body: 'Understand whether your resume makes your level of ownership and influence clear.',                                 featured: false },
            { iconId: 'clock',          title: 'Where AdCom will push back',      body: 'The specific soft flags in your profile, and how to frame each one before it becomes a question.',              featured: false },
            { iconId: 'alertTriangle',  title: 'Narrative risks',                 body: 'Spot repetition, scattered positioning, unclear transitions, or profile signals that may confuse AdCom.',           featured: false },
          ].map((card, i) => (
            <div key={i} className={`flex items-start gap-3 rounded-xl px-4 py-4 border ${card.featured ? 'bg-blue-50 border-blue-200' : 'bg-gray-50 border-gray-100'}`}>
              <div className={`flex-shrink-0 w-8 h-8 rounded-lg flex items-center justify-center ${card.featured ? 'bg-blue-100' : 'bg-gray-200/60'}`}>
                <Pi id={card.iconId} cls={`w-4 h-4 ${card.featured ? 'text-blue-600' : 'text-gray-500'}`} />
              </div>
              <div className="flex-1 min-w-0">
                <div className="flex items-center justify-between gap-2 mb-0.5">
                  <p className={`text-sm font-semibold leading-snug ${card.featured ? 'text-blue-800' : 'text-gray-800'}`}>{card.title}</p>
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-3.5 h-3.5 text-gray-300 flex-shrink-0">
                    <rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
                    <path d="M7 11V7a5 5 0 0 1 10 0v4"/>
                  </svg>
                </div>
                <p className={`text-xs leading-relaxed ${card.featured ? 'text-blue-600' : 'text-gray-500'}`}>{card.body}</p>
              </div>
            </div>
          ))}
        </div>

        {/* Closing value line */}
        <p className="text-sm text-gray-500 text-center italic mt-5 max-w-md mx-auto leading-relaxed">
          Unlock the strategic read that turns resume feedback into MBA positioning.
        </p>
      </div>

      {/* CTA block */}
      <div className="px-6 pb-8 text-center border-t border-gray-100 pt-6">
        <p className="text-base font-bold text-gray-900 mb-2">Unlock your full MBA resume report</p>
        <p className="text-sm text-gray-500 leading-relaxed mb-5 max-w-sm mx-auto">
          Get the MBA Lens plus every role, every bullet, education section, additional info, and format review.
        </p>
        <div className="max-w-sm mx-auto">
          <button
            onClick={() => setShowPayment(true)}
            className="w-full bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-500 hover:to-blue-600 text-white font-bold text-base px-6 py-4 rounded-xl transition-all shadow-md hover:shadow-lg mb-3">
            Unlock the full report — <PriceTag />
          </button>
          {PRICING.isLaunchOffer && (
            <p className="text-xs text-amber-600 font-medium mb-1">This price is available while we're in beta, through Oct 15.</p>
          )}
          <p className="text-xs text-gray-400 leading-relaxed">One-time payment. Instant access.</p>
        </div>
      </div>

    </div>
  );

  // ── TAB: LOCKED ──
  const LockedTab = ({ tabId }) => {
    const copy = {
      education: {
        badge: 'HIGH-STAKES FRAMING',
        title: 'How adcoms will read your Education Section',
        support: 'We assess whether your education strengthens your candidacy, raises questions, or needs better framing for top European MBA programs.',
        includedHeading: "WHAT YOU'LL UNLOCK",
        lines: [
          'How each degree strengthens, weakens, or complicates your profile',
          'Institution-by-institution interpretation for European MBA readers',
          'GPA, rigor, and prestige framing',
          'Whether your education supports your broader candidacy narrative',
        ],
        teaser: 'Your academic profile has at least one strong signal, but one part of this section may be under-explaining your readiness for a top MBA.',
        bridge: 'Unlock the full education read before this section gets reduced to just names on the page.',
        cta: 'Unlock full resume feedback - ₹2,499',
      },
      addinfo: {
        badge: 'COMMONLY OVERLOOKED',
        title: 'The section that quietly helps or hurts',
        support: 'We identify which entries add signal, which feel like filler, and how this section should be structured for maximum credibility.',
        includedHeading: "WHAT YOU'LL UNLOCK",
        lines: [
          'KEEP / CUT / LEAD recommendations for each entry',
          'Which certifications actually add signal',
          'Which extracurriculars show leadership or depth',
          'The strongest order and framing for this section',
        ],
        teaser: 'Some items here may be strengthening your story. Others may be taking up space without helping your candidacy.',
        bridge: 'Unlock the full triage so this section stops diluting your strongest material.',
        cta: 'Unlock full resume feedback - ₹2,499',
      },
      lens: {
        badge: 'ADCOM THOUGHTS',
        badgePremium: true,
        title: 'This is what Adcom think when they see your resume',
        support: 'Your experience has strong raw material, but the full report checks whether the overall profile points clearly to why MBA and why now.',
        includedHeading: "WHAT YOU'LL UNLOCK",
        lines: [
          'Career progression and seniority calibration across roles',
          'Repetition, gaps, and pattern reading across your full story',
          'Whether your profile leads clearly to an MBA',
          'Narrative direction for the rest of your application',
        ],
        teaser: summaryCopyRaw,
        bridge: 'Unlock the strategic read that turns resume feedback into candidacy positioning.',
        cta: 'Unlock full resume feedback - ₹2,499',
      },
    };
    const c = copy[tabId];
    return (
      <div className="space-y-5">
        <div className={`bg-white rounded-xl shadow-sm overflow-hidden ${c.badgePremium ? 'border border-blue-200' : 'border border-gray-200'}`}>
          <div className={`px-6 py-5 border-b flex items-center justify-between ${c.badgePremium ? 'border-blue-100 bg-blue-50/30' : 'border-gray-100'}`}>
            <div>
              <div className="font-bold text-gray-800">{c.title}</div>
              <div className="text-xs text-gray-500 mt-0.5">{c.support || 'Included in the paid report'}</div>
            </div>
            <span className={`text-xs font-semibold px-3 py-1 rounded-full uppercase tracking-wide flex-shrink-0 ml-4 ${c.badgePremium ? 'bg-blue-600 text-white' : 'bg-blue-50 text-blue-600'}`}>{c.badge || 'Paid'}</span>
          </div>
          <div className="px-6 py-5">
            {/* Blurred teaser */}
            <div className="relative mb-5">
              <div className="locked-blur bg-gray-50 rounded-lg p-4 text-sm text-gray-700 leading-relaxed">
                {c.teaser}
              </div>
              <div className="absolute inset-0 flex items-center justify-center">
                <div className="bg-white/90 rounded-full p-2.5 shadow-sm border border-gray-200">
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-5 h-5 text-gray-500" aria-hidden="true">
                    <rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
                    <path d="M7 11V7a5 5 0 0 1 10 0v4"/>
                  </svg>
                </div>
              </div>
            </div>
            {/* What's inside */}
            <div className="text-xs font-semibold text-gray-400 uppercase tracking-wide mb-2">{c.includedHeading || "What's included"}</div>
            <ul className="space-y-1.5 mb-6">
              {c.lines.map((line, i) => (
                <li key={i} className="flex items-start gap-2 text-sm text-gray-600">
                  <span className="text-blue-400 mt-0.5 flex-shrink-0">•</span><span>{line}</span>
                </li>
              ))}
            </ul>
            {c.bridge && (
              <p className="text-sm text-gray-700 leading-relaxed mb-4">{c.bridge}</p>
            )}
            <div className="pt-2">
              <button onClick={() => setShowPayment(true)} className="block w-full text-center bg-blue-600 hover:bg-blue-700 text-white text-sm font-semibold px-4 py-3 rounded-xl transition">
                {c.cta || 'Unlock full resume feedback: ₹2,499'}
              </button>
              <p className="text-center text-xs text-gray-400 mt-2">One-time payment. Instant access.</p>
            </div>
          </div>
        </div>
      </div>
    );
  };

  // ── SHARED HELPERS for Education + Additional Info ──

  // Split feedback prose into sentence-level flag rows
  function parseFeedbackFlags(feedbackText) {
    if (!feedbackText) return [];
    return feedbackText
      .split(/\.\s+/)
      .map(s => s.replace(/\.$/, '').trim())
      .filter(Boolean);
  }

  function flagColor(sentence) {
    const s = sentence.toLowerCase();
    if (/\b(strong|solid|legitimate|excellent|well|good|clear|effective|reinforces|supports|adds)\b/.test(s)) return 'green';
    if (/\b(missing|lacks|no gpa|typo|error|poorly|weak|absent|unclear|cluttered|contradicts|fails|insufficient)\b/.test(s)) return 'red';
    return 'amber';
  }

  const FlagDot = ({ color }) => {
    const cls = color === 'green' ? 'bg-green-400' : color === 'red' ? 'bg-red-400' : 'bg-amber-400';
    return <span className={`w-2 h-2 rounded-full flex-shrink-0 mt-1.5 ${cls}`} />;
  };

  // Best-effort parse of raw content string into degree rows
  function parseContentRows(raw) {
    if (!raw) return [];
    return raw
      .split(/\n|;/)
      .map(s => s.trim())
      .filter(Boolean);
  }

  // ── TAB: EDUCATION (paid) ──
  const EducationTab = () => {
    const edu = analysisData?.education || null;

    const isMissing = !edu
      || !edu.content
      || /^missing$/i.test((edu.content || '').trim())
      || edu.content.trim().length < 5;

    const feedbackText  = (edu?.feedback  || '').trim();
    const feedbackLower = feedbackText.toLowerCase();
    const suggestions   = Array.isArray(edu?.suggestions) ? edu.suggestions.slice(0, 6) : [];
    const rawContent    = (edu?.content || '').trim();
    const contentLower  = rawContent.toLowerCase();

    // Split AI feedback into sentences for signal analysis
    const rawSentences = feedbackText
      .split(/[.!?]+\s+/)
      .map(s => sanitizeBulletRefs(s.replace(/[.!?]+$/, '').trim()))
      .filter(s => s.length > 20);

    // ── Signal detection ──
    // MBA_RX intentionally only tests contentLower (actual degree names), never feedbackLower.
    // AI feedback always mentions "MBA programs" generically, which would cause false positives.
    const MBA_RX = /\b(international mba|executive mba|master of business administration|master.?s? in business administration|mba|pgdm|pgdbm|pgpm|mim|m\.?im|msc management|msc business|masters? in management|masters? in business|business administration)\b/i;
    const hasMBACredential  = MBA_RX.test(contentLower);
    const hasMissingProof   = /\b(missing|lacks|no gpa|absent|without (gpa|honors|distinction)|grade|percentage|rank)\b/.test(feedbackLower);
    const needsClarity      = /\b(unclear|needs clarity|cluttered|confus)\b/.test(feedbackLower);
    const isWeak            = /\b(weak|thin|contradicts|insufficient|raises questions)\b/.test(feedbackLower);
    const hasTimelineIssue  = /\b(overlap|timeline|concurrent|part.?time|full.?time|unclear when|while working|expected|completing|in progress)\b/.test(feedbackLower + ' ' + contentLower);
    const isStrongSignal    = /\b(strong|excellent|prestigious|reinforces candidacy|adds credibility)\b/.test(feedbackLower);
    const isSolidSignal     = /\b(solid|credible|good|clear signal|well-presented)\b/.test(feedbackLower);
    const hasExchange       = /\b(exchange|study abroad|international program)\b/.test(feedbackLower + ' ' + contentLower);

    // Extract degree name and school name from edu.content for personalized copy
    const mbaNameMatch   = rawContent.match(/\b(international mba|master.?s? in business administration|master of business administration|mba|pgdm|pgp|mim|msc management)\b/i);
    const mbaDegreeName  = mbaNameMatch ? mbaNameMatch[0] : 'MBA / business school credential';
    const mbaSchoolMatch = rawContent.match(/\b([A-Z][a-zA-Z\s&'.\-]{2,40}(?:business school|school of business|school of management|university|college|institute|b-school))/i);
    const mbaSchoolName  = mbaSchoolMatch ? mbaSchoolMatch[1].trim() : null;
    const mbaLabel       = mbaSchoolName ? `${mbaDegreeName} (${mbaSchoolName})` : mbaDegreeName;

    // ── Verdict ──
    let verdictHeadline, verdictLabel, verdictLabelCls, verdictBorderCls;
    if (isMissing) {
      verdictHeadline  = 'No education section was detected.';
      verdictLabel     = 'Missing';
      verdictLabelCls  = 'bg-amber-100 text-amber-700';
      verdictBorderCls = 'border-l-4 border-amber-300';
    } else if (hasMBACredential && hasMissingProof) {
      verdictHeadline  = 'Credible academic profile, but missing proof and second-MBA clarity.';
      verdictLabel     = 'Needs clarity';
      verdictLabelCls  = 'bg-amber-100 text-amber-700';
      verdictBorderCls = 'border-l-4 border-amber-300';
    } else if (hasMBACredential) {
      verdictHeadline  = 'Strong education signal, but the MBA rationale needs clarity.';
      verdictLabel     = 'MBA rationale';
      verdictLabelCls  = 'bg-amber-100 text-amber-700';
      verdictBorderCls = 'border-l-4 border-amber-300';
    } else if (isStrongSignal && !hasMissingProof && !needsClarity) {
      verdictHeadline  = 'Strong academic base, but the section needs sharper evidence.';
      verdictLabel     = 'Strong';
      verdictLabelCls  = 'bg-green-100 text-green-700';
      verdictBorderCls = 'border-l-4 border-green-400';
    } else if (isStrongSignal && hasMissingProof) {
      verdictHeadline  = 'Credible education section, but missing academic proof.';
      verdictLabel     = 'Needs proof';
      verdictLabelCls  = 'bg-amber-100 text-amber-700';
      verdictBorderCls = 'border-l-4 border-amber-300';
    } else if (isSolidSignal && !needsClarity) {
      verdictHeadline  = 'Education supports the profile, but does not yet differentiate it.';
      verdictLabel     = 'Solid';
      verdictLabelCls  = 'bg-blue-100 text-blue-700';
      verdictBorderCls = 'border-l-4 border-blue-300';
    } else if (isWeak || needsClarity || hasTimelineIssue) {
      verdictHeadline  = 'Education section raises timeline or credential questions.';
      verdictLabel     = 'Needs clarity';
      verdictLabelCls  = 'bg-amber-100 text-amber-700';
      verdictBorderCls = 'border-l-4 border-amber-300';
    } else {
      verdictHeadline  = 'Education supports the profile, but does not yet differentiate it.';
      verdictLabel     = 'Solid';
      verdictLabelCls  = 'bg-blue-100 text-blue-700';
      verdictBorderCls = 'border-l-4 border-blue-300';
    }

    const problemSentence = rawSentences.find(s =>
      /\b(missing|lacks|unclear|no gpa|confus|weak|thin|absent|issue|problem|needs|raises)\b/i.test(s)
    );

    // ── AdCom questions — hyper-personalized to actual resume signals ──
    const buildQuestions = () => {
      const qs = [];
      if (hasMBACredential) {
        qs.push(`If you already have or are completing the ${mbaLabel}, why are you applying for another MBA or business school program now? What does this next program solve that the first did not?`);
        if (hasTimelineIssue || /expected|completing|in progress/i.test(contentLower))
          qs.push(`Was the ${mbaLabel} completed, still in progress, or expected? The completion status is not immediately clear from the section as presented.`);
        qs.push('Does the resume explain how the prior MBA connects to the next career step, or does the combination of credentials create confusion about the need for another degree?');
      }
      if (hasMissingProof)
        qs.push('What was the academic performance at each institution? GPA, grade percentage, class rank, or academic distinction — if strong — would help AdCom assess how competitive the academic background is.');
      if (!hasMBACredential && (needsClarity || hasTimelineIssue))
        qs.push('Are the completion dates, degree names, and institution names clear enough for an international admissions reader who is unfamiliar with the local academic system?');
      if (!isMissing && !hasMBACredential)
        qs.push('Does the education section clearly connect to the professional story this candidate is building for their MBA application? Or does it just list credentials without signalling what they mean?');
      if (!hasMissingProof && !hasMBACredential && !hasTimelineIssue && !isMissing)
        qs.push('Is there additional academic proof — GPA, academic awards, thesis, exchange, or coursework — that could make this section more compelling?');
      if (hasExchange)
        qs.push('Is any international academic experience — exchange, study abroad, or international program — clearly visible in the section?');
      const seen = new Set();
      return qs.filter(q => {
        const k = q.slice(0, 50).toLowerCase();
        if (seen.has(k)) return false;
        seen.add(k);
        return true;
      }).slice(0, 6);
    };

    const rawQuestions = buildQuestions();
    const displayQuestions = rawQuestions.length > 0
      ? rawQuestions
      : isMissing
        ? ['What institution, degree, and graduation year should appear on this resume?', 'Does the candidate hold a qualification that European MBA programs would recognise?', 'Are dates and completion status clear enough for a quick admissions read?']
        : feedbackText
          ? ['Are there academic performance markers, distinctions, or awards not yet shown?', 'Does the education section clearly support the career story this candidate is telling?']
          : [];

    // ── Fix items — directly respond to the detected questions ──
    const getPriority = (s) => {
      const lower = s.toLowerCase();
      if (/\b(mba rationale|why.*mba|second mba|another mba|completion date|graduation date|degree name|missing|no education|credential|postgrad|diploma)\b/.test(lower))
        return { label: 'High', cls: 'bg-amber-100 text-amber-700' };
      if (/\b(gpa|rank|honors|distinction|award|scholarship|thesis|exchange|coursework|project|capstone)\b/.test(lower))
        return { label: 'Medium', cls: 'bg-blue-100 text-blue-700' };
      return { label: 'Low', cls: 'bg-gray-100 text-gray-500' };
    };

    const getWhy = (s) => {
      const lower = s.toLowerCase();
      if (/mba rationale|why.*mba|another mba|second mba/.test(lower))
        return 'A second or follow-on MBA is not inherently a problem, but it is a question AdCom will have. If the rationale is not visible in the resume itself, it must be answered clearly in the essays.';
      if (/completion date|graduation date|expected|in progress/.test(lower))
        return 'Expected or unclear completion dates create ambiguity. A parenthetical like (Expected July 2023) or (In Progress) removes unnecessary friction for a quick admissions read.';
      if (/gpa|percentage|rank/.test(lower))
        return 'Add only if the score is strong for your academic system and improves the profile. If average or below, leave it out — absence is neutral, a weak score is not.';
      if (/honors|distinction|dean|award|scholarship|fellowship/.test(lower))
        return 'Academic distinction signals selectivity and intellectual capacity. Add only if true — do not inflate or approximate.';
      if (/thesis|capstone|project|research|dissertation/.test(lower))
        return 'A thesis or independent project shows depth and academic rigour. Add only if true and relevant to the MBA narrative.';
      if (/location|country|city|institution name/.test(lower))
        return 'International AdComs are less familiar with regional institutions. Full name, city, and country help them assess the signal accurately.';
      if (/dates|graduation|completion|year/.test(lower))
        return 'Unclear dates create timeline confusion. AdCom reads quickly — ambiguity about when a program was completed can raise unnecessary questions.';
      if (/postgrad|diploma|management|credential/.test(lower))
        return 'A prior management credential can raise the "why MBA now?" question. Address it proactively in essays if needed, not by removing the credential.';
      return null;
    };

    const soften = (s) => {
      if (/gpa|percentage|grade|rank/i.test(s) && !/only if/i.test(s))
        return s.replace(/\.$/, '') + '. Add only if the score is strong for your academic system.';
      if (/(honors|distinction|award|scholarship|thesis|exchange|coursework|capstone)/i.test(s) && !/only if/i.test(s))
        return s.replace(/\.$/, '') + '. Add only if true.';
      return s;
    };

    const fixItems = [];
    if (isMissing) {
      fixItems.push(
        { text: 'Add an education section. Include institution, degree, and graduation year at minimum.', why: 'AdCom expects education on every MBA resume. Without it, even a strong professional profile feels incomplete.', priority: { label: 'High', cls: 'bg-amber-100 text-amber-700' } },
        { text: 'Use the official institution and degree name exactly as it appears on your transcript.', why: 'Institution names are often abbreviated or misspelled. AdCom readers notice this immediately.', priority: { label: 'High', cls: 'bg-amber-100 text-amber-700' } },
        { text: 'Add city and country for each institution.', why: 'International programs evaluate institutions differently based on geography. Full context is needed for an accurate read.', priority: { label: 'Medium', cls: 'bg-blue-100 text-blue-700' } },
      );
    } else {
      if (hasMBACredential)
        fixItems.push({ text: `Clarify the MBA rationale. If you already have or are completing the ${mbaLabel}, your application must clearly explain why another MBA or business school program is still necessary. The resume itself should not make the path look redundant.`, why: 'A second or follow-on MBA is not automatically a red flag, but it is a direct question AdCom will have. If the rationale is not answered clearly in your essays, the academic section creates more confusion than confidence.', priority: { label: 'High', cls: 'bg-amber-100 text-amber-700' } });
      if (hasTimelineIssue || /expected|completing|in progress/i.test(contentLower))
        fixItems.push({ text: 'Clarify the completion date or status of each degree. Add "Expected [Month Year]" or "In Progress" where applicable.', why: 'Unclear completion dates create unnecessary timeline questions during a quick admissions read.', priority: { label: 'High', cls: 'bg-amber-100 text-amber-700' } });
      if (hasMissingProof)
        fixItems.push({ text: 'Add GPA, grade percentage, or class rank only if it is strong for your academic system and improves the profile.', why: 'Add only if strong. If average or below, leave it out — absence is neutral, a weak score is not.', priority: { label: 'Medium', cls: 'bg-blue-100 text-blue-700' } });
      if (suggestions.length > 0) {
        suggestions.slice(0, 4).forEach(s => {
          const text = soften(s);
          if (!fixItems.some(f => f.text.slice(0, 30) === text.slice(0, 30)))
            fixItems.push({ text, why: getWhy(s), priority: getPriority(s) });
        });
      }
      fixItems.push({ text: 'Ensure institution name, city, and country are present for every qualification.', why: 'International programs evaluate institutions differently based on geography. Full context helps AdCom assess the signal accurately.', priority: { label: 'Low', cls: 'bg-gray-100 text-gray-500' } });
    }

    return (
      <div className="space-y-4">

        {/* ── BLOCK 1: EDUCATION VERDICT ── */}
        <div className={`bg-white rounded-2xl border border-gray-100 shadow-sm px-5 py-5 ${verdictBorderCls}`}>
          <div className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest mb-3">Education verdict</div>
          <div className="flex items-start gap-3">
            <div className="flex-1">
              <p className="text-base font-semibold text-gray-900 leading-snug mb-2">{verdictHeadline}</p>
              {!isMissing && problemSentence && (
                <p className="text-sm text-gray-500 leading-relaxed">{problemSentence}.</p>
              )}
              {isMissing && (
                <p className="text-sm text-gray-500 leading-relaxed">AdCom expects an education section on every MBA resume. Without it, even a strong professional profile feels incomplete.</p>
              )}
            </div>
            <span className={`flex-shrink-0 text-xs font-semibold px-2.5 py-1 rounded-full ${verdictLabelCls}`}>{verdictLabel}</span>
          </div>
        </div>

        {/* ── BLOCK 3: QUESTIONS ADCOM MAY HAVE ── */}
        {displayQuestions.length > 0 && (
          <div className="bg-white rounded-2xl border border-amber-100 shadow-sm px-5 py-5">
            <div className="text-[10px] font-semibold text-amber-600 uppercase tracking-widest mb-4">Questions AdCom may have</div>
            <ul className="divide-y divide-gray-50">
              {displayQuestions.map((q, i) => (
                <li key={i} className="flex items-start gap-3 py-3 first:pt-0 last:pb-0">
                  <span className="flex-shrink-0 w-5 h-5 rounded-full bg-amber-50 border border-amber-200 flex items-center justify-center text-[10px] font-bold text-amber-600 mt-0.5 select-none">{i + 1}</span>
                  <p className="flex-1 text-sm text-gray-700 leading-relaxed">{q}{/[.?!]$/.test(q.trim()) ? '' : '?'}</p>
                </li>
              ))}
            </ul>
          </div>
        )}

        {/* ── BLOCK 4: WHAT TO FIX BEFORE SUBMITTING ── */}
        {fixItems.length > 0 && (
          <div className="bg-white rounded-2xl border border-gray-100 shadow-sm px-5 py-5">
            <div className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest mb-4">What to fix before submitting</div>
            <ul className="divide-y divide-gray-50">
              {fixItems.map((item, i) => (
                <li key={i} className="py-3.5 first:pt-0 last:pb-0">
                  <div className="flex items-start gap-3">
                    <span className="flex-shrink-0 w-5 h-5 rounded-full bg-gray-100 border border-gray-200 flex items-center justify-center text-[10px] font-bold text-gray-500 mt-0.5 select-none">{i + 1}</span>
                    <div className="flex-1 min-w-0">
                      <p className="text-sm font-medium text-gray-800 leading-relaxed mb-1">{item.text}</p>
                      {item.why && <p className="text-xs text-gray-500 leading-relaxed">{item.why}</p>}
                    </div>
                    <span className={`flex-shrink-0 text-[10px] font-semibold px-2 py-0.5 rounded-full ${item.priority.cls}`}>{item.priority.label}</span>
                  </div>
                </li>
              ))}
            </ul>
          </div>
        )}

      </div>
    );
  };

  // ── TAB: ADDITIONAL INFO (paid) ──
  const AdditionalInfoTab = () => {
    const { additionalInfo } = analysisData;
    if (!additionalInfo) return <div className="text-sm text-gray-500 py-8 text-center">No additional info data available.</div>;

    const isMissing    = !additionalInfo.content || /^missing$/i.test((additionalInfo.content || '').trim()) || (additionalInfo.content || '').trim().length < 5;
    const feedbackText = (additionalInfo.feedback || '').trim();
    const feedbackLower = feedbackText.toLowerCase();
    const rawContent   = (additionalInfo.content || '').trim();
    const contentLower = rawContent.toLowerCase();
    const combined     = feedbackLower + ' ' + contentLower;

    // Split AI feedback into cleaned, deduplicated sentences for bucket analysis
    const seenSentenceKeys = new Set();
    const rawSentences = feedbackText
      .split(/[.!?]+\s+/)
      .map(s => sanitizeBulletRefs(s.replace(/[.!?]+$/, '').trim()))
      .filter(s => {
        if (s.length < 20) return false;
        const key = s.slice(0, 60).toLowerCase().replace(/\s+/g, ' ');
        if (seenSentenceKeys.has(key)) return false;
        seenSentenceKeys.add(key);
        return true;
      });

    // ── Signal detection from resume content and AI feedback ──
    const hasLanguages    = /\blanguage|english|hindi|spanish|french|arabic|mandarin|german|portuguese|japanese|korean\b/i.test(combined);
    const hasFamilyBiz    = /\bfamily (business|portfolio|enterprise|company)|portfolio|insure.?tech|own (business|company|startup)|entrepreneurial|founder|co.?founder|start.?up|venture\b/i.test(combined);
    const hasVolunteering = /\bvolunteer|ngo|teach|underprivileged|community|society|charitable|social sector|social impact|raahein|pragyavataran\b/i.test(combined);
    const hasSports       = /\bsport|swim|athletic|gold medal|medal|compet|represent.*state|represent.*country|championship|marathon|triathlon\b/i.test(combined);
    const hasCertif       = /\bcertif|cfa|cpa|pmp|acca|aws|gmat|gre|toefl|ielts|credential|qualification\b/i.test(combined);
    // Matches only actual tool names in the content — never the section header
    // itself (e.g. a "Technical Skills" heading used to match here, which meant
    // this fired even when no specific generic tool was ever listed).
    const GENERIC_SKILL_PATTERNS = [
      { rx: /\bms\s*excel\b|\bmicrosoft\s*excel\b|\bexcel\b/i, label: 'Excel' },
      { rx: /\bms\s*powerpoint\b|\bmicrosoft\s*powerpoint\b|\bpowerpoint\b/i, label: 'PowerPoint' },
      { rx: /\bms\s*word\b|\bmicrosoft\s*word\b/i, label: 'Word' },
      { rx: /\bmicrosoft\s*office\b|\bms\s*office\b/i, label: 'MS Office' },
    ];
    const detectedGenericSkills = GENERIC_SKILL_PATTERNS.filter(p => p.rx.test(contentLower)).map(p => p.label);
    const hasGenericSkills = detectedGenericSkills.length > 0;
    const genericSkillsList = detectedGenericSkills.join(', ');
    const hasOldEntries   = /\b(200[0-9]|201[0-6])\b/.test(contentLower);
    const hasOlderYear    = (() => { const m = contentLower.match(/\b(200[0-9]|201[0-2])\b/); return !!m; })();
    const hasInternational = /\binternational|cross.?border|global|mesa|middle east|south asia|saudi|uae|qatar|bahrain|kuwait|europe\b/i.test(combined);
    const hasLeadership   = /\bleader|mentor|board|ngo|association|committee|head|captain|president|chair\b/i.test(combined);
    const isScattered     = /\b(diverse|varied|mix|range|several different|unrelated|scattered|unclear|no clear thread)\b/.test(feedbackLower);
    const isStrong        = /\b(strong|comprehensive|well.?rounded|effective|differentiates|excellent|adds (real|genuine|good) (breadth|depth|value))\b/.test(feedbackLower);
    const isWeak          = /\b(weak|thin|minimal|limited|bare|lacking|underused|underutilized|sparse|could be (stronger|fuller|sharper))\b/.test(feedbackLower);
    const hasDilution     = /\b(dilut|weaken|undermin|tak(es|ing) up space|lower.*signal|hurt|noise)\b/.test(feedbackLower);

    // ── Verdict ──
    let verdictHeadline, verdictLabel, verdictLabelCls, verdictBorderCls;
    if (isMissing) {
      verdictHeadline  = 'The resume lacks an Additional Information section entirely.';
      verdictLabel     = 'Missing';
      verdictLabelCls  = 'bg-amber-100 text-amber-700';
      verdictBorderCls = 'border-l-4 border-amber-300';
    } else if (isStrong && !hasDilution && !isScattered) {
      verdictHeadline  = 'This section adds useful breadth, but needs stronger prioritization.';
      verdictLabel     = 'Good';
      verdictLabelCls  = 'bg-green-100 text-green-700';
      verdictBorderCls = 'border-l-4 border-green-400';
    } else if (hasDilution) {
      verdictHeadline  = 'Additional Info has strong material, but weaker entries dilute it.';
      verdictLabel     = 'Needs triage';
      verdictLabelCls  = 'bg-amber-100 text-amber-700';
      verdictBorderCls = 'border-l-4 border-amber-300';
    } else if (isScattered) {
      verdictHeadline  = 'This section adds differentiation, but needs sharper selection and order.';
      verdictLabel     = 'Needs focus';
      verdictLabelCls  = 'bg-amber-100 text-amber-700';
      verdictBorderCls = 'border-l-4 border-amber-300';
    } else if (isWeak) {
      verdictHeadline  = 'Useful section overall, but some entries are too old or too weak to justify space.';
      verdictLabel     = 'Underused';
      verdictLabelCls  = 'bg-amber-100 text-amber-700';
      verdictBorderCls = 'border-l-4 border-amber-300';
    } else {
      verdictHeadline  = rawSentences[0]
        ? rawSentences[0] + '.'
        : 'Additional Information is present, but needs sharper prioritization.';
      verdictLabel     = 'Needs work';
      verdictLabelCls  = 'bg-amber-100 text-amber-700';
      verdictBorderCls = 'border-l-4 border-amber-300';
    }

    // ── Questions AdCom may have — personalized to what is actually present ──
    const buildAdcomQuestions = () => {
      const qs = [];
      if (isMissing) {
        return [
          'What languages does this candidate speak, and at what level of proficiency?',
          'Is there leadership, volunteering, mentoring, or community involvement outside of work?',
          'Are there certifications, test scores, or technical credentials that support the profile?',
          'What makes this candidate feel like a person beyond their job titles?',
        ];
      }
      if (hasFamilyBiz)
        qs.push('The portfolio / family business entry is the most current entrepreneurial signal in this section. Is this involvement active and concrete enough to read as genuine initiative, or does it risk reading as a passive family obligation?');
      if (hasInternational && hasOldEntries)
        qs.push('The international project or cross-border work is from a past role or internship. Is it still current enough to be worth leading with, or would it be stronger to mention it within the work experience bullet it came from?');
      if (hasVolunteering && hasOlderYear)
        qs.push('The volunteering entries reference work from several years ago. Unless this involvement has continued or grown, entries this old may read as historical rather than active commitments. Is the engagement still ongoing?');
      if (hasSports && /\b200[0-7]\b/.test(contentLower))
        qs.push('A sports achievement from nearly two decades ago is unlikely to add meaningful signal at this stage of a career. Is there a more recent athletic or community achievement that could replace it?');
      if (hasGenericSkills)
        qs.push(`${genericSkillsList} ${detectedGenericSkills.length > 1 ? 'are' : 'is'} standard for most business roles. Does listing ${detectedGenericSkills.length > 1 ? 'these' : 'it'} genuinely add signal here, or is this space better used for a meaningful credential, language, or active involvement?`);
      if (!hasCertif)
        qs.push('Are there any professional certifications, industry qualifications, or test scores that are not currently listed but would support the profile?');
      if (!hasLanguages && !isMissing)
        qs.push('Does this candidate speak additional languages? Language capability is a strong signal for European MBA programs and is noticeably absent from this section.');
      const seen = new Set();
      return qs.filter(q => {
        const k = q.slice(0, 50).toLowerCase();
        if (seen.has(k)) return false;
        seen.add(k);
        return true;
      }).slice(0, 6);
    };
    const adcomQuestions = buildAdcomQuestions();

    // ── What to fix before submitting ──
    const fixItems = [];
    if (!isMissing) {
      if (hasFamilyBiz)
        fixItems.push({ title: 'Move the most current, active item to the top', text: 'If the portfolio or entrepreneurial involvement is ongoing, it should lead. Ordering matters — AdCom reads the first entry as the most important signal.', priority: { label: 'High', cls: 'bg-amber-100 text-amber-700' } });
      if (hasOldEntries && hasOlderYear)
        fixItems.push({ title: 'Remove or contextualise entries from over a decade ago', text: 'Unless involvement has been sustained continuously, activities from 10+ years ago should be cut. They date the profile without adding signal.', priority: { label: 'High', cls: 'bg-amber-100 text-amber-700' } });
      if (hasGenericSkills)
        fixItems.push({ title: 'Remove generic technical skills unless they are genuinely advanced', text: `${genericSkillsList} ${detectedGenericSkills.length > 1 ? 'are' : 'is'} assumed for most business roles. Remove ${detectedGenericSkills.length > 1 ? 'them' : 'it'} or replace with a more advanced or specific credential.`, priority: { label: 'Medium', cls: 'bg-blue-100 text-blue-700' } });
      if (hasVolunteering && hasOlderYear)
        fixItems.push({ title: 'Clarify whether volunteering is ongoing or historical', text: 'Add a date range or a note that the engagement is ongoing if true. Without it, old volunteering reads as a one-time activity rather than a sustained commitment.', priority: { label: 'Medium', cls: 'bg-blue-100 text-blue-700' } });
      if (hasFamilyBiz)
        fixItems.push({ title: 'Add one specific detail to the entrepreneurial entry', text: 'Replace vague phrasing like "overlooking family portfolio" with what you are actually doing — managing X investment, advising on Y strategy, or supporting Z company. Specificity converts it from filler to signal.', priority: { label: 'Medium', cls: 'bg-blue-100 text-blue-700' } });
      fixItems.push({ title: 'Check the section reads as a coherent picture, not a list', text: 'Every entry should answer: does this make the profile stronger or more rounded? If an entry does not clearly add depth, credibility, or differentiation, cut it.', priority: { label: 'Low', cls: 'bg-gray-100 text-gray-500' } });
    } else {
      fixItems.push(
        { title: 'Add a languages line', text: 'List every language you speak with an honest proficiency level (native, professional, conversational). This carries real weight at European MBA programs.', priority: { label: 'High', cls: 'bg-amber-100 text-amber-700' } },
        { title: 'Add leadership or community involvement outside of work', text: 'Mentoring, NGO work, board roles, professional association involvement — anything that shows how you contribute beyond your day job.', priority: { label: 'High', cls: 'bg-amber-100 text-amber-700' } },
        { title: 'Add one or two specific interests', text: 'Not generic phrases like "travel and reading." Something specific that makes you feel like a person: ultra-running, competitive chess, documentary filmmaking.', priority: { label: 'Medium', cls: 'bg-blue-100 text-blue-700' } },
      );
    }

    // ── Suggestion categories ──
    const SUGGESTION_CATEGORIES = [
      {
        category: 'Languages',
        text: 'If you speak additional languages, list them with an honest proficiency level (native, professional, conversational). This carries real weight at INSEAD, LBS, and HEC Paris.',
      },
      {
        category: 'Certifications',
        text: 'Industry certifications — CFA, CPA, PMP, AWS, ACCA, or sector equivalents — add credibility if relevant to your target industry or MBA narrative. Only include if obtained.',
      },
      {
        category: 'Test Scores',
        text: 'GMAT or GRE scores, if strong, can be listed here. Omit if the program already collects them separately, or if the score falls below the program median.',
      },
      {
        category: 'Leadership, Volunteering, and Mentoring',
        text: 'Roles outside of work — mentoring programmes, NGO board seats, community leadership, or professional association involvement — show how you contribute beyond your day job. Only include if the involvement was real and substantive.',
      },
      {
        category: 'Publications, Speaking, and Community',
        text: 'Articles, conference talks, podcast appearances, or open-source contributions show intellectual engagement and professional visibility. Add only if true.',
      },
      {
        category: 'Interests',
        text: 'One or two specific, genuine interests — not generic phrases like "travel and reading" — can make the profile feel real. Be specific: ultra-running, Swahili fiction, competitive chess.',
      },
    ];

    // ── Pills for "what strong sections include" ──
    const STRONG_SECTION_PILLS = [
      { icon: '🌐', label: 'Languages' },
      { icon: '📜', label: 'Certifications' },
      { icon: '📊', label: 'Test Scores' },
      { icon: '🤝', label: 'Volunteering' },
      { icon: '👥', label: 'Leadership' },
      { icon: '🏘️', label: 'Community' },
      { icon: '📝', label: 'Publications' },
      { icon: '🎤', label: 'Speaking' },
      { icon: '🏆', label: 'Awards' },
      { icon: '💡', label: 'Interests' },
      { icon: '🎨', label: 'Hobbies' },
      { icon: '💻', label: 'Technical Skills' },
      { icon: '🎓', label: 'Mentoring' },
      { icon: '🔧', label: 'Open Source' },
      { icon: '🪑', label: 'Board Roles' },
    ];

    return (
      <div className="space-y-4">

        {/* 1. VERDICT */}
        <div className={`bg-white rounded-2xl border border-gray-100 shadow-sm px-5 py-5 ${verdictBorderCls}`}>
          <div className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest mb-3">Additional Info verdict</div>
          <div className="flex items-start gap-3">
            <div className="flex-1">
              <p className="text-base font-semibold text-gray-900 leading-snug mb-2">{verdictHeadline}</p>
              {isMissing && (
                <p className="text-sm text-gray-500 leading-relaxed">This section is your chance to show AdCom who you are beyond your job titles. Its absence is a missed opportunity to humanise the profile.</p>
              )}
            </div>
            <span className={`flex-shrink-0 text-xs font-semibold px-2.5 py-1 rounded-full ${verdictLabelCls}`}>{verdictLabel}</span>
          </div>
        </div>

        {/* 3. QUESTIONS ADCOM MAY HAVE */}
        {adcomQuestions.length > 0 && (
          <div className="bg-white rounded-2xl border border-amber-100 shadow-sm px-5 py-5">
            <div className="text-[10px] font-semibold text-amber-600 uppercase tracking-widest mb-4">Questions AdCom may have</div>
            <ul className="divide-y divide-gray-50">
              {adcomQuestions.map((q, i) => (
                <li key={i} className="flex items-start gap-3 py-3 first:pt-0 last:pb-0">
                  <span className="flex-shrink-0 w-5 h-5 rounded-full bg-amber-50 border border-amber-200 flex items-center justify-center text-[10px] font-bold text-amber-600 mt-0.5 select-none">{i + 1}</span>
                  <p className="flex-1 text-sm text-gray-700 leading-relaxed">{q}{/[.?!]$/.test(q.trim()) ? '' : '?'}</p>
                </li>
              ))}
            </ul>
          </div>
        )}

        {/* 4. WHAT TO FIX BEFORE SUBMITTING */}
        {fixItems.length > 0 && (
          <div className="bg-white rounded-2xl border border-gray-100 shadow-sm px-5 py-5">
            <div className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest mb-4">What to fix before submitting</div>
            <ul className="divide-y divide-gray-50">
              {fixItems.map((item, i) => (
                <li key={i} className="py-3.5 first:pt-0 last:pb-0">
                  <div className="flex items-start gap-3">
                    <span className="flex-shrink-0 w-5 h-5 rounded-full bg-gray-100 border border-gray-200 flex items-center justify-center text-[10px] font-bold text-gray-500 mt-0.5 select-none">{i + 1}</span>
                    <div className="flex-1 min-w-0">
                      <p className="text-sm font-semibold text-gray-800 leading-snug mb-1">{item.title}</p>
                      <p className="text-xs text-gray-500 leading-relaxed">{item.text}</p>
                    </div>
                    <span className={`flex-shrink-0 text-[10px] font-semibold px-2 py-0.5 rounded-full ${item.priority.cls}`}>{item.priority.label}</span>
                  </div>
                </li>
              ))}
            </ul>
          </div>
        )}

        {/* 5. SUGGESTED THINGS TO ADD, IF TRUE */}
        <div className="bg-white rounded-2xl border border-gray-100 shadow-sm px-5 py-5">
          <div className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest mb-1">Suggested things you can add, if true</div>
          <p className="text-xs text-gray-400 mb-4">Only add what is accurate. Do not invent credentials, roles, or activities.</p>
          <div className="divide-y divide-gray-50">
            {SUGGESTION_CATEGORIES.map((item, i) => (
              <div key={i} className="flex items-start gap-3 py-3.5 first:pt-0 last:pb-0">
                <div className="flex-shrink-0 w-7 h-7 rounded-lg bg-gray-100 flex items-center justify-center mt-0.5">
                  <Pi id={['globe2','badgeCheck','trendingUp','users','pencilLine','sparkles'][i] || 'sparkles'} cls="w-3.5 h-3.5 text-gray-500" />
                </div>
                <div className="flex-1">
                  <p className="text-sm font-semibold text-gray-800 mb-0.5">{item.category}</p>
                  <p className="text-xs text-gray-500 leading-relaxed">{item.text}</p>
                </div>
              </div>
            ))}
          </div>
        </div>

        {/* 6. WHAT STRONG SECTIONS USUALLY INCLUDE */}
        <div className="bg-gradient-to-br from-blue-900 to-blue-800 rounded-2xl px-5 py-5">
          <div className="text-[10px] font-semibold text-blue-300 uppercase tracking-widest mb-1">What strong Additional Information sections usually include</div>
          <p className="text-xs text-blue-400 mb-4">A quick reference, not a checklist. Include only what is true for you.</p>
          <div className="flex flex-wrap gap-2">
            {STRONG_SECTION_PILLS.map((pill, i) => (
              <span key={i} className="inline-flex items-center gap-1.5 bg-white/10 border border-white/15 text-white text-xs font-medium px-3 py-1.5 rounded-full">
                <span>{pill.icon}</span>
                <span>{pill.label}</span>
              </span>
            ))}
          </div>
        </div>

      </div>
    );
  };

  // ── TAB: MBA LENS (paid) ──
  const LensTab = () => {
    const summary       = analysisData.bottomLine?.summary || '';
    const roles         = analysisData.workExperience || [];
    const eduContent    = analysisData.education?.content || '';
    const addContent    = analysisData.additionalInfo?.content || '';

    const allBullets       = roles.flatMap(r => (r.bullets || []).map(b => ({ ...b, _role: r })));
    const allBulletText    = allBullets.map(b => b.original || '').join(' ');
    const allText          = [allBulletText, eduContent, addContent].join(' ');
    const strongGoodBullets = allBullets.filter(b => ['strong', 'good'].includes(normalizeStatus(b.status)));
    const weakBullets       = allBullets.filter(b => ['improve', 'rewrite'].includes(normalizeStatus(b.status)));
    const totalBullets      = allBullets.length;
    const strongRatio       = totalBullets > 0 ? strongGoodBullets.length / totalBullets : 0;
    const isOverallWeak     = strongRatio < 0.3;

    // Quantification
    const quantRx = /\b\d[\d,.]*\s*(%|million|billion|k\b|thousand|USD|EUR|INR|GBP|revenue|growth|increase|reduction|saving|clients|countries|employees|people|portfolio)/i;
    const quantifiedBullets  = allBullets.filter(b => quantRx.test(b.original || ''));
    const hasQuantified      = quantifiedBullets.length >= 2;
    const hasStrongQuantified = quantifiedBullets.length >= 4;

    // Leadership
    const leadershipRx      = /\b(led|managed|supervised|mentored|coached|directed|oversaw|head of|vp|vice president|director|partner|founder|co-founder|team of|cross-functional|initiative|established|built|launched)\b/i;
    const leadershipBullets = allBullets.filter(b => leadershipRx.test(b.original || ''));
    const hasLeadership     = leadershipBullets.length >= 2;

    // Seniority
    const seniorTitleRx = /\b(director|vp|vice president|senior|head|chief|partner|founder|co-founder|principal|manager|lead|president|associate director|associate partner)\b/i;
    const seniorRoles   = roles.filter(r => seniorTitleRx.test(r.title || ''));
    const hasSeniorTitle = seniorRoles.length > 0;

    // International — keyword match alone misses the common case where the
    // signal is structural, not lexical: e.g. an Indian candidate doing an
    // MBA in Spain, where no bullet ever literally says "international" but
    // relocating between countries for study or work is itself the evidence.
    // Compare education location against work locations (and work locations
    // against each other) — 2+ distinct countries means real cross-border
    // experience regardless of what the bullets say.
    const intlRx        = /\b(international|global|cross-border|multinational|overseas|region|countries|markets|cross-cultural|expat|foreign)\b/i;
    const countryToken = (loc) => {
      if (!loc) return '';
      // Defensive cleanup: the prompt asks for one clean "City, Country" per
      // location, but the model doesn't always comply (e.g. appends "(primary)"
      // or "and City, Country (undergraduate)" for a second institution) —
      // strip that noise rather than trust the format.
      let cleaned = String(loc).split(/\s+and\s+/i)[0];
      cleaned = cleaned.replace(/\([^)]*\)/g, '').trim();
      const parts = cleaned.split(',').map(s => s.trim()).filter(Boolean);
      return (parts[parts.length - 1] || '').toLowerCase();
    };
    const allLocations = [...roles.map(r => r.location || ''), analysisData.education?.location || ''].filter(Boolean);
    const distinctCountries = new Set(allLocations.map(countryToken).filter(Boolean));
    const hasLocationDiversity = distinctCountries.size >= 2;
    const hasInternational = intlRx.test(allBulletText) || intlRx.test(addContent) || hasLocationDiversity;

    // Sector
    const isFinance     = /\b(investment|banking|finance|private equity|pe|vc|venture capital|fund|portfolio|equity|asset management|capital markets|trading|treasury|m&a|mergers|acquisitions)\b/i.test(allText);
    const isConsulting  = /\b(consulting|consultant|advisory|mckinsey|bcg|bain|deloitte|ey|pwc|kpmg|accenture|strategy|strategic advisory)\b/i.test(allText);
    const isTech        = /\b(software|engineering|product manager|product management|technology|tech startup|saas|platform|digital transformation|data science|machine learning)\b/i.test(allText);
    const isEntrepreneur = /\b(founder|co-founder|startup|own business|built from scratch|launched)\b/i.test(allText);
    const isFamilyBiz   = /\b(family business|family-owned|family firm|family enterprise)\b/i.test(allText);

    // Progression
    const hasProgression = roles.length >= 3;

    // MBA credential detection — intentionally only tests eduContent (actual
    // degree names on the resume), never AI feedback text. Feedback almost
    // always mentions "MBA" generically (e.g. "not yet clear why an MBA now"),
    // which would false-positive as if a prior MBA credential exists.
    const MBA_RX        = /\b(mba|international mba|master.?s? in business administration|pgdm|pgp|mim|msc management|management programme|pgpm)\b/i;
    const hasMBACredential = MBA_RX.test(eduContent);
    const mbaNameMatch  = eduContent.match(/\b(international mba|master of business administration|mba|pgdm|pgp|mim|msc management|pgpm)\b/i);
    const mbaDegreeName = mbaNameMatch ? mbaNameMatch[0].toUpperCase() : 'MBA / business credential';

    // Languages / community
    const hasLanguages    = /\b(fluent|proficient|native|bilingual|language|languages|speaks|arabic|french|spanish|german|mandarin|hindi|portuguese)\b/i.test(addContent);
    const hasVolunteering = /\b(volunteer|volunteering|ngo|nonprofit|non-profit|social impact|community)\b/i.test(addContent);

    // ── SECTION 2: What AdCom will like ──
    const strengthSignals = [
      hasStrongQuantified && {
        iconId: 'trendingUp', title: 'Quantified impact',
        what: `${quantifiedBullets.length} bullets include specific numbers, percentages, or measurable scale. This is rare and immediately credible.`,
        why: 'AdCom reads hundreds of resumes with vague claims. Real numbers are the fastest way to cut through.',
      },
      hasQuantified && !hasStrongQuantified && {
        iconId: 'trendingUp', title: 'Some measurable impact',
        what: `${quantifiedBullets.length} bullets include specific metrics. The rest describe work without outcome data, but the presence of numbers already helps.`,
        why: 'Quantified bullets give AdCom something to anchor on. Even partial quantification signals commercial awareness.',
      },
      hasLeadership && {
        iconId: 'users', title: 'Leadership signal',
        what: leadershipBullets.length > 3
          ? `Leadership language appears across ${leadershipBullets.length} bullets. Team management, initiative, and cross-functional ownership are visible.`
          : `Leadership appears in ${leadershipBullets.length} bullet${leadershipBullets.length > 1 ? 's' : ''}. There is ownership beyond individual contribution.`,
        why: 'European MBA programs select heavily on leadership potential. Clear evidence of leading people, projects, or decisions strengthens the application.',
      },
      hasSeniorTitle && {
        iconId: 'badgeCheck', title: `Seniority: ${seniorRoles[0]?.title || 'senior role'}`,
        what: `At least one role carries a senior or leadership title. This signals meaningful responsibility has been trusted to this profile.`,
        why: 'Programs like INSEAD and LBS attract applicants with genuine accountability. A credible title, backed by substantive bullets, shows readiness for business school.',
      },
      hasInternational && {
        iconId: 'globe2', title: 'International or cross-border exposure',
        what: 'The resume references international markets, cross-border work, or global scope. This is visible even without an expat posting.',
        why: 'International exposure is a core differentiator for European MBA programs. INSEAD, LBS, and HEC prize it explicitly.',
      },
      isFinance && {
        iconId: 'trendingUp', title: 'Finance sector depth',
        what: 'The profile shows significant experience in investment, banking, or financial markets. Sector vocabulary and context are clearly visible.',
        why: 'Finance backgrounds translate well to case-method programs. Strong sector depth, paired with strategic framing, positions the applicant as ready to drive business conversations.',
      },
      isConsulting && {
        iconId: 'sparkles', title: 'Consulting background',
        what: 'The resume reflects a structured, advisory background. Problem-solving, client service, and strategy framing are present.',
        why: 'Consulting experience signals analytical rigor and communication discipline. European programs recognise the transition value an MBA provides for post-consulting pivots.',
      },
      isTech && {
        iconId: 'layers', title: 'Technology and product depth',
        what: 'The resume reflects a technology or product background. Technical credibility combined with business context is visible.',
        why: 'Tech candidates bring analytical and systems-thinking strengths that are increasingly valued in European MBA cohorts.',
      },
      isEntrepreneur && {
        iconId: 'sparkles', title: 'Entrepreneurial signal',
        what: 'The resume includes founding, building, or early-stage venture experience. This signals risk tolerance and personal initiative.',
        why: 'Entrepreneurial candidates differentiate a cohort. Programs like INSEAD and IESE actively seek applicants who have launched, owned, or built something from scratch.',
      },
      isFamilyBiz && {
        iconId: 'building2', title: 'Family business exposure',
        what: 'The resume references family business involvement. This is often an underleveraged signal in MBA applications.',
        why: 'Family business experience shows commercial stakes, operational reality, and often multi-generational decision-making. It is an emerging strength for European programs.',
      },
      hasLanguages && {
        iconId: 'globe2', title: 'Language skills',
        what: 'The profile lists multiple languages or fluency in non-native languages. This supports the international citizenship narrative.',
        why: 'INSEAD admits students from 80+ nationalities. Language skills reinforce the international profile and signal cultural agility.',
      },
      hasProgression && {
        iconId: 'gitBranch', title: 'Career progression visible',
        what: `The resume shows ${roles.length} roles with a visible build across positions. Each move adds some scope or responsibility.`,
        why: 'A clear trajectory tells AdCom that this applicant has intentionally grown their career, not just accumulated experience.',
      },
    ].filter(Boolean).slice(0, 6);

    // ── SECTION 3: Potential areas of concern ──
    const concernSignals = [
      hasMBACredential && {
        iconId: 'circleHelp', title: `Why another ${mbaDegreeName}?`,
        concern: `The education section shows a prior ${mbaDegreeName}. AdCom will ask what gap this degree fills that the first did not.`,
        howToFrame: 'The essays must answer this directly. Frame it as a deliberate, informed decision. If the context changed, say so explicitly.',
      },
      isOverallWeak && {
        iconId: 'alertTriangle', title: 'Bullet quality is inconsistent',
        concern: 'More than half of the bullets reviewed need significant work in language, structure, or impact framing. The underlying experience may be strong, but the resume is not showing it.',
        howToFrame: 'A resume that reads weak costs credibility before the essays are even opened. Rewriting bullets around outcomes rather than responsibilities is the highest leverage action.',
      },
      !hasQuantified && {
        iconId: 'alertTriangle', title: 'Limited quantification',
        concern: 'Few or no bullets include specific numbers, percentages, or scale. This makes it harder for AdCom to gauge the impact of the work described.',
        howToFrame: 'Even one or two anchoring numbers per role change how the profile reads. Think: team size, budget, client revenue, growth rate, timeframe.',
      },
      !hasLeadership && {
        iconId: 'users', title: 'Leadership signal is not visible',
        concern: 'The resume describes work and responsibilities, but leadership, people management, initiative ownership, and cross-functional influence are not clearly present.',
        howToFrame: 'If leadership exists in the role, it needs to appear in the bullets. Who did you lead? Who followed? What changed because you were in the room?',
      },
      weakBullets.length > 2 && {
        iconId: 'eye', title: 'Some bullets read as task descriptions',
        concern: `${weakBullets.length} bullet${weakBullets.length > 1 ? 's' : ''} describe responsibilities rather than outcomes. This pattern makes the profile read as an executor, not a driver.`,
        howToFrame: 'Outcome-led bullets signal commercial thinking. Each bullet should answer: what changed because of this person?',
      },
      !hasInternational && {
        iconId: 'globe2', title: 'International exposure is not visible',
        concern: 'For European MBA programs like INSEAD, LBS, and HEC, international scope is a differentiating signal. The resume does not currently surface this.',
        howToFrame: 'If the role involved any cross-border exposure, client diversity, or international markets, make it explicit in the bullets rather than leaving it implied.',
      },
      hasSeniorTitle && isOverallWeak && {
        iconId: 'alertTriangle', title: 'Title and bullet content may not match',
        concern: 'The resume carries senior or leadership titles, but several bullets describe execution-level work. This mismatch can be a credibility risk.',
        howToFrame: 'AdCom expects bullet content to validate the title. If the work was strategic, frame it explicitly. Show what changed, not what was managed.',
      },
    ].filter(Boolean).slice(0, 6);

    // ── SECTION 4: Career arc, seniority, leadership reads ──
    const careerArcRead = roles.length === 0
      ? 'No work experience roles could be read from the resume.'
      : roles.length === 1
        ? 'The resume shows one role. Without visible progression across multiple positions, career arc is difficult to assess. The essays must compensate.'
        : hasSeniorTitle
          ? `The resume shows ${roles.length} roles including ${seniorRoles.map(r => r.title).slice(0, 2).join(' and ')}. There is a visible build-up in responsibility over time.`
          : `The resume shows ${roles.length} roles. Progression exists, but may not read as strongly upward without clearer seniority markers in the titles.`;

    const careerArcFix = hasProgression
      ? 'Make the step-change at each transition explicit. Each role should signal a bigger remit than the one before.'
      : roles.length === 1
        ? 'A single-role resume is limiting. Context around scope, promotions, or internal growth can partially compensate. The essays must tell the arc story.'
        : 'Make sure the oldest role is not given equal space to the most recent one. Weight the resume toward the last 5 years.';

    const seniorityRead = hasSeniorTitle
      ? 'Title signals are present. The question is whether the bullets under those titles confirm the level of authority implied by the role.'
      : 'The titles on this resume do not clearly signal seniority. AdCom may read this as a mid-level profile unless the bullet content compensates.';

    const seniorityFix = hasSeniorTitle
      ? 'For each senior or leadership title, at least one bullet should name a decision, a business outcome, or a scope boundary that validates the title.'
      : 'If ownership exists in the role, such as budget, team, client, or P&L, it needs to be named explicitly in the bullets rather than left implied.';

    const leadershipRead = hasLeadership
      ? `Leadership language is present. ${leadershipBullets.length} bullet${leadershipBullets.length > 1 ? 's' : ''} reference${leadershipBullets.length === 1 ? 's' : ''} leading, managing, or owning an initiative.`
      : 'Leadership is not clearly visible in the current resume. The profile reads as a strong individual contributor, but ownership of outcomes is not explicit.';

    const leadershipFix = hasLeadership
      ? 'Ensure at least one bullet per role names a leadership moment: who reported to you, what cross-functional team you drove, or what initiative you owned end-to-end.'
      : 'If you managed a team, led a project, or influenced decisions beyond your formal scope, that needs to appear explicitly. Informal leadership still counts if you name it.';

    // ── Executive strategy read ──
    const mainRisk = hasMBACredential
      ? `Why another ${mbaDegreeName}? This question needs a direct answer in the goals essay, not an implicit assumption that AdCom will understand.`
      : isOverallWeak
        ? 'Bullet quality may not yet reflect actual experience level. The resume needs rewriting before the application goes in.'
        : !hasLeadership
          ? 'Leadership signal is not clearly visible. The profile reads as a strong individual contributor, but AdCom needs to see ownership of decisions and outcomes.'
          : !hasQuantified
            ? 'Limited measurable impact. Most bullets describe work without quantified outcomes, making it harder for AdCom to gauge scale.'
            : 'MBA rationale is not visible from the resume alone. Essays must explain why this profile points specifically to an MBA at this stage.';

    const bestAngle = isFinance
      ? 'Finance and investment sector depth is a genuine differentiator. Position around real commercial ownership, not just deal exposure.'
      : isConsulting
        ? 'Structured advisory and strategy background. Frame around client outcomes and strategic decisions, not project management tasks.'
        : isTech
          ? 'Technology and product leadership. Lead with the business impact of the technical work, not the technical work itself.'
          : (isEntrepreneur || isFamilyBiz)
            ? 'Entrepreneurial or family business experience. Frame around initiative, risk, and commercial stakes, not just operational involvement.'
            : (hasLeadership && hasSeniorTitle)
              ? 'Senior leadership with clear ownership. Lead every essay and interview story around what changed because of your decisions.'
              : hasInternational
                ? 'International and cross-border business perspective. Frame around the business judgement this experience developed, not the geography alone.'
                : 'Professional expertise and sector knowledge. The essays must build the narrative arc that the resume cannot carry alone.';

    // ── SECTION 8: Next application moves ──
    const nextMoves = [
      {
        iconId: 'pencilLine', action: 'Rewrite weak bullets around outcomes, not responsibilities',
        detail: weakBullets.length > 0
          ? `${weakBullets.length} bullet${weakBullets.length > 1 ? 's' : ''} currently need${weakBullets.length === 1 ? 's' : ''} significant work. Start there before anything else goes out.`
          : 'Focus on any bullet where the outcome is vague or the contribution is unclear.',
      },
      !hasQuantified && {
        iconId: 'trendingUp', action: 'Quantify impact wherever possible',
        detail: 'Go back to each role and find at least one specific number: team size, budget, revenue, client count, or growth rate. These anchors transform how the profile reads.',
      },
      hasMBACredential && {
        iconId: 'circleHelp', action: `Prepare a clear answer to "why another ${mbaDegreeName}"`,
        detail: 'This must appear in the goals essay, not buried in an interview response. Be direct, be specific about what changed, and show you have thought carefully about it.',
      },
      {
        iconId: 'compass', action: 'Define and align 2 to 3 application story pillars',
        detail: 'Pick the themes that best represent your candidacy: leadership, sector expertise, international scope, entrepreneurial drive. Every part of the application should reinforce these.',
      },
      {
        iconId: 'users', action: 'Prepare 3 to 4 leadership stories for recommendations and interviews',
        detail: 'Identify specific moments where you led, influenced, or delivered a meaningful outcome. These become the backbone of your interview answers and what you brief your recommenders on.',
      },
      {
        iconId: 'listChecks', action: 'Align resume, essays, LinkedIn, and goals into one narrative',
        detail: 'Inconsistency across application components is one of the most common weaknesses AdCom flags. Make sure the resume story and the essays are not telling parallel versions of the same profile.',
      },
      targetSchools && {
        iconId: 'graduationCap', action: `Build 2 to 3 school-specific examples for ${targetSchools.split(',')[0].trim()}`,
        detail: 'Use the strongest bullets from this resume as the foundation for school-specific essays. Each bullet that shows clear impact is a story you can expand in a 250-word response.',
      },
    ].filter(Boolean).slice(0, 6);

    return (
      <div className="space-y-4">

        {/* 1. Based on your resume alone */}
        <div className="bg-white rounded-2xl border border-gray-100 shadow-sm px-5 py-5">
          <div className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest mb-3">Based on your resume alone</div>
          <p className="text-sm text-gray-600 leading-relaxed">
            This read is based only on what your resume currently communicates. It does not replace school selection, essays, recommendations, goals strategy, or a full application review. But it shows how your current resume is likely to shape the first impression of your MBA candidacy.
          </p>
        </div>

        {/* 3. What AdCom will like */}
        {strengthSignals.length > 0 && (
          <div className="bg-white rounded-2xl border border-gray-100 shadow-sm px-5 py-5">
            <div className="text-[10px] font-semibold text-green-600 uppercase tracking-widest mb-1">What AdCom will like</div>
            <p className="text-xs text-gray-400 mb-4">The strongest signals visible in the resume right now.</p>
            <div className="divide-y divide-gray-50">
              {strengthSignals.map((s, i) => (
                <div key={i} className="flex items-start gap-3 py-4 first:pt-0 last:pb-0">
                  <div className="flex-shrink-0 w-8 h-8 rounded-lg bg-green-50 flex items-center justify-center mt-0.5">
                    <Pi id={s.iconId} cls="w-4 h-4 text-green-600" />
                  </div>
                  <div className="flex-1 min-w-0">
                    <p className="text-sm font-semibold text-gray-800 mb-1">{s.title}</p>
                    <p className="text-xs text-gray-600 leading-relaxed mb-1">{s.what}</p>
                    <p className="text-xs text-green-700 font-medium leading-relaxed">{s.why}</p>
                  </div>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* 4. Potential areas of concern */}
        {concernSignals.length > 0 && (
          <div className="bg-white rounded-2xl border border-amber-100 shadow-sm px-5 py-5">
            <div className="text-[10px] font-semibold text-amber-600 uppercase tracking-widest mb-1">Potential areas of concern</div>
            <p className="text-xs text-gray-400 mb-4">Soft flags, not hard disqualifiers. Each one is addressable.</p>
            <div className="divide-y divide-amber-50">
              {concernSignals.map((c, i) => (
                <div key={i} className="flex items-start gap-3 py-4 first:pt-0 last:pb-0">
                  <div className="flex-shrink-0 w-8 h-8 rounded-lg bg-amber-50 flex items-center justify-center mt-0.5">
                    <Pi id={c.iconId} cls="w-4 h-4 text-amber-600" />
                  </div>
                  <div className="flex-1 min-w-0">
                    <p className="text-sm font-semibold text-gray-800 mb-1">{c.title}</p>
                    <p className="text-xs text-gray-600 leading-relaxed mb-1.5">{c.concern}</p>
                    <p className="text-xs text-amber-700 font-medium leading-relaxed">{c.howToFrame}</p>
                  </div>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* 5. Career arc, seniority, and leadership */}
        <div className="bg-white rounded-2xl border border-gray-100 shadow-sm px-5 py-5">
          <div className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest mb-4">Career arc, seniority, and leadership signal</div>
          <div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
            {[
              { label: 'Career arc clarity',  read: careerArcRead,    fix: careerArcFix,    iconId: 'gitBranch' },
              { label: 'Seniority signal',     read: seniorityRead,    fix: seniorityFix,    iconId: 'badgeCheck' },
              { label: 'Leadership signal',    read: leadershipRead,   fix: leadershipFix,   iconId: 'users' },
            ].map((card, i) => (
              <div key={i} className="bg-gray-50 rounded-xl p-4">
                <div className="flex items-center gap-2 mb-2">
                  <Pi id={card.iconId} cls="w-4 h-4 text-blue-500 flex-shrink-0" />
                  <p className="text-xs font-semibold text-gray-700">{card.label}</p>
                </div>
                <p className="text-xs text-gray-600 leading-relaxed mb-3">{card.read}</p>
                <div className="border-t border-gray-200 pt-3">
                  <p className="text-[10px] font-semibold text-blue-500 uppercase tracking-wide mb-1">How to strengthen this</p>
                  <p className="text-xs text-gray-500 leading-relaxed">{card.fix}</p>
                </div>
              </div>
            ))}
          </div>
        </div>

        {/* 6. Next application moves */}
        <div className="bg-white rounded-2xl border border-gray-100 shadow-sm px-5 py-5">
          <div className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest mb-4">Next application moves</div>
          <ul className="divide-y divide-gray-50">
            {nextMoves.map((move, i) => (
              <li key={i} className="flex items-start gap-3 py-3.5 first:pt-0 last:pb-0">
                <div className="flex-shrink-0 w-7 h-7 rounded-full bg-blue-50 border border-blue-100 flex items-center justify-center">
                  <Pi id={move.iconId} cls="w-3.5 h-3.5 text-blue-500" />
                </div>
                <div className="flex-1 min-w-0">
                  <p className="text-sm font-semibold text-gray-800 leading-snug mb-0.5">{move.action}</p>
                  <p className="text-xs text-gray-500 leading-relaxed">{move.detail}</p>
                </div>
              </li>
            ))}
          </ul>
        </div>

      </div>
    );
  };

  return (
    <div className="min-h-screen bg-gray-50">
      {showPayment && (paymentState === 'done' ? (
        <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
          <div className="bg-white rounded-2xl p-8 max-w-sm w-full text-center shadow-2xl">
            <div className="text-5xl mb-4">✓</div>
            <div className="text-lg font-bold text-gray-900 mb-1">Unlocked</div>
            <div className="text-sm text-gray-500">Running your full analysis now...</div>
          </div>
        </div>
      ) : (
        <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
          <div className="bg-white rounded-2xl shadow-2xl w-full max-w-md overflow-hidden">
            <div className="bg-blue-900 px-6 py-5 text-white">
              <div className="flex items-center justify-between mb-3">
                <div className="text-xs font-semibold text-blue-300 uppercase tracking-widest">AccioAdmit</div>
                <button onClick={() => { setShowPayment(false); setPaymentError(null); }} className="text-blue-300 hover:text-white text-xl leading-none">×</button>
              </div>
              <div className="text-2xl font-bold mb-0.5">Your full MBA resume report</div>
              <div className="text-sm text-blue-200">Every role, every bullet, read the way an admissions officer actually reads them.</div>
            </div>

            <div className="px-6 py-4 bg-blue-50 border-b border-blue-100">
              <ul className="space-y-1.5">
                {['Every role and bullet scored, rewritten, and given MBA Lens commentary','Full Education and Additional Info section feedback, institution by institution','All 13 format checklist items with specific, actionable fixes','What AdCom will like, and where they will push back, before you submit'].map((line, i) => (
                  <li key={i} className="flex items-start gap-2 text-xs text-blue-900">
                    <span className="text-blue-500 mt-0.5 flex-shrink-0">✓</span>{line}
                  </li>
                ))}
              </ul>
            </div>

            <div className="px-6 py-4 border-b border-gray-100">
              <p className="text-xs text-gray-600 italic leading-relaxed mb-1.5">
                "AccioAdmit didn't just review my essays. They coached me through the entire mindset shift needed and shaped my narrative accordingly. I successfully got into Oxford Saïd thanks to them."
              </p>
              <p className="text-[11px] text-gray-400 font-semibold">— Joshua Anastasius, Admitted to Oxford Saïd</p>
            </div>

            <div className="px-6 py-6 space-y-3">
              <div>
                <label className="block text-xs font-semibold text-gray-600 mb-1">Phone number</label>
                <input
                  type="tel"
                  inputMode="numeric"
                  value={phone}
                  onChange={e => { setPhone(e.target.value); setPaymentError(null); }}
                  placeholder="10-digit mobile number"
                  maxLength={10}
                  disabled={paymentState === 'processing'}
                  className="w-full px-3 py-2.5 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-60"
                />
                <p className="text-[11px] text-gray-400 mt-1">Required by Cashfree to process your payment.</p>
              </div>
              {showCoupon ? (
                <div>
                  <label className="block text-xs font-semibold text-gray-600 mb-1">Coupon code</label>
                  <input
                    type="text"
                    value={couponCode}
                    onChange={e => { setCouponCode(e.target.value); setPaymentError(null); }}
                    placeholder="Enter code"
                    disabled={paymentState === 'processing'}
                    className="w-full px-3 py-2.5 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-60"
                  />
                </div>
              ) : (
                <button type="button" onClick={() => setShowCoupon(true)} className="text-[11px] text-gray-400 hover:text-gray-600 underline">
                  Have a coupon code?
                </button>
              )}
              <button
                onClick={handlePayment}
                disabled={paymentState === 'processing'}
                className="w-full py-3.5 rounded-xl text-sm font-bold bg-blue-600 hover:bg-blue-700 text-white transition disabled:opacity-60">
                {paymentState === 'processing' ? (
                  <span className="flex items-center justify-center gap-2">
                    <svg className="animate-spin h-4 w-4" viewBox="0 0 24 24" fill="none">
                      <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"/>
                      <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8z"/>
                    </svg>
                    Processing payment...
                  </span>
                ) : couponCode.trim() ? 'Unlock full report' : <>Pay <PriceTag /> — unlock full report</>}
              </button>
              {PRICING.isLaunchOffer && (
                <p className="text-center text-[11px] text-amber-600 font-medium">This price is available while we're in beta, through Oct 15.</p>
              )}
              {paymentError && (
                <p className="text-center text-xs text-red-600 leading-relaxed">{paymentError}</p>
              )}
              <p className="text-center text-[11px] text-gray-400">One-time payment via Cashfree (UPI, cards, netbanking). We never see your card details.</p>
            </div>
          </div>
        </div>
      ))}
      {showConfirmReset && <ConfirmResetModal />}
      {/* Header */}
      <div className="bg-gradient-to-r from-blue-700 to-blue-800 text-white px-4 sm:px-6 py-4 shadow-lg">
        <div className="max-w-4xl mx-auto flex items-center justify-between">
          <div className="flex items-center gap-3">
            <span className="text-yellow-400 text-xl">✨</span>
            <div>
              <div className="text-lg font-bold">Accio <span className="text-yellow-400">Admit</span></div>
              <div className="text-xs text-blue-300 hidden sm:block">MBA Resume Review Tool</div>
            </div>
          </div>
          <div className="flex items-center gap-2">
            <span className="bg-yellow-400 text-blue-900 text-xs font-bold px-2.5 py-1 rounded-full">BETA</span>
            <span className={`text-[10px] font-semibold px-2.5 py-1 rounded-full hidden sm:inline-block ${isPreview ? 'bg-white/10 text-blue-200' : 'bg-green-400/20 text-green-300'}`}>
              {isPreview ? 'Preview report' : 'Full report unlocked'}
            </span>
            <button onClick={() => { gtag_event('start_new_report_clicked'); setShowConfirmReset(true); }}
              className="text-xs text-blue-200 hover:text-white border border-blue-500 hover:border-blue-300 px-3 py-1.5 rounded-lg transition hidden sm:block">
              Start new report
            </button>
          </div>
        </div>
      </div>

      <div className="max-w-4xl mx-auto px-4 sm:px-6 py-6">

        {/* ── SAVED REPORT BANNER ── */}
        {isSavedReport && (
          <div className="mb-4 bg-blue-50 border border-blue-200 rounded-xl px-4 py-3 flex items-center gap-3">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" className="w-4 h-4 text-blue-500 flex-shrink-0"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
            <p className="text-sm text-blue-800">You are viewing your saved report. This link is private to you.</p>
          </div>
        )}
        {!isSavedReport && reportSaveState === 'saved' && reportUrl && (
          <div className="mb-4 bg-green-50 border border-green-200 rounded-xl px-4 py-3 flex items-center justify-between gap-3">
            <p className="text-sm text-green-800">
              Your report has been saved and a link sent to <strong data-clarity-mask="True">{email}</strong>.
              You can bookmark <a href={reportUrl} className="underline font-semibold">this report link</a> to come back any time.
            </p>
            <button
              onClick={() => { gtag_event('report_link_copied'); navigator.clipboard.writeText(window.location.origin + reportUrl); }}
              className="flex-shrink-0 text-xs font-semibold text-green-700 bg-green-100 hover:bg-green-200 px-3 py-1.5 rounded-lg transition">
              Copy link
            </button>
          </div>
        )}
        {!isSavedReport && reportSaveState === 'saving' && (
          <div className="mb-4 bg-gray-50 border border-gray-200 rounded-xl px-4 py-3">
            <p className="text-sm text-gray-500">Saving your report and sending the link to <span data-clarity-mask="True">{email}</span>…</p>
          </div>
        )}
        {!isSavedReport && reportSaveState === 'error' && (
          <div className="mb-4 bg-amber-50 border border-amber-200 rounded-xl px-4 py-3">
            <p className="text-sm text-amber-800">Your report couldn't be saved automatically. Please screenshot or bookmark this page before closing.</p>
          </div>
        )}

        {/* ── REPORT HEADER ── */}
        <div className="mb-6">
          <div className="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden">
            {/* Reviewer framing + thoughts */}
            <div className="px-6 pt-6 pb-5">
              <div className="flex items-center gap-2 mb-1">
                <span className="text-2xl">👋</span>
                <h1 className="text-2xl font-bold text-gray-900">Hi{candidateFirstName ? ` ${candidateFirstName}` : ' there'}.</h1>
              </div>
              <p className="text-base font-semibold text-gray-700 mb-4">You've done great work!</p>
              <p className="text-sm text-gray-500 leading-relaxed mb-1">
                {isPreview
                  ? `In this free preview, I've reviewed your${recentTitle ? ` ${recentTitle}` : ' most recent'} role${recentCompany ? ` at ${recentCompany}` : ''}.`
                  : "I've reviewed your full resume across work experience, format, education, additional information, and MBA fit."}
              </p>
              {targetSchools && (
                <p className="text-sm text-gray-500 leading-relaxed mb-1">You are targeting {targetSchools}. I have kept that in mind.</p>
              )}
              <p className="text-xs font-bold text-gray-400 uppercase tracking-widest mt-3 mb-2">My read</p>
              <p className="text-sm text-gray-800 leading-relaxed mb-4">{summaryCopyRaw}</p>
              {/* Credentials callout — preview only */}
              {isPreview && (
                <div className="bg-blue-50 border border-blue-100 rounded-lg px-4 py-3 space-y-1.5">
                  <p className="text-xs text-blue-900 font-semibold leading-relaxed">Your experience is definitely stronger than your resume currently shows.</p>
                  <p className="text-xs text-blue-800 leading-relaxed">I built this tool after reviewing thousands of MBA resumes and coaching applicants targeting top European MBA programs, so you can see the same gaps admissions committees notice.</p>
                </div>
              )}
            </div>

            {/* Free vs Paid — preview only */}
            {isPreview ? (
              <div className="grid grid-cols-1 sm:grid-cols-2 divide-y sm:divide-y-0 sm:divide-x divide-gray-100 border-t border-gray-100">
                {/* Free */}
                <div className="px-5 py-4">
                  <div className="flex items-center gap-2 mb-3">
                    <span className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest">Your free preview</span>
                    <span className="text-[10px] font-semibold bg-green-100 text-green-700 px-2 py-0.5 rounded-full uppercase tracking-wide">Free</span>
                  </div>
                  <div className="space-y-2.5">
                    {[
                      { title: 'Work Experience tab', desc: 'First 3 bullets in your most recent role scored and rewritten.' },
                      { title: 'Format and Structure tab', desc: 'Catch formatting issues before your content even lands.' },
                    ].map((item, i) => (
                      <div key={i} className="flex items-start gap-2">
                        <span className="text-green-500 flex-shrink-0 mt-0.5 text-xs font-bold">✓</span>
                        <div>
                          <p className="text-xs font-semibold text-gray-800">{item.title}</p>
                          <p className="text-xs text-gray-500 mt-0.5 leading-relaxed">{item.desc}</p>
                        </div>
                      </div>
                    ))}
                  </div>
                </div>
                {/* Paid */}
                <div className="px-5 py-4 bg-blue-50/30">
                  <div className="flex items-center gap-2 mb-3">
                    <span className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest">Full report</span>
                    <span className="text-[10px] font-semibold bg-blue-100 text-blue-700 px-2 py-0.5 rounded-full uppercase tracking-wide">Paid</span>
                  </div>
                  <div className="space-y-2">
                    {[
                      'Every role, every bullet scored and rewritten',
                      'Education section read institution by institution',
                      'MBA Lens: does your career arc lead to an MBA, or work against it?',
                    ].map((item, i) => (
                      <div key={i} className="flex items-start gap-2">
                        <span className="text-blue-400 flex-shrink-0 mt-0.5 text-xs font-bold">+</span>
                        <p className="text-xs text-gray-600 leading-relaxed">{item}</p>
                      </div>
                    ))}
                  </div>
                </div>
              </div>
            ) : (
              <div className="flex items-center gap-3 px-5 py-3 border-t border-gray-100 bg-green-50/40">
                <span className="text-green-500 text-base">✓</span>
                <p className="text-xs text-green-800 font-medium">Full report unlocked. Every role, bullet, education section, and additional info reviewed.</p>
              </div>
            )}

          </div>
        </div>

        {/* ── TABS ── */}
        <div className="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden">

          {/* Tab bar */}
          <div className="flex border-b border-gray-200 overflow-x-auto bg-gray-50">
            {TABS.map(tab => (
              <button
                key={tab.id}
                onClick={() => {
                  gtag_event(analysisData?._scope === 'preview' ? 'preview_tab_viewed' : 'full_report_tab_viewed', { tab: tab.id, locked: !!tab.locked });
                  if (tab.locked) gtag_event('paywall_tab_clicked', { tab: tab.id });
                  setActiveTab(tab.id);
                }}
                className={`flex items-center gap-1.5 -mb-px px-4 py-3 text-sm whitespace-nowrap transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-1
                  ${activeTab === tab.id
                    ? 'border-b-2 border-blue-600 text-blue-700 font-semibold bg-white'
                    : tab.locked
                      ? 'border-b-2 border-transparent text-gray-500 hover:text-gray-800 hover:bg-white cursor-pointer'
                      : 'border-b-2 border-transparent text-gray-500 hover:text-gray-800 hover:border-gray-300'
                  }`}
              >
                <span>{tab.label}</span>
                {tab.badge != null && (
                  <span className="ml-1 bg-amber-100 text-amber-700 text-[10px] font-bold px-1.5 py-0.5 rounded-full leading-none">{tab.badge}</span>
                )}
                {tab.locked && (
                  <span className="flex items-center gap-1 ml-1">
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" className="w-3 h-3 text-gray-400 flex-shrink-0" aria-hidden="true">
                      <path fillRule="evenodd" d="M8 1a3.5 3.5 0 0 0-3.5 3.5V6H4a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1h-.5V4.5A3.5 3.5 0 0 0 8 1zm2 5V4.5a2 2 0 1 0-4 0V6h4z" clipRule="evenodd" />
                    </svg>
                    <span className="bg-blue-50 text-blue-600 text-[10px] font-semibold px-1.5 py-0.5 rounded-full leading-none tracking-wide uppercase">Paid</span>
                  </span>
                )}
              </button>
            ))}
          </div>

          {/* Tab content */}
          <div className="p-5 sm:p-6">
            {activeTab === 'work'      && <WorkTab />}
            {activeTab === 'format'    && <FormatTab />}
            {activeTab === 'education' && (isPreview ? <EducationLockedTab /> : <EducationTab />)}
            {activeTab === 'addinfo'   && (isPreview ? <AdditionalInfoLockedTab /> : <AdditionalInfoTab />)}
            {activeTab === 'lens'      && (isPreview ? <LensLockedTab />               : <LensTab />)}
          </div>
        </div>

        {/* Note from the founder */}
        <div className="mt-8 bg-white rounded-2xl border border-gray-100 shadow-lg px-6 py-6 sm:px-8 sm:py-8">
          <div className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest mb-4">A note from the founder</div>
          <p className="text-sm text-gray-700 leading-relaxed mb-4 italic">
            I've reviewed thousands of resumes: for MBA applications, for hiring at startups, and pitch decks when helping founders raise. Somewhere in there I built a pretty specific instinct for what earns a spot on one page, what gets cut, and what needs to be pushed higher instead of buried in the middle.
          </p>
          <p className="text-sm text-gray-700 leading-relaxed mb-4 italic">
            These are usually small edits. But when someone is deciding whether to keep reading yours or move on to the next resume in the pile, small edits are the whole game. We've seen it firsthand: a handful of word changes can be the difference between an application that gets a second look and one that doesn't.
          </p>
          <p className="text-sm text-gray-700 leading-relaxed mb-6 italic">
            This tool is us trying to put that instinct into something you can use directly, without hiring us. We spent months building it, then broke it on purpose: our own resumes, our clients' resumes, our friends' resumes, and a stack of AI-generated ones, before we trusted it enough to ship. Use it to see how your bullets actually read to the person deciding your future, and how to say the same things better.
          </p>
          <div className="flex items-center gap-4 mb-5">
            <img
              src="/assets/founder/tanvi-singla.jpg"
              alt="Tanvi Singla"
              className="w-20 h-20 rounded-full object-cover flex-shrink-0 bg-blue-100"
              onError={e => { e.target.style.display = 'none'; e.target.nextSibling.style.display = 'flex'; }}
            />
            <div className="w-20 h-20 rounded-full bg-blue-100 text-blue-700 font-bold text-xl flex-shrink-0 items-center justify-center" style={{ display: 'none' }}>TS</div>
            <div>
              <p className="text-sm font-semibold text-gray-900">Tanvi Singla</p>
              <p className="text-xs text-gray-500">Co-founder, AccioAdmit &middot; Oxford Saïd MBA &middot; admitted to Cambridge Judge and Cornell &middot; 5+ years across VC investing, startup incubation, and entrepreneurship</p>
            </div>
          </div>
          <p className="text-xs text-gray-400 border-t border-gray-100 pt-4">
            We're always open to feedback on this tool. Reach us on <a href="https://www.linkedin.com/in/tanvisingla/" target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:underline">LinkedIn</a> or at <a href="mailto:workwithaccioadmit@gmail.com" className="text-blue-600 hover:underline">workwithaccioadmit@gmail.com</a>.
          </p>
        </div>

        <div className="mt-8">
          <StrategyCTA />
        </div>

        {/* Footer */}
        <div className="mt-8 border-t border-gray-200 pt-6">
          <div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-6">
            {/* Brand + blurb */}
            <div className="max-w-xs">
              <div className="flex items-center gap-2 mb-2">
                <span className="text-yellow-400">✨</span>
                <span className="font-bold text-gray-800">Accio<span className="text-yellow-500">Admit</span></span>
              </div>
              <p className="text-xs text-gray-500 leading-relaxed">AccioAdmit is an MBA admissions consultancy helping applicants from India and emerging markets get into top European business schools. Founded by Tanvi (Oxford Said, 2022) and Sanath (IE Business School).</p>
            </div>
            {/* Links */}
            <div className="flex flex-col gap-2 text-sm">
              <a href="https://accioadmit.com" target="_blank" rel="noopener noreferrer" onClick={() => gtag_event('footer_cta_clicked', { cta: 'services' })} className="text-blue-600 hover:underline">Our Services</a>
              <a href="https://accios-newsletter.beehiiv.com/" target="_blank" rel="noopener noreferrer" onClick={() => gtag_event('footer_cta_clicked', { cta: 'newsletter' })} className="text-blue-600 hover:underline">Free Weekly Guide</a>
              <a href="https://cal.com/accio-admit/30min" target="_blank" rel="noopener noreferrer" onClick={() => gtag_event('footer_cta_clicked', { cta: 'chat' })} className="text-blue-600 hover:underline">Chat with Us</a>
              <a href="/privacy.html" className="text-blue-600 hover:underline">Privacy Policy</a>
              <a href="/terms.html" className="text-blue-600 hover:underline">Terms &amp; Conditions</a>
              <a href="/refund-policy.html" className="text-blue-600 hover:underline">Refunds &amp; Cancellations</a>
              <a href="/contact.html" className="text-blue-600 hover:underline">Contact Us</a>
            </div>
            {/* CTA */}
            <button onClick={() => { gtag_event('start_new_report_clicked'); setShowConfirmReset(true); }} className="self-start sm:self-center bg-blue-600 text-white px-4 py-2 rounded-lg text-sm font-semibold hover:bg-blue-700 transition">
              Start new report
            </button>
          </div>
          <p className="text-xs text-gray-400 mt-6 text-center">© 2026 AccioAdmit. All rights reserved. · MBA Resume Tool (BETA)</p>
        </div>

      </div>
    </div>
  );
  } // end results block

  return null;
}


// ── SAVED REPORT VIEW ──
// Handles /report/:token — loads report JSON from server and renders the full paid report.
// No AI is called; the report is served from Supabase.
function SavedReportView({ token }) {
  const [state, setState] = React.useState('loading'); // loading | loaded | not-found | expired | error
  const [reportJson, setReportJson] = React.useState(null);
  const [errorMsg, setErrorMsg] = React.useState('');

  React.useEffect(() => {
    fetch(`/api/get-saved-report?token=${encodeURIComponent(token)}`)
      .then(async res => {
        if (res.status === 404) { setState('not-found'); return; }
        if (res.status === 410) { setState('expired'); return; }
        if (!res.ok) { setErrorMsg('Something went wrong. Please try again.'); setState('error'); return; }
        const data = await res.json();
        setReportJson(data.reportJson);
        setState('loaded');
      })
      .catch(() => { setErrorMsg('Could not load your report. Check your connection and try again.'); setState('error'); });
  }, [token]);

  if (state === 'loading') {
    return React.createElement('div', { style: { minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: 'sans-serif', color: '#6b7280' } },
      React.createElement('p', null, 'Loading your report…')
    );
  }
  if (state === 'not-found') {
    return React.createElement('div', { style: { minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', fontFamily: 'sans-serif', color: '#374151', gap: '12px' } },
      React.createElement('p', { style: { fontSize: '18px', fontWeight: '700' } }, 'Report not found.'),
      React.createElement('p', { style: { color: '#6b7280', fontSize: '14px' } }, 'This link may be invalid. Please check the URL or contact AccioAdmit.')
    );
  }
  if (state === 'expired') {
    return React.createElement('div', { style: { minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', fontFamily: 'sans-serif', color: '#374151', gap: '12px' } },
      React.createElement('p', { style: { fontSize: '18px', fontWeight: '700' } }, 'This report link has expired.'),
      React.createElement('p', { style: { color: '#6b7280', fontSize: '14px' } }, 'Please contact AccioAdmit if you need help accessing your report.')
    );
  }
  if (state === 'error') {
    return React.createElement('div', { style: { minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', fontFamily: 'sans-serif', color: '#374151', gap: '12px' } },
      React.createElement('p', { style: { fontSize: '18px', fontWeight: '700' } }, 'Something went wrong.'),
      React.createElement('p', { style: { color: '#6b7280', fontSize: '14px' } }, errorMsg)
    );
  }
  // Render the full paid report using the existing ResumeReviewer component
  return React.createElement(ResumeReviewer, { initialAnalysisData: reportJson, initialStage: 'results', isSavedReport: true });
}

// ── MOUNT ──
const savedReportMatch = window.location.pathname.match(/^\/report\/([A-Za-z0-9_-]{10,})$/);
if (savedReportMatch) {
  ReactDOM.createRoot(document.getElementById('root')).render(
    React.createElement(SavedReportView, { token: savedReportMatch[1] })
  );
} else {
  ReactDOM.createRoot(document.getElementById('root')).render(React.createElement(ResumeReviewer));
}
