// Mahaan Consulting — free-build qualification application.
// ---------------------------------------------------------------------------
// Standalone, business-facing application form. Reuses the EXISTING brand
// design system from brand.jsx (BRAND tokens, Eyebrow, DotGrid, PerspectiveGrid)
// so it matches the rest of the project pixel-for-pixel — same near-black base,
// coral accent, Archivo Black / DM Serif Display / Plus Jakarta Sans / JetBrains
// Mono type stack, same field + button + panel styling.
//
// Deliberately NO mascot and NO Skool/community framing — this is for potential
// free-work clients, not Claude Code users. Clean, minimal, premium.
//
// Flow: 11 questions grouped into 6 short steps with per-step validation and a
// branded confirmation screen. Submissions POST to a Google Apps Script web app
// that appends one row per submission to a Google Sheet (one column per field).

// BRAND, Eyebrow, DotGrid and PerspectiveGrid are declared globally by brand.jsx
// (every in-browser-Babel script shares one global scope), so we reference them
// directly here — re-declaring them would collide and halt this whole file.

// ── Where submissions go ───────────────────────────────────────────────────
// Paste your deployed Google Apps Script web-app URL here (ends in /exec).
// See the setup steps shared alongside this file. Until it's set, the form runs
// in PREVIEW mode: it validates + shows the confirmation but logs the payload to
// the console instead of writing to the Sheet (so you can demo it immediately).
const SHEETS_ENDPOINT = 'https://script.google.com/macros/s/AKfycbxHUsCe46EZoh5mzPb9MWDy3pOKkBfjirxUbUOv97SpZ0WAsQPGfdso1bWIojZyrJgaRg/exec'; // deployed Google Apps Script web app (/exec)

// ── Responsive hook ────────────────────────────────────────────────────────
function useIsMobile(breakpoint = 820) {
  const [isMobile, setIsMobile] = React.useState(() =>
    typeof window !== 'undefined' ? window.innerWidth < breakpoint : false
  );
  React.useEffect(() => {
    const mql = window.matchMedia(`(max-width: ${breakpoint - 1}px)`);
    const onChange = e => setIsMobile(e.matches);
    setIsMobile(mql.matches);
    if (mql.addEventListener) mql.addEventListener('change', onChange);
    else mql.addListener(onChange);
    return () => {
      if (mql.removeEventListener) mql.removeEventListener('change', onChange);
      else mql.removeListener(onChange);
    };
  }, [breakpoint]);
  return isMobile;
}

// ── Email validation (same approach as the waitlist form) ──────────────────
// WHATWG syntax via a cached hidden <input type="email">, then reject the
// edge cases the spec allows but real signups never use (single-label domains,
// 1-char TLDs). Plus a Levenshtein typo-suggester for common providers.
let _emailValidatorInput = null;
function isValidEmailFormat(email) {
  if (typeof document === 'undefined') return /^\S+@\S+\.\S+$/.test(email);
  if (!_emailValidatorInput) {
    _emailValidatorInput = document.createElement('input');
    _emailValidatorInput.type = 'email';
  }
  _emailValidatorInput.value = email;
  return _emailValidatorInput.checkValidity();
}
function validateEmail(email) {
  const trimmed = (email || '').trim();
  if (!trimmed) return { ok: false, reason: 'Email is required' };
  if (trimmed.length > 254) return { ok: false, reason: 'That email looks too long' };
  if (!isValidEmailFormat(trimmed)) return { ok: false, reason: 'Enter a valid email address' };
  const at = trimmed.lastIndexOf('@');
  const domain = trimmed.slice(at + 1);
  const lastDot = domain.lastIndexOf('.');
  if (lastDot < 1 || domain.length - lastDot < 3) {
    return { ok: false, reason: 'Enter a valid email address' };
  }
  // Real TLDs are alphabetic — rejects random-looking domains like "foo@bar.123".
  if (!/^[a-zA-Z]{2,}$/.test(domain.slice(lastDot + 1))) {
    return { ok: false, reason: 'Enter a valid email address' };
  }
  return { ok: true };
}
const COMMON_EMAIL_DOMAINS = [
  'gmail.com', 'googlemail.com', 'yahoo.com', 'yahoo.co.uk', 'ymail.com',
  'outlook.com', 'outlook.co.uk', 'hotmail.com', 'hotmail.co.uk', 'live.com',
  'icloud.com', 'me.com', 'aol.com', 'proton.me', 'protonmail.com',
  'btinternet.com', 'sky.com', 'virginmedia.com', 'gmx.com',
];
function levenshtein(a, b) {
  if (a === b) return 0;
  const m = a.length, n = b.length;
  if (!m) return n;
  if (!n) return m;
  let prev = new Array(n + 1), curr = new Array(n + 1);
  for (let j = 0; j <= n; j++) prev[j] = j;
  for (let i = 1; i <= m; i++) {
    curr[0] = i;
    for (let j = 1; j <= n; j++) {
      const cost = a[i - 1] === b[j - 1] ? 0 : 1;
      curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
    }
    [prev, curr] = [curr, prev];
  }
  return prev[n];
}
function suggestEmailFix(email) {
  const trimmed = (email || '').trim().toLowerCase();
  const at = trimmed.lastIndexOf('@');
  if (at < 1 || at >= trimmed.length - 1) return null;
  const local = trimmed.slice(0, at);
  const domain = trimmed.slice(at + 1);
  if (!domain || COMMON_EMAIL_DOMAINS.includes(domain)) return null;
  let best = null;
  let bestDist = Math.min(3, Math.max(2, Math.floor(domain.length / 4)));
  for (const d of COMMON_EMAIL_DOMAINS) {
    if (Math.abs(d.length - domain.length) > 3) continue;
    const dist = levenshtein(domain, d);
    if (dist > 0 && dist <= bestDist) { bestDist = dist; best = d; }
  }
  return best ? `${local}@${best}` : null;
}

// ── Phone validation (lazy-loaded libphonenumber-js) ───────────────────────
// Same approach the waitlist used: load ~22-28 kB gzipped from esm.sh, but only
// on first phone-field focus, so first paint is unaffected.
let _phoneLibPromise = null;
function loadPhoneLib() {
  if (typeof window === 'undefined') return Promise.resolve(null);
  if (!_phoneLibPromise) {
    _phoneLibPromise = import('https://esm.sh/libphonenumber-js@1.11.20/min')
      .catch(err => {
        console.warn('Failed to load libphonenumber-js:', err);
        _phoneLibPromise = null;
        return null;
      });
  }
  return _phoneLibPromise;
}

// Catches obvious junk regardless of whether the library loaded: all-same-digit
// ("0000000000") and pure sequential ("1234567890" / "9876543210"). A real
// signup never types these, but libphonenumber accepts some in permissive
// countries, so we pre-filter.
function looksLikeJunkPhone(numStr) {
  const digits = (numStr || '').replace(/\D/g, '');
  if (digits.length < 6) return true;
  if (/^(\d)\1+$/.test(digits)) return true;
  let asc = true, desc = true;
  for (let i = 1; i < digits.length; i++) {
    const d = (+digits[i] - +digits[i - 1] + 10) % 10;
    if (d !== 1) asc = false;
    if (d !== 9) desc = false;
  }
  return asc || desc;
}

async function validatePhoneAsync(num, iso) {
  const trimmed = (num || '').trim();
  if (!trimmed) return { ok: false, reason: 'Phone number is required' };
  const digits = (trimmed.match(/\d/g) || []).length;
  if (digits < 6) return { ok: false, reason: 'Enter a valid phone number' };
  if (looksLikeJunkPhone(trimmed)) return { ok: false, reason: 'Enter a valid phone number' };
  const lib = await loadPhoneLib();
  if (!lib) {
    // Library blocked/failed to load. Conservative fallback: require 8+ digits.
    return digits >= 8 ? { ok: true } : { ok: false, reason: 'Enter a valid phone number' };
  }
  try {
    const parser = lib.parsePhoneNumberFromString || (lib.default && lib.default.parsePhoneNumberFromString);
    if (!parser) return digits >= 8 ? { ok: true } : { ok: false, reason: 'Enter a valid phone number' };
    const parsed = parser(trimmed, iso || 'US');
    if (parsed && parsed.isValid()) return { ok: true };
    return { ok: false, reason: 'Enter a valid phone number for this country' };
  } catch (e) {
    return digits >= 8 ? { ok: true } : { ok: false, reason: 'Enter a valid phone number' };
  }
}

const COUNTRIES = [
  { code: '+1',   iso: 'US', label: '+1 US/CA' },
  { code: '+44',  iso: 'GB', label: '+44 UK' },
  { code: '+61',  iso: 'AU', label: '+61 Australia' },
  { code: '+64',  iso: 'NZ', label: '+64 New Zealand' },
  { code: '+353', iso: 'IE', label: '+353 Ireland' },
  { code: '+33',  iso: 'FR', label: '+33 France' },
  { code: '+49',  iso: 'DE', label: '+49 Germany' },
  { code: '+34',  iso: 'ES', label: '+34 Spain' },
  { code: '+39',  iso: 'IT', label: '+39 Italy' },
  { code: '+31',  iso: 'NL', label: '+31 Netherlands' },
  { code: '+351', iso: 'PT', label: '+351 Portugal' },
  { code: '+41',  iso: 'CH', label: '+41 Switzerland' },
  { code: '+43',  iso: 'AT', label: '+43 Austria' },
  { code: '+32',  iso: 'BE', label: '+32 Belgium' },
  { code: '+46',  iso: 'SE', label: '+46 Sweden' },
  { code: '+47',  iso: 'NO', label: '+47 Norway' },
  { code: '+45',  iso: 'DK', label: '+45 Denmark' },
  { code: '+358', iso: 'FI', label: '+358 Finland' },
  { code: '+48',  iso: 'PL', label: '+48 Poland' },
  { code: '+91',  iso: 'IN', label: '+91 India' },
  { code: '+971', iso: 'AE', label: '+971 UAE' },
  { code: '+966', iso: 'SA', label: '+966 Saudi Arabia' },
  { code: '+972', iso: 'IL', label: '+972 Israel' },
  { code: '+27',  iso: 'ZA', label: '+27 South Africa' },
  { code: '+234', iso: 'NG', label: '+234 Nigeria' },
  { code: '+254', iso: 'KE', label: '+254 Kenya' },
  { code: '+65',  iso: 'SG', label: '+65 Singapore' },
  { code: '+852', iso: 'HK', label: '+852 Hong Kong' },
  { code: '+60',  iso: 'MY', label: '+60 Malaysia' },
  { code: '+62',  iso: 'ID', label: '+62 Indonesia' },
  { code: '+63',  iso: 'PH', label: '+63 Philippines' },
  { code: '+66',  iso: 'TH', label: '+66 Thailand' },
  { code: '+84',  iso: 'VN', label: '+84 Vietnam' },
  { code: '+81',  iso: 'JP', label: '+81 Japan' },
  { code: '+82',  iso: 'KR', label: '+82 South Korea' },
  { code: '+86',  iso: 'CN', label: '+86 China' },
  { code: '+886', iso: 'TW', label: '+886 Taiwan' },
  { code: '+52',  iso: 'MX', label: '+52 Mexico' },
  { code: '+55',  iso: 'BR', label: '+55 Brazil' },
  { code: '+54',  iso: 'AR', label: '+54 Argentina' },
  { code: '+56',  iso: 'CL', label: '+56 Chile' },
  { code: '+57',  iso: 'CO', label: '+57 Colombia' },
];

// Parse a stored "+CC USER_NUMBER" string back into {dial, iso, num} so the
// field restores after a remount (rotation across the breakpoint, or the
// sessionStorage rehydration we just added).
function parseInitialPhone(value) {
  if (typeof value !== 'string' || !value) return { dial: '+1', iso: 'US', num: '' };
  const sp = value.indexOf(' ');
  if (sp <= 0) return { dial: '+1', iso: 'US', num: value.trim() };
  const d = value.slice(0, sp).trim();
  const found = COUNTRIES.find(c => c.code === d);
  if (found) return { dial: found.code, iso: found.iso, num: value.slice(sp + 1).trim() };
  return { dial: '+1', iso: 'US', num: value.trim() };
}

// ── Submission ─────────────────────────────────────────────────────────────
// Apps Script web apps can't answer a CORS preflight, so we send a "simple"
// request: no custom headers, which keeps Content-Type at text/plain and skips
// preflight entirely. The /exec endpoint 302-redirects to a googleusercontent
// URL that returns the JSON body with permissive CORS, so we can still read it.
async function submitApplication(payload) {
  if (!SHEETS_ENDPOINT) {
    console.log('[Mahaan Consulting · preview mode] application payload:', payload);
    await new Promise(r => setTimeout(r, 650));
    return { result: 'preview' };
  }
  const res = await fetch(SHEETS_ENDPOINT, {
    method: 'POST',
    body: JSON.stringify(payload),
  });
  if (!res.ok) {
    const body = await res.text().catch(() => '');
    throw new Error(`Submit failed (${res.status}): ${body}`);
  }
  return res.json().catch(() => ({ result: 'success' }));
}

// ── Question option sets ───────────────────────────────────────────────────
const REVENUE_OPTIONS   = ['£0 / pre-revenue', '£1 to £3k', '£3k to £7k', '£7k to £10k', '£10k+'];
const COMMIT_OPTIONS    = ['Yes', 'No', 'Not sure'];
const TEAM_OPTIONS      = ['Just me', 'Me + a small team', 'Me + a larger team'];
const AI_LEVEL_OPTIONS  = [
  "New to it, haven't really used AI tools yet",
  'Basic, use simple tools like ChatGPT now and then',
  'Some experience, use AI regularly and have tried building a few things',
  'Very comfortable, build systems and use tools like Claude Code, Codex, etc.',
];
const INTERVIEW_OPTIONS = ['Yes', 'No', 'Maybe'];
const DECISION_OPTIONS  = ["It's me", 'Someone else'];
const START_OPTIONS     = ['Right now', 'Within the next 2 weeks', 'Within a month', 'Later than that'];

// ───────────────────────────────────────────────────────────────────────────
// Shared atoms — styled to match brand.jsx / direction-blueprint.jsx exactly
// ───────────────────────────────────────────────────────────────────────────
// Field/question labels use plain sentence-case body text (same as the
// multiple-choice questions) so long questions stay readable — all-caps mono
// looks slick on a 2-word label but is hard to read on a full sentence.
function FieldLabel({ children }) {
  return (
    <div style={{
      fontFamily: BRAND.body, fontSize: 15, color: BRAND.ink,
      marginBottom: 10, lineHeight: 1.4,
    }}>{children}</div>
  );
}

const fieldBase = {
  width: '100%', boxSizing: 'border-box',
  background: '#0a0a0a',
  borderRadius: 6,
  color: BRAND.ink,
  // 16px is deliberate: iOS Safari auto-zooms when focusing any text field
  // smaller than 16px. Keeping inputs at 16 avoids that jarring zoom on mobile.
  fontFamily: BRAND.body, fontSize: 16,
  outline: 'none',
  transition: 'border-color .12s',
};

function QInput({ label, optional, type = 'text', value, onChange, placeholder, autoComplete, maxLength, error, suggestion, onAcceptSuggestion }) {
  const [touched, setTouched] = React.useState(false);
  const showError = touched && !!error;
  const showSuggestion = touched && !error && !!suggestion;
  return (
    <label style={{ display: 'block' }}>
      <FieldLabel>{label}{optional && <span style={{ color: BRAND.inkFaint }}> (optional)</span>}</FieldLabel>
      <input
        type={type} value={value} onChange={onChange} placeholder={placeholder}
        autoComplete={autoComplete} maxLength={maxLength}
        style={{ ...fieldBase, padding: '14px 14px', border: `1px solid ${showError ? BRAND.accent : BRAND.ruleHi}` }}
        onFocus={e => { e.target.style.borderColor = BRAND.accent; }}
        onBlur={e => { setTouched(true); e.target.style.borderColor = showError || !!error ? BRAND.accent : BRAND.ruleHi; }}
      />
      {showError && <FieldMsg color={BRAND.accent}>{error}</FieldMsg>}
      {showSuggestion && (
        <FieldMsg color={BRAND.inkDim}>
          Did you mean{' '}
          <button type="button" onClick={onAcceptSuggestion} style={{
            background: 'transparent', border: 'none', padding: 0, color: BRAND.accent,
            fontFamily: BRAND.body, fontSize: 12, textDecoration: 'underline', cursor: 'pointer',
          }}>{suggestion}</button>?
        </FieldMsg>
      )}
    </label>
  );
}

function QTextArea({ label, optional, value, onChange, placeholder, rows = 4, maxLength = 1200 }) {
  const [focus, setFocus] = React.useState(false);
  return (
    <label style={{ display: 'block' }}>
      <FieldLabel>{label}{optional && <span style={{ color: BRAND.inkFaint }}> (optional)</span>}</FieldLabel>
      <textarea
        value={value} onChange={onChange} placeholder={placeholder} rows={rows} maxLength={maxLength}
        onFocus={() => setFocus(true)} onBlur={() => setFocus(false)}
        style={{
          ...fieldBase, padding: '13px 14px', resize: 'vertical', lineHeight: 1.5,
          minHeight: 96, border: `1px solid ${focus ? BRAND.accent : BRAND.ruleHi}`,
        }}
      />
    </label>
  );
}

function FieldMsg({ color, children }) {
  return (
    <div style={{ marginTop: 6, fontFamily: BRAND.body, fontSize: 12, color, lineHeight: 1.4 }}>{children}</div>
  );
}

// Single-select radio cards (matches the BPQuestion look). When `revealOn`
// matches the selection, an extra free-text input appears (Q7 "someone else").
function QChoice({ question, hint, options, value, onSelect, compact, columns, revealOn, revealPlaceholder, revealValue, onRevealChange }) {
  // On mobile everything is a single full-width column — bigger tap targets,
  // easier thumb reach, and no cramped/wrapping labels. Desktop keeps the grid.
  const gridCols = compact ? '1fr' : (columns === 1 ? '1fr' : `repeat(${columns || 2}, minmax(0, 1fr))`);
  return (
    <div>
      <div style={{ fontFamily: BRAND.body, fontSize: 15, color: BRAND.ink, marginBottom: hint ? 4 : 12, lineHeight: 1.4 }}>
        {question}
      </div>
      {hint && (
        <div style={{ fontFamily: BRAND.body, fontSize: 13, color: BRAND.inkDim, marginBottom: 12, lineHeight: 1.45 }}>{hint}</div>
      )}
      <div style={{ display: 'grid', gap: compact ? 10 : 8, gridTemplateColumns: gridCols }}>
        {options.map(opt => {
          const selected = value === opt;
          return (
            <button
              key={opt} type="button" role="radio" aria-checked={selected}
              onClick={() => onSelect(opt)}
              style={{
                display: 'flex', alignItems: 'center', gap: 11,
                padding: compact ? '14px 15px' : '12px 13px',
                minHeight: compact ? 52 : 'auto',
                background: selected ? BRAND.accentSoft : '#0a0a0a',
                border: `1px solid ${selected ? BRAND.accent : BRAND.ruleHi}`,
                borderRadius: 8, cursor: 'pointer',
                color: selected ? BRAND.accent : BRAND.ink,
                fontFamily: BRAND.body, fontSize: compact ? 15 : 13.5, textAlign: 'left', lineHeight: 1.35,
                transition: 'background .12s, border-color .12s, color .12s',
              }}
            >
              <span style={{
                width: 15, height: 15, borderRadius: 8, flexShrink: 0,
                border: `1.5px solid ${selected ? BRAND.accent : BRAND.ruleHi}`,
                background: selected ? BRAND.accent : 'transparent',
                boxShadow: selected ? `inset 0 0 0 3px ${BRAND.bgPanel}` : 'none',
                transition: 'all .12s',
              }} />
              <span>{opt}</span>
            </button>
          );
        })}
      </div>
      {revealOn != null && value === revealOn && (
        <input
          type="text" placeholder={revealPlaceholder} value={revealValue} onChange={e => onRevealChange(e.target.value)}
          style={{ ...fieldBase, marginTop: 10, padding: '13px 14px', border: `1px solid ${BRAND.accent}` }}
        />
      )}
    </div>
  );
}

// International phone field: searchable country-code selector + real
// libphonenumber validation (lazy-loaded on first focus). Adapted from the
// waitlist's BPPhoneField to this form's field styling. Emits the full
// "+CC number" string via onChange and async validity via onValidChange.
function QPhone({ label, initialValue, onChange, onValidChange }) {
  const initial = React.useMemo(() => parseInitialPhone(initialValue), []);
  const [dial, setDial] = React.useState(initial.dial);
  const [iso, setIso] = React.useState(initial.iso);
  const [num, setNum] = React.useState(initial.num);
  const [focused, setFocused] = React.useState(false);
  const [open, setOpen] = React.useState(false);
  const [query, setQuery] = React.useState('');
  const [touched, setTouched] = React.useState(false);
  const [error, setError] = React.useState(null);
  const wrapRef = React.useRef(null);
  const searchRef = React.useRef(null);
  const libLoadedRef = React.useRef(false);
  const validateIdRef = React.useRef(0);

  React.useEffect(() => {
    if (!open) { setQuery(''); return; }
    const onDoc = e => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
    const onKey = e => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    document.addEventListener('keydown', onKey);
    const t = setTimeout(() => searchRef.current && searchRef.current.focus(), 30);
    return () => {
      document.removeEventListener('mousedown', onDoc);
      document.removeEventListener('keydown', onKey);
      clearTimeout(t);
    };
  }, [open]);

  // Re-validate when the number or country changes. A stale-id guard stops a
  // fast typer from flashing an old result when the async lib resolves late.
  React.useEffect(() => {
    const trimmed = num.trim();
    const myId = ++validateIdRef.current;
    if (!trimmed) { setError(null); onValidChange && onValidChange(false); return; }
    validatePhoneAsync(trimmed, iso).then(result => {
      if (myId !== validateIdRef.current) return;
      setError(result.ok ? null : result.reason);
      onValidChange && onValidChange(result.ok);
    });
  }, [num, iso, onValidChange]);

  const triggerLibLoad = () => {
    if (!libLoadedRef.current) { libLoadedRef.current = true; loadPhoneLib(); }
  };

  const emit = (d, n) => onChange && onChange({ target: { value: n ? `${d} ${n}`.trim() : '' } });
  const placeholder = dial === '+1' ? '(555) 123-4567' : 'Phone number';
  const qy = query.trim().toLowerCase();
  const filtered = qy ? COUNTRIES.filter(c => c.label.toLowerCase().includes(qy)) : COUNTRIES;
  const showError = touched && !!error;
  const wrapperBorder = showError ? BRAND.accent : (focused || open) ? BRAND.accent : BRAND.ruleHi;

  return (
    <label style={{ display: 'block', position: 'relative' }} ref={wrapRef}>
      <FieldLabel>{label}</FieldLabel>
      <div style={{
        display: 'flex', alignItems: 'stretch', background: '#0a0a0a',
        border: `1px solid ${wrapperBorder}`, borderRadius: 6, overflow: 'hidden',
        transition: 'border-color .12s',
      }}>
        <button
          type="button"
          onClick={() => { triggerLibLoad(); setOpen(o => !o); }}
          onFocus={() => { setFocused(true); triggerLibLoad(); }}
          onBlur={() => setFocused(false)}
          aria-haspopup="listbox" aria-expanded={open} aria-label="Select country code"
          style={{
            display: 'inline-flex', alignItems: 'center', gap: 8, background: 'transparent',
            color: BRAND.ink, fontFamily: BRAND.body, fontSize: 16, border: 'none',
            borderRight: `1px solid ${BRAND.ruleHi}`, padding: '14px 12px 14px 14px',
            outline: 'none', cursor: 'pointer', whiteSpace: 'nowrap',
          }}>
          <span>{dial}</span>
          <svg width="10" height="6" viewBox="0 0 10 6" aria-hidden="true" style={{ transform: open ? 'rotate(180deg)' : 'none', transition: 'transform .15s', flexShrink: 0 }}>
            <path d="M1 1l4 4 4-4" stroke={BRAND.accent} strokeWidth="1.5" fill="none" strokeLinecap="round" strokeLinejoin="round" />
          </svg>
        </button>
        <input
          type="tel" inputMode="tel" autoComplete="tel-national" placeholder={placeholder} value={num}
          onChange={e => {
            const val = e.target.value;
            // Auto-sync the dial dropdown when a "+CC" prefix is typed/pasted.
            // Longest match first so "+1" doesn't claim "+44".
            if (val.startsWith('+')) {
              const sorted = [...COUNTRIES].sort((a, b) => b.code.length - a.code.length);
              for (const c of sorted) {
                if (val.startsWith(c.code) && (val.length === c.code.length || /[\s\d]/.test(val[c.code.length]))) {
                  const stripped = val.slice(c.code.length).trim();
                  setDial(c.code); setIso(c.iso); setNum(stripped); emit(c.code, stripped);
                  return;
                }
              }
            }
            setNum(val); emit(dial, val);
          }}
          onFocus={() => { setFocused(true); triggerLibLoad(); }}
          onBlur={() => { setFocused(false); setTouched(true); }}
          style={{
            flex: 1, minWidth: 0, background: 'transparent', color: BRAND.ink,
            fontFamily: BRAND.body, fontSize: 16, border: 'none', padding: '14px 14px', outline: 'none',
          }}
        />
      </div>
      {showError && <FieldMsg color={BRAND.accent}>{error}</FieldMsg>}
      {open && (
        <div role="listbox" style={{
          position: 'absolute', top: 'calc(100% + 6px)', left: 0, width: '100%', minWidth: 260,
          background: BRAND.bgRaise, border: `1px solid ${BRAND.ruleHi}`, borderRadius: 8,
          boxShadow: `0 24px 48px -16px rgba(0,0,0,0.7), 0 0 0 1px ${BRAND.rule}`, zIndex: 60, overflow: 'hidden',
        }}>
          <div style={{ padding: 8, borderBottom: `1px solid ${BRAND.rule}`, background: BRAND.bgPanel }}>
            <input
              ref={searchRef} type="text" placeholder="Search country or code" value={query}
              onChange={e => setQuery(e.target.value)}
              style={{
                width: '100%', boxSizing: 'border-box', background: '#0a0a0a', color: BRAND.ink,
                fontFamily: BRAND.body, fontSize: 16, border: `1px solid ${BRAND.ruleHi}`,
                borderRadius: 4, padding: '8px 10px', outline: 'none',
              }}
              onFocus={e => e.target.style.borderColor = BRAND.accent}
              onBlur={e => e.target.style.borderColor = BRAND.ruleHi}
            />
          </div>
          <div style={{ maxHeight: 240, overflowY: 'auto' }}>
            {filtered.length === 0 ? (
              <div style={{ padding: '14px 14px', fontFamily: BRAND.body, fontSize: 13, color: BRAND.inkFaint }}>No matches</div>
            ) : filtered.map(c => {
              const isSelected = c.code === dial && c.iso === iso;
              return (
                <button
                  key={c.label} type="button" role="option" aria-selected={isSelected}
                  onClick={() => { setDial(c.code); setIso(c.iso || 'US'); setOpen(false); emit(c.code, num); }}
                  onMouseEnter={e => { if (!isSelected) e.currentTarget.style.background = 'rgba(255,255,255,0.04)'; }}
                  onMouseLeave={e => { if (!isSelected) e.currentTarget.style.background = 'transparent'; }}
                  style={{
                    width: '100%', display: 'flex', alignItems: 'center', gap: 12,
                    background: isSelected ? BRAND.accentSoft : 'transparent', border: 'none',
                    color: isSelected ? BRAND.accent : BRAND.ink, fontFamily: BRAND.body, fontSize: 14,
                    padding: '10px 14px', textAlign: 'left', cursor: 'pointer',
                  }}>
                  <span style={{ fontFamily: BRAND.mono, fontSize: 12, color: isSelected ? BRAND.accent : BRAND.inkDim, minWidth: 44 }}>{c.code}</span>
                  <span style={{ flex: 1 }}>{c.label.replace(c.code, '').trim()}</span>
                  {isSelected && (
                    <svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true">
                      <path d="M2 6l3 3 5-6" stroke={BRAND.accent} strokeWidth="1.8" fill="none" strokeLinecap="round" strokeLinejoin="round" />
                    </svg>
                  )}
                </button>
              );
            })}
          </div>
        </div>
      )}
    </label>
  );
}

function CTAButton({ children, full, disabled, type }) {
  const baseShadow = `0 0 0 1px ${BRAND.accentDim}, 0 18px 40px -16px ${BRAND.accentGlow}`;
  const hoverShadow = `0 0 0 1px ${BRAND.accentDim}, 0 22px 50px -14px ${BRAND.accentGlow}, 0 8px 24px -12px ${BRAND.accentGlow}`;
  return (
    <button type={type || 'submit'} disabled={disabled}
      onMouseEnter={e => {
        if (disabled) return;
        e.currentTarget.style.transform = 'translateY(-1px)';
        e.currentTarget.style.boxShadow = hoverShadow;
        const a = e.currentTarget.querySelector('[data-arrow]'); if (a) a.style.transform = 'translateX(3px)';
      }}
      onMouseLeave={e => {
        e.currentTarget.style.transform = 'translateY(0)';
        e.currentTarget.style.boxShadow = baseShadow;
        const a = e.currentTarget.querySelector('[data-arrow]'); if (a) a.style.transform = 'translateX(0)';
      }}
      style={{
        width: full ? '100%' : 'auto',
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 10,
        padding: '16px 22px', background: BRAND.accent, color: '#1a0d07', border: 'none', borderRadius: 6,
        fontFamily: BRAND.display, fontSize: 15, letterSpacing: '0.04em', textTransform: 'uppercase',
        cursor: disabled ? 'not-allowed' : 'pointer', opacity: disabled ? 0.45 : 1, boxShadow: baseShadow,
        transform: 'translateY(0)',
        transition: 'transform .2s cubic-bezier(.2,.7,.2,1), box-shadow .2s cubic-bezier(.2,.7,.2,1), opacity .12s',
      }}>
      <span>{children}</span>
      <span data-arrow style={{ fontFamily: BRAND.mono, fontSize: 13, transition: 'transform .2s cubic-bezier(.2,.7,.2,1)' }}>→</span>
    </button>
  );
}

function BackButton({ onClick, disabled }) {
  return (
    <button type="button" onClick={onClick} disabled={disabled} style={{
      padding: '14px 18px', background: 'transparent', color: BRAND.inkDim,
      border: `1px solid ${BRAND.ruleHi}`, borderRadius: 6,
      fontFamily: BRAND.mono, fontSize: 12, letterSpacing: '0.1em', textTransform: 'uppercase',
      cursor: disabled ? 'not-allowed' : 'pointer', opacity: disabled ? 0.5 : 1,
      transition: 'color .12s, border-color .12s',
    }}
      onMouseEnter={e => { if (!disabled) { e.currentTarget.style.color = BRAND.ink; e.currentTarget.style.borderColor = BRAND.accent; } }}
      onMouseLeave={e => { e.currentTarget.style.color = BRAND.inkDim; e.currentTarget.style.borderColor = BRAND.ruleHi; }}
    >← Back</button>
  );
}

// Corner ticks — the little "blueprint" detail from the waitlist form panel.
function CornerTicks() {
  return [['t', 'l'], ['t', 'r'], ['b', 'l'], ['b', 'r']].map(([v, h], i) => (
    <div key={i} style={{
      position: 'absolute', [v === 't' ? 'top' : 'bottom']: -1, [h === 'l' ? 'left' : 'right']: -1,
      width: 8, height: 8,
      borderTop: v === 't' ? `2px solid ${BRAND.accent}` : 'none',
      borderBottom: v === 'b' ? `2px solid ${BRAND.accent}` : 'none',
      borderLeft: h === 'l' ? `2px solid ${BRAND.accent}` : 'none',
      borderRight: h === 'r' ? `2px solid ${BRAND.accent}` : 'none',
      animation: `bp-corner-draw-${v}${h} .55s cubic-bezier(.2,.7,.2,1) ${0.35 + i * 0.05}s backwards`,
    }} />
  ));
}

// ───────────────────────────────────────────────────────────────────────────
// The application form
// ───────────────────────────────────────────────────────────────────────────
// One question per step (mirrors the waitlist flow). Choice questions auto-
// advance ~250ms after a selection so the highlight registers first; text
// questions advance on Continue; the final step collects contact details.
// title = [plain, accented] halves of the section heading shown above each step.
const QUESTIONS = [
  { key: 'revenue', type: 'choice', title: ['Your ', 'business.'], columns: 2,
    question: "What's your monthly revenue?", options: REVENUE_OPTIONS },
  { key: 'business', type: 'textarea', title: ['Your ', 'business.'], required: true,
    label: 'Who do you serve, and what do you help them achieve?',
    placeholder: "e.g. I'm a business coach for agency owners, helping them hit consistent £30k months without working weekends." },
  { key: 'team', type: 'choice', title: ['Your ', 'business.'], columns: 3,
    question: 'Is it just you, or do you have a team?',
    hint: 'Freelancers, VAs and contractors count.', options: TEAM_OPTIONS },
  { key: 'commit', type: 'choice', title: ['Time & ', 'commitment.'], columns: 3,
    question: 'Can you commit a few hours a week to working with us on this?',
    hint: 'A couple of calls, then we build the system with you.', options: COMMIT_OPTIONS },
  { key: 'blockers', type: 'textarea', title: ['Time & ', 'commitment.'],
    label: 'Anything that would get in the way of us working together or delivering a result?',
    placeholder: 'e.g. I can only cover software costs for a short time, limited time over the holidays, etc.' },
  { key: 'aiLevel', type: 'choice', title: ['Your ', 'AI experience.'], columns: 1,
    question: 'How would you rate your understanding of AI?', options: AI_LEVEL_OPTIONS },
  { key: 'aiTools', type: 'input', title: ['Your ', 'AI experience.'], maxLength: 300,
    label: 'Which AI tools do you currently use?',
    placeholder: 'e.g. ChatGPT, Claude, Claude Code, Codex, n8n, Make' },
  { key: 'interview', type: 'choice', title: ['The ', 'testimonial.'], columns: 3,
    question: 'If we get you a result, would you be happy to do a short on-camera interview about your experience?',
    hint: "A short video where you're on screen, talking about how it went.", options: INTERVIEW_OPTIONS },
  { key: 'decisionMaker', type: 'choice', title: ['The ', 'testimonial.'], columns: 2,
    question: "Who's the decision maker, you or someone else?", options: DECISION_OPTIONS,
    revealOn: 'Someone else', revealKey: 'decisionWho', revealPlaceholder: "Who's the decision maker?" },
  { key: 'access', type: 'textarea', title: ['Access & ', 'outcome.'], rows: 4,
    label: 'What tools or systems would we likely need access to (CRM, scheduling, email, etc.), and is there anything that would stop you giving us access (privacy, company policy, client confidentiality)?',
    placeholder: 'e.g. GoHighLevel CRM, Calendly, Gmail. No restrictions, happy to grant access.' },
  { key: 'result', type: 'textarea', title: ['Access & ', 'outcome.'], required: true, rows: 3,
    label: 'What would a successful result look like for you in the next 30 days?',
    placeholder: 'e.g. 10 qualified sales calls booked automatically from inbound leads.' },
  { key: 'startWhen', type: 'choice', title: ['Access & ', 'outcome.'], columns: 2,
    question: 'How soon could you start?', options: START_OPTIONS },
  { key: 'contact', type: 'contact', title: ['Your ', 'details.'] },
];
const TOTAL_STEPS = QUESTIONS.length;

// In-progress answers persist to sessionStorage so an accidental refresh, or a
// phone rotation that crosses the mobile breakpoint and remounts the form,
// doesn't wipe a part-finished application. sessionStorage clears when the tab
// closes and we clear it on submit, so nothing lingers on shared devices
// (unlike localStorage).
const APPLY_SS_KEY = 'mahaan_apply_progress';
function readApplyStateFromSession() {
  try {
    const raw = sessionStorage.getItem(APPLY_SS_KEY);
    if (!raw) return null;
    const parsed = JSON.parse(raw);
    if (parsed && typeof parsed === 'object' && parsed.v && typeof parsed.step === 'number') {
      return parsed;
    }
  } catch (e) {}
  return null;
}

function QualForm({ compact, onSubmitted }) {
  // Restore a part-finished application if the form remounted (refresh, or a
  // rotation that crossed the mobile/desktop breakpoint).
  const ssInit = React.useMemo(() => readApplyStateFromSession(), []);
  const [step, setStep] = React.useState(() => {
    const s = ssInit && ssInit.step;
    return (typeof s === 'number' && s >= 1 && s <= TOTAL_STEPS) ? s : 1;
  });
  const [direction, setDirection] = React.useState('forward');
  const [submitting, setSubmitting] = React.useState(false);
  const [error, setError] = React.useState(null);
  const [phoneValid, setPhoneValid] = React.useState(false);
  const honeypotRef = React.useRef(null);
  const advanceTimerRef = React.useRef(null);

  // Clear any pending auto-advance timer when the form unmounts.
  React.useEffect(() => () => {
    if (advanceTimerRef.current) clearTimeout(advanceTimerRef.current);
  }, []);

  const [v, setV] = React.useState(() => ({
    revenue: '', business: '', team: '',
    commit: '', blockers: '',
    aiLevel: '', aiTools: '',
    interview: '', decisionMaker: '', decisionWho: '',
    access: '', result: '', startWhen: '',
    name: '', email: '', businessName: '', phone: '',
    ...(ssInit && ssInit.v ? ssInit.v : {}),
  }));
  const set = k => e => setV(s => ({ ...s, [k]: e.target.value }));
  const setVal = (k, val) => setV(s => ({ ...s, [k]: val }));

  const clearSavedProgress = () => { try { sessionStorage.removeItem(APPLY_SS_KEY); } catch (e) {} };

  // Persist in-progress answers on every change so a remount can restore them.
  React.useEffect(() => {
    try { sessionStorage.setItem(APPLY_SS_KEY, JSON.stringify({ step, v })); } catch (e) {}
  }, [step, v]);

  const emailCheck = validateEmail(v.email);
  const emailValid = emailCheck.ok;
  const emailError = emailValid ? null : emailCheck.reason;
  const emailSuggestion = emailValid ? suggestEmailFix(v.email) : null;

  // The current step's question (one question per step).
  const q = QUESTIONS[step - 1];

  // Is the current step's required input satisfied?
  const stepValid = (() => {
    if (q.type === 'contact') {
      return v.name.trim().length > 0 && emailValid && v.businessName.trim().length > 0 && phoneValid;
    }
    if (q.type === 'choice') {
      if (!v[q.key]) return false;
      if (q.revealOn && v[q.key] === q.revealOn) return v[q.revealKey].trim().length > 0;
      return true;
    }
    return q.optional ? true : v[q.key].trim().length > 0; // textarea / input
  })();

  const goNext = () => {
    if (advanceTimerRef.current) { clearTimeout(advanceTimerRef.current); advanceTimerRef.current = null; }
    setDirection('forward'); setStep(s => s + 1); setError(null);
  };

  const goBack = () => {
    if (step <= 1) return;
    if (advanceTimerRef.current) { clearTimeout(advanceTimerRef.current); advanceTimerRef.current = null; }
    setDirection('back'); setStep(s => s - 1); setError(null);
  };

  // Choice answers auto-advance after a short delay so the selection registers
  // visually first (same feel as the waitlist). A selection that reveals a
  // follow-up field, or the final step, does not auto-advance.
  const handleChoiceSelect = (qq, val) => {
    setVal(qq.key, val);
    if (advanceTimerRef.current) clearTimeout(advanceTimerRef.current);
    if (qq.revealOn && val === qq.revealOn) return;
    if (step >= TOTAL_STEPS) return;
    advanceTimerRef.current = setTimeout(() => {
      setDirection('forward'); setStep(s => s + 1); advanceTimerRef.current = null;
    }, 250);
  };

  const submitForm = async () => {
    if (submitting) return;
    setSubmitting(true); setError(null);
    try {
      const honeypot = (honeypotRef.current && honeypotRef.current.value) || '';
      const payload = {
        revenue: v.revenue,
        business: v.business.trim(),
        team: v.team,
        commit: v.commit,
        blockers: v.blockers.trim(),
        aiLevel: v.aiLevel,
        aiTools: v.aiTools.trim(),
        interview: v.interview,
        decisionMaker: v.decisionMaker,
        decisionWho: v.decisionMaker === 'Someone else' ? v.decisionWho.trim() : '',
        access: v.access.trim(),
        result: v.result.trim(),
        startWhen: v.startWhen,
        name: v.name.trim(),
        email: v.email.trim(),
        businessName: v.businessName.trim(),
        phone: v.phone.trim(),
        honeypot,
        userAgent: navigator.userAgent,
        referrer: document.referrer || '',
      };
      if (honeypot) { // bot filled the hidden field, fake success and write nothing
        clearSavedProgress();
        onSubmitted({ name: payload.name, email: payload.email });
        return;
      }
      await submitApplication(payload);
      clearSavedProgress();
      onSubmitted({ name: payload.name, email: payload.email });
    } catch (err) {
      console.error(err);
      setError('Something went wrong submitting your application. Check your connection and try again.');
    } finally {
      setSubmitting(false);
    }
  };

  const handleSubmit = e => {
    e.preventDefault();
    if (!stepValid) return;
    if (step < TOTAL_STEPS) { goNext(); return; }
    submitForm();
  };

  const stepAnim = direction === 'forward'
    ? 'bp-slide-in-right .42s cubic-bezier(.2,.7,.2,1) backwards'
    : 'bp-slide-in-left .42s cubic-bezier(.2,.7,.2,1) backwards';
  const isLast = step === TOTAL_STEPS;

  return (
    <form onSubmit={handleSubmit} style={{
      padding: compact ? 20 : 32, background: BRAND.bgPanel,
      border: `1px solid ${BRAND.ruleHi}`, borderRadius: 10,
      boxShadow: `0 30px 60px -30px rgba(0,0,0,0.7), 0 0 0 1px ${BRAND.rule}`,
      position: 'relative', zIndex: 5,
      animation: 'bp-form-mount .7s cubic-bezier(.2,.7,.2,1) backwards',
    }}>
      <CornerTicks />

      {/* Honeypot — offscreen, aria-hidden, unreachable by keyboard. */}
      <div aria-hidden="true" style={{ position: 'absolute', left: '-9999px', top: 'auto', width: 1, height: 1, overflow: 'hidden' }}>
        <label>Company website (leave blank)
          <input ref={honeypotRef} type="text" name="company_site" tabIndex={-1} autoComplete="off" defaultValue="" />
        </label>
      </div>

      {/* Step counter + progress bar. The big title below names the section, so
          there's no separate eyebrow to avoid saying it three times over. */}
      <div style={{ marginBottom: 10 }}>
        <span style={{ fontFamily: BRAND.mono, fontSize: 10, letterSpacing: '0.2em', textTransform: 'uppercase', color: BRAND.accent }}>
          Step {step} / {TOTAL_STEPS}
        </span>
      </div>
      <div style={{ height: 3, background: BRAND.rule, borderRadius: 2, overflow: 'hidden', marginBottom: compact ? 18 : 24 }}>
        <div style={{ height: '100%', width: `${(step / TOTAL_STEPS) * 100}%`, background: BRAND.accent, borderRadius: 2, transition: 'width .4s cubic-bezier(.2,.7,.2,1)' }} />
      </div>

      <div style={{ fontFamily: BRAND.display, fontSize: compact ? 24 : 30, color: BRAND.ink, lineHeight: 1.1, marginBottom: compact ? 18 : 24 }}>
        {q.title[0]}
        <span style={{ fontFamily: BRAND.serif, fontStyle: 'italic', color: BRAND.accent, fontWeight: 400 }}>{q.title[1]}</span>
      </div>

      <div key={step} style={{ animation: stepAnim, display: 'grid', gap: compact ? 16 : 18 }}>
        {q.type === 'choice' && (
          <QChoice compact={compact} columns={q.columns}
            question={q.question} hint={q.hint} options={q.options}
            value={v[q.key]} onSelect={val => handleChoiceSelect(q, val)}
            revealOn={q.revealOn} revealPlaceholder={q.revealPlaceholder}
            revealValue={q.revealKey ? v[q.revealKey] : ''}
            onRevealChange={q.revealKey ? (val => setVal(q.revealKey, val)) : undefined} />
        )}
        {q.type === 'textarea' && (
          <QTextArea label={q.label} optional={q.optional} value={v[q.key]} onChange={set(q.key)}
            rows={q.rows} placeholder={q.placeholder} />
        )}
        {q.type === 'input' && (
          <QInput label={q.label} optional={q.optional} value={v[q.key]} onChange={set(q.key)}
            maxLength={q.maxLength} placeholder={q.placeholder} />
        )}
        {q.type === 'contact' && (
          <>
            <QInput label="Name" value={v.name} onChange={set('name')} autoComplete="name" maxLength={80} placeholder="Your full name" />
            <QInput label="Email" type="email" value={v.email} onChange={set('email')} autoComplete="email" maxLength={254}
              placeholder="you@email.com" error={emailError} suggestion={emailSuggestion}
              onAcceptSuggestion={() => emailSuggestion && setVal('email', emailSuggestion)} />
            <QInput label="Business name" value={v.businessName} onChange={set('businessName')} maxLength={120} placeholder="Your company / brand name" />
            <QPhone label="Phone number" initialValue={v.phone} onChange={set('phone')} onValidChange={setPhoneValid} />
          </>
        )}
      </div>

      {/* Controls */}
      <div style={{ display: 'flex', gap: 10, marginTop: 22 }}>
        {step > 1 && <BackButton onClick={goBack} disabled={submitting} />}
        <CTAButton full disabled={!stepValid || submitting}>
          {isLast ? (submitting ? 'Sending…' : 'Send it over') : 'Continue'}
        </CTAButton>
      </div>

      {error && (
        <div style={{
          marginTop: 14, padding: '10px 12px', background: 'rgba(224,120,86,0.08)',
          border: `1px solid ${BRAND.accent}`, borderRadius: 6,
          fontFamily: BRAND.body, fontSize: 13, color: BRAND.accent,
        }}>{error}</div>
      )}

      <div style={{ marginTop: 14, fontFamily: BRAND.mono, fontSize: 10, color: BRAND.inkFaint, letterSpacing: '0.08em', textAlign: 'center' }}>
        {isLast ? 'A real person reads every one.' : 'Takes a few minutes · a real person reads every one'}
      </div>
    </form>
  );
}

// ── Confirmation ───────────────────────────────────────────────────────────
function Confirmation({ name, email, compact }) {
  const first = (name || '').trim().split(/\s+/)[0] || 'there';
  const igLinks = [
    { handle: 'karam.mahaan', url: 'https://instagram.com/karam.mahaan' },
    { handle: 'varunmahaan_', url: 'https://instagram.com/varunmahaan_' },
  ];
  return (
    <div style={{
      padding: compact ? 24 : 36, background: BRAND.bgPanel,
      border: `1px solid ${BRAND.accent}`, borderRadius: 10,
      boxShadow: `0 30px 60px -30px rgba(0,0,0,0.7), 0 0 80px -30px ${BRAND.accentGlow}`,
      position: 'relative', zIndex: 5,
      animation: 'bp-form-mount .7s cubic-bezier(.2,.7,.2,1) backwards',
    }}>
      <div style={{
        display: 'inline-block', background: BRAND.accent, color: '#1a0d07',
        fontFamily: BRAND.display, fontSize: 10, letterSpacing: '0.14em', padding: '5px 9px',
        marginBottom: compact ? 16 : 20,
        animation: 'bp-field-in .5s cubic-bezier(.2,.7,.2,1) .1s backwards',
      }}>GOT IT, THANK YOU</div>

      <div style={{
        fontFamily: BRAND.display, fontSize: compact ? 28 : 36, color: BRAND.ink, lineHeight: 1.05, marginBottom: 18,
        animation: 'bp-field-in .55s cubic-bezier(.2,.7,.2,1) .18s backwards',
      }}>
        Thanks,{' '}
        <span style={{ fontFamily: BRAND.serif, fontStyle: 'italic', color: BRAND.accent, fontWeight: 400 }}>{first}.</span>
      </div>

      <div style={{
        color: BRAND.inkDim, fontFamily: BRAND.body, fontSize: 15, lineHeight: 1.6, marginBottom: compact ? 22 : 28,
        animation: 'bp-field-in .55s cubic-bezier(.2,.7,.2,1) .26s backwards',
      }}>
        We've got everything, thanks for sharing. A real person reads every one of these, and if we think
        we can get you a real, measurable result, we'll reach out to set up a quick call. On that call we dig
        into your business; then we go away, map out the right approach, and come back to you with a plan
        before recommending anything. Keep an eye on your inbox at{' '}
        <span style={{ color: BRAND.ink }}>{(email || '').trim()}</span>.
      </div>

      <div style={{
        borderTop: `1px solid ${BRAND.ruleHi}`, paddingTop: compact ? 18 : 22,
        animation: 'bp-field-in .6s cubic-bezier(.2,.7,.2,1) .34s backwards',
      }}>
        <div style={{ fontFamily: BRAND.mono, fontSize: 9, letterSpacing: '0.18em', color: BRAND.inkDim, textTransform: 'uppercase', marginBottom: 10 }}>
          While you wait
        </div>
        <div style={{ fontFamily: BRAND.body, fontSize: 14, color: BRAND.ink, lineHeight: 1.55, marginBottom: 14 }}>
          Follow along. We post the systems we build and the thinking behind them.
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: compact ? '1fr' : 'repeat(2, minmax(0, 1fr))', gap: 10 }}>
          {igLinks.map(({ handle, url }) => (
            <a key={handle} href={url} target="_blank" rel="noopener noreferrer" aria-label={`Follow @${handle} on Instagram`}
              onMouseEnter={e => { e.currentTarget.style.borderColor = BRAND.accent; e.currentTarget.style.transform = 'translateY(-1px)'; e.currentTarget.style.background = BRAND.accentSoft; }}
              onMouseLeave={e => { e.currentTarget.style.borderColor = BRAND.ruleHi; e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.background = '#0a0a0a'; }}
              style={{
                display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, padding: '12px 14px',
                background: '#0a0a0a', border: `1px solid ${BRAND.ruleHi}`, borderRadius: 6, color: BRAND.ink,
                fontFamily: BRAND.body, fontSize: 14, textDecoration: 'none', transform: 'translateY(0)',
                transition: 'border-color .2s, transform .2s, background .2s', minWidth: 0,
              }}>
              <span style={{ display: 'inline-flex', alignItems: 'center', gap: 9, minWidth: 0 }}>
                <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke={BRAND.accent} strokeWidth="1.8" aria-hidden="true" style={{ flexShrink: 0 }}>
                  <rect x="3" y="3" width="18" height="18" rx="5" /><circle cx="12" cy="12" r="4" /><circle cx="17.5" cy="6.5" r="1" fill={BRAND.accent} stroke="none" />
                </svg>
                <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>@{handle}</span>
              </span>
              <span style={{ color: BRAND.accent, fontFamily: BRAND.mono, fontSize: 13, flexShrink: 0 }}>↗</span>
            </a>
          ))}
        </div>
      </div>
    </div>
  );
}

// ── Page shell ─────────────────────────────────────────────────────────────
function Wordmark({ compact }) {
  return (
    <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
      <div style={{ fontFamily: BRAND.display, fontSize: compact ? 13 : 15, letterSpacing: '0.06em' }}>
        MAHAAN<span style={{ color: BRAND.accent }}>.</span>
      </div>
      <div style={{ fontFamily: BRAND.mono, fontSize: compact ? 8 : 9, letterSpacing: '0.2em', color: BRAND.inkDim, textTransform: 'uppercase' }}>
        Consulting
      </div>
    </div>
  );
}

function ApplyApp() {
  const isMobile = useIsMobile();
  const [done, setDone] = React.useState(null); // { name, email } once submitted

  React.useEffect(() => {
    if (done) { try { window.scrollTo({ top: 0, behavior: 'auto' }); } catch (_) {} }
  }, [done]);

  return (
    <div style={{ width: '100%', minHeight: '100vh', background: BRAND.bg, color: BRAND.ink, fontFamily: BRAND.body, position: 'relative', overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
      <DotGrid size={isMobile ? 20 : 28} color="rgba(255,255,255,0.04)" />

      {/* NAV */}
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: isMobile ? '20px 20px' : '24px 56px', position: 'relative', zIndex: 2 }}>
        <Wordmark compact={isMobile} />
        {/* No scarcity badge in the form state — a warm lead shouldn't be met
            with a "spots left" tag. Only a quiet thank-you once submitted. */}
        {done && (
          <div style={{ fontFamily: BRAND.mono, fontSize: isMobile ? 9 : 11, color: BRAND.accent, border: `1px solid ${BRAND.accent}`, borderRadius: 4, padding: isMobile ? '4px 8px' : '6px 10px', letterSpacing: '0.14em' }}>
            THANK YOU
          </div>
        )}
      </div>

      {/* BODY */}
      <section style={{ position: 'relative', flex: 1, padding: isMobile ? '24px 20px 56px' : '24px 56px 72px' }}>
        {/* Perspective grid: full-bleed on desktop. On mobile the page is tall and
            narrow, so stretching the grid full-height turned the converging rays
            into a narrow vertical "light beam". Anchor it to a short band at the
            TOP of the page instead (behind the hero), so the rays fan out nicely
            without stretching down the whole page. */}
        <div style={{
          position: 'absolute', opacity: 0.4, pointerEvents: 'none',
          ...(isMobile ? { left: 0, right: 0, top: 0, height: 300 } : { inset: 0 }),
        }}>
          <PerspectiveGrid opacity={0.14} />
        </div>

        {done ? (
          <div style={{ position: 'relative', zIndex: 2, maxWidth: 620, margin: '0 auto', paddingTop: isMobile ? 8 : 24 }}>
            <Confirmation name={done.name} email={done.email} compact={isMobile} />
          </div>
        ) : isMobile ? (
          <div style={{ position: 'relative', zIndex: 2 }}>
            <Intro compact />
            <div style={{ marginTop: 30 }}>
              <QualForm compact onSubmitted={setDone} />
            </div>
            <div style={{ marginTop: 40 }}>
              <Examples compact />
            </div>
          </div>
        ) : (
          <div style={{ position: 'relative', zIndex: 2, display: 'grid', gridTemplateColumns: '1fr 560px', gap: 60, alignItems: 'start', maxWidth: 1180, margin: '0 auto' }}>
            <div style={{ paddingTop: 16 }}><Intro /></div>
            <QualForm onSubmitted={setDone} />
          </div>
        )}
      </section>

      {/* FOOTER */}
      <div style={{
        padding: isMobile ? '20px 20px 28px' : '22px 56px 30px', borderTop: `1px solid ${BRAND.rule}`,
        display: 'flex', flexDirection: isMobile ? 'column' : 'row', alignItems: isMobile ? 'flex-start' : 'center',
        justifyContent: 'space-between', gap: isMobile ? 10 : 16, position: 'relative', zIndex: 2,
        fontFamily: BRAND.mono, fontSize: isMobile ? 9 : 11, color: BRAND.inkFaint, letterSpacing: '0.14em', textTransform: 'uppercase',
      }}>
        <span>Mahaan Consulting © 2026</span>
        <span style={{ textTransform: 'none', letterSpacing: 0, fontFamily: BRAND.body, fontSize: isMobile ? 11 : 12 }}>
          Free build · in exchange for an on-camera testimonial
        </span>
      </div>
    </div>
  );
}

// Example systems — common revenue plays, framed clearly as examples (not a
// menu). Renders beside the form on desktop and below the form on mobile.
function Examples({ compact }) {
  const items = [
    ['Lead gen', 'Fill your pipeline with qualified inbound'],
    ['Appt setting', 'Booked calls without the manual chase'],
    ['Customer LTV', 'More revenue from the clients you already have'],
  ];
  return (
    <div style={{ maxWidth: compact ? '100%' : 470 }}>
      <Eyebrow>A few examples, not a fixed list</Eyebrow>
      <div style={{ fontFamily: BRAND.body, fontSize: compact ? 13.5 : 14, color: BRAND.inkDim, lineHeight: 1.55, marginTop: 8 }}>
        Some of the ways we generate revenue with AI. What we build for you is decided by your audit. It isn't limited to these.
      </div>
      <div style={{ marginTop: compact ? 16 : 20, display: 'flex', flexDirection: compact ? 'column' : 'row', gap: compact ? 13 : 28 }}>
        {items.map(([k, d]) => compact ? (
          <div key={k} style={{ display: 'flex', gap: 12, alignItems: 'baseline' }}>
            <div style={{ fontFamily: BRAND.mono, fontSize: 10, letterSpacing: '0.14em', color: BRAND.accent, textTransform: 'uppercase', minWidth: 92, flexShrink: 0 }}>{k}</div>
            <div style={{ fontFamily: BRAND.body, fontSize: 13.5, color: BRAND.inkDim, lineHeight: 1.45 }}>{d}</div>
          </div>
        ) : (
          <div key={k} style={{ maxWidth: 140 }}>
            <div style={{ fontFamily: BRAND.mono, fontSize: 10, letterSpacing: '0.16em', color: BRAND.accent, textTransform: 'uppercase', marginBottom: 6 }}>{k}</div>
            <div style={{ fontFamily: BRAND.body, fontSize: 12.5, color: BRAND.inkDim, lineHeight: 1.45 }}>{d}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

function Intro({ compact }) {
  return (
    <div>
      <Eyebrow>Coaches and consultants</Eyebrow>
      <h1 style={{
        fontFamily: BRAND.display, fontSize: compact ? 34 : 64, lineHeight: 0.98,
        margin: compact ? '20px 0 0' : '22px 0 0', letterSpacing: '-0.02em',
      }}>
        GROW WITHOUT<br />
        <span style={{ fontFamily: BRAND.serif, fontStyle: 'italic', color: BRAND.accent, fontWeight: 400 }}>more hours.</span>
      </h1>
      {compact ? (
        <p style={{ fontFamily: BRAND.body, fontSize: 15, color: BRAND.inkDim, lineHeight: 1.6, margin: '18px 0 0' }}>
          We find your biggest revenue bottleneck and build an AI system that removes it, <span style={{ color: BRAND.ink }}>free</span>, in exchange for an
          on-camera testimonial. You run it, you bring the selling. We take on two at a time.
        </p>
      ) : (
        <p style={{ fontFamily: BRAND.body, fontSize: 17, color: BRAND.inkDim, lineHeight: 1.6, margin: '26px 0 0', maxWidth: 470 }}>
          We find your biggest revenue bottleneck and build an AI system that removes it, <span style={{ color: BRAND.ink }}>free</span>, in exchange for an
          on-camera testimonial. You run it, you bring the selling. We only take on two businesses at a time.
        </p>
      )}
      {!compact && <div style={{ marginTop: 34 }}><Examples /></div>}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<ApplyApp />);
