// STUDIO — Enterprise Word Processor (StudioEditor)
const { useState: useStEd, useEffect: useEffEd, useRef: useRefEd } = React;
const TspEd = window.ArbiterTokens;

// ── Static constants ──────────────────────────────────────────────────────────

const EDITOR_INITIAL_HTML = `
<div style="text-align:center;margin-bottom:24px;">
  <div style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:0.07em;color:#1E293B;margin-bottom:5px;">IN THE UNITED STATES DISTRICT COURT</div>
  <div style="font-size:11px;color:#1E293B;margin-bottom:10px;">FOR THE SOUTHERN DISTRICT OF NEW YORK</div>
  <div style="border:1px solid #CBD5E1;padding:9px 14px;text-align:left;font-size:11px;color:#1E293B;line-height:1.8;display:inline-block;min-width:340px;">
    <div style="font-weight:700;">MERIDIAN OIL CORP.,</div>
    <div style="padding-left:16px;margin-bottom:4px;">Plaintiff,</div>
    <div style="margin-bottom:4px;">v. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Case No. 1:26-cv-04421-JST</div>
    <div style="font-weight:700;">COASTAL ENERGY LLC,</div>
    <div style="padding-left:16px;">Defendant.</div>
  </div>
  <div style="margin-top:14px;font-size:13px;font-weight:700;text-transform:uppercase;letter-spacing:0.04em;color:#1E293B;">PLAINTIFF'S MOTION TO COMPEL PRODUCTION</div>
  <div style="font-size:11px;color:#64748B;margin-top:4px;">Civil Action No. 1:26-cv-04421-JST · S.D.N.Y.</div>
</div>

<h2 style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:0.05em;color:#1E293B;margin:20px 0 8px;border-bottom:1px solid #E2E8F0;padding-bottom:4px;">Preliminary Statement</h2>
<p style="margin-bottom:14px;text-indent:2em;text-align:justify;">Plaintiff Meridian Oil Corp. (&ldquo;Meridian&rdquo;) hereby moves, pursuant to Federal Rule of Civil Procedure 37(a)(3)(B)(iv), for an order compelling Defendant Coastal Energy LLC (&ldquo;Coastal&rdquo;) to produce all documents responsive to Meridian&rsquo;s First Set of Requests for Production of Documents. Despite sixty (60) days having elapsed since service of the Requests, and notwithstanding four meet-and-confer conferences held in good faith by Meridian&rsquo;s counsel, Coastal has failed to produce a single document and has interposed boilerplate objections to each and every request.</p>

<h2 style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:0.05em;color:#1E293B;margin:20px 0 8px;border-bottom:1px solid #E2E8F0;padding-bottom:4px;">I. Factual Background</h2>
<p style="margin-bottom:12px;text-indent:2em;text-align:justify;">This action arises from a dispute over the acquisition of the Pelican Bay offshore asset portfolio. On February 14, 2026, Meridian served its First Set of Requests seeking, <em>inter alia</em>, documents relating to the <span style="background:rgba(201,168,76,0.28);padding:0 3px;border-radius:2px;">Pelican Bay asset acquisition, internal valuations thereof, and all communications with third-party suppliers from January 1, 2024 through December 31, 2025.</span></p>
<p style="margin-bottom:12px;text-indent:2em;text-align:justify;">Coastal&rsquo;s response, served on April 1, 2026, consisted entirely of general objections &mdash; to scope, relevance, and proportionality &mdash; without producing a single responsive document. Coastal&rsquo;s counsel has repeatedly represented that production was &ldquo;forthcoming,&rdquo; but no documents have been produced as of the date of this filing. Meridian&rsquo;s ESI vendor has identified an estimated 14,204 documents likely responsive to the Requests.</p>

<h2 style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:0.05em;color:#1E293B;margin:20px 0 8px;border-bottom:1px solid #E2E8F0;padding-bottom:4px;">II. Legal Standard</h2>
<p style="margin-bottom:12px;text-indent:2em;text-align:justify;">A party may move to compel production of documents when another party fails to produce them as requested under Rule 34. <em>See</em> Fed. R. Civ. P. 37(a)(3)(B)(iv). The party resisting discovery bears the burden of establishing that the requested documents are not relevant or that the request is disproportionate to the needs of the case. <em>See Zubulake v. UBS Warburg LLC</em>, 217 F.R.D. 309, 316 (S.D.N.Y. 2003).</p>
<p style="margin-bottom:12px;text-indent:2em;text-align:justify;">Relevance under Rule 26(b)(1) is to be &ldquo;construed broadly to encompass any matter that bears on, or that reasonably could lead to other matter that could bear on, any issue that is or may be in the case.&rdquo; <em>Oppenheimer Fund, Inc. v. Sanders</em>, 437 U.S. 340, 351 (1978). Boilerplate objections that lack specific support are improper and do not constitute valid grounds for refusing production. <em>See S.D.N.Y. Local Civ. R. 37.2.</em></p>

<h2 style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:0.05em;color:#1E293B;margin:20px 0 8px;border-bottom:1px solid #E2E8F0;padding-bottom:4px;">III. Argument</h2>
<p style="margin-bottom:12px;text-indent:2em;text-align:justify;"><strong>A. Coastal&rsquo;s Objections Are Boilerplate and Improper.</strong> Coastal&rsquo;s wholesale failure to produce any documents in response to Meridian&rsquo;s First Set of Requests is unjustified and constitutes a violation of Rules 34 and 37. Meridian&rsquo;s Requests are narrowly tailored to documents directly relevant to the claims and defenses in this action.</p>
<p style="margin-bottom:12px;text-indent:2em;text-align:justify;">Coastal&rsquo;s objections to each Request &mdash; asserting that they are &ldquo;overbroad,&rdquo; &ldquo;unduly burdensome,&rdquo; and &ldquo;not proportional to the needs of the case&rdquo; &mdash; are identical, boilerplate assertions that provide no specific support. Courts in this District have consistently held that such objections are improper. <em>See Pension Comm. of Univ. of Montreal Pension Plan v. Banc of Am. Sec., LLC</em>, 685 F. Supp. 2d 456, 475 (S.D.N.Y. 2010).</p>
<p style="margin-bottom:12px;text-indent:2em;text-align:justify;"><strong>B. The Requested Documents Are Proportional.</strong> Under the proportionality factors of Rule 26(b)(1), the importance of the issues at stake, the parties&rsquo; relative access to relevant information, and the amount in controversy (exceeding $50 million) all weigh strongly in favor of compelling full production.</p>

<h2 style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:0.05em;color:#1E293B;margin:20px 0 8px;border-bottom:1px solid #E2E8F0;padding-bottom:4px;">IV. Conclusion</h2>
<p style="margin-bottom:12px;text-indent:2em;text-align:justify;">For the foregoing reasons, Meridian respectfully requests that the Court enter an order compelling Coastal to produce all documents responsive to Meridian&rsquo;s First Set of Requests for Production of Documents within fourteen (14) days of the Court&rsquo;s order, and awarding Meridian its reasonable attorneys&rsquo; fees and costs in bringing this motion pursuant to Rule 37(a)(5)(A).</p>
<p style="margin-bottom:8px;">Respectfully submitted,</p>
<p style="margin-bottom:4px;font-weight:700;">KIRKLAND &amp; CHEN LLP</p>
<p style="margin-bottom:4px;">By: <span style="font-style:italic;">/s/ Marcus Kirkland</span></p>
<p style="margin-bottom:2px;">Marcus Kirkland, Senior Partner</p>
<p style="margin-bottom:2px;">Sophia Chen, Associate</p>
<p style="margin-bottom:2px;">1 Liberty Plaza, Suite 3200, New York, NY 10006</p>
<p style="color:#94A3B8;font-style:italic;font-size:10px;margin-top:20px;border-top:1px solid #E2E8F0;padding-top:10px;">Certificate of Conference per S.D.N.Y. Local Civil Rule 37.2 appended hereto as Exhibit A. Undersigned counsel certifies that the parties met and conferred on March 3, April 7, April 12, and April 18, 2026 without reaching resolution.</p>
`;

const TABLE_3X3_HTML = `<br><table style="border-collapse:collapse;width:100%;margin:12px 0;font-family:inherit;font-size:inherit;"><tbody><tr><td style="border:1px solid #CBD5E1;padding:6px 10px;font-weight:700;background:#F8FAFC;font-size:11px;">Column A</td><td style="border:1px solid #CBD5E1;padding:6px 10px;font-weight:700;background:#F8FAFC;font-size:11px;">Column B</td><td style="border:1px solid #CBD5E1;padding:6px 10px;font-weight:700;background:#F8FAFC;font-size:11px;">Column C</td></tr><tr><td style="border:1px solid #CBD5E1;padding:6px 10px;font-size:11px;">Cell 1,1</td><td style="border:1px solid #CBD5E1;padding:6px 10px;font-size:11px;">Cell 1,2</td><td style="border:1px solid #CBD5E1;padding:6px 10px;font-size:11px;">Cell 1,3</td></tr><tr><td style="border:1px solid #CBD5E1;padding:6px 10px;font-size:11px;">Cell 2,1</td><td style="border:1px solid #CBD5E1;padding:6px 10px;font-size:11px;">Cell 2,2</td><td style="border:1px solid #CBD5E1;padding:6px 10px;font-size:11px;">Cell 2,3</td></tr></tbody></table><br>`;

const PT_TO_HTML = { '8':1,'9':1,'10':2,'11':2,'12':3,'13':3,'14':4,'16':4,'18':5,'20':5,'24':6,'28':6,'36':7,'48':7,'72':7 };
const HTML_TO_PT = { '1':'8','2':'10','3':'12','4':'14','5':'18','6':'24','7':'36' };
const FONT_OPTIONS = [
  ['Georgia, "Times New Roman", serif', 'Georgia'],
  ['"Times New Roman", Times, serif',   'Times New Roman'],
  ['Arial, Helvetica, sans-serif',      'Arial'],
  ['Verdana, Geneva, sans-serif',       'Verdana'],
  ['"Courier New", Courier, monospace', 'Courier New'],
  ['Garamond, "EB Garamond", serif',    'Garamond'],
];
const SIZE_OPTIONS = ['8','9','10','11','12','14','16','18','20','24','28','36','48','72'];

// ── Component ─────────────────────────────────────────────────────────────────

function StudioEditor({ doc, mode, onModeChange, onCommentAdd, onStatsUpdate }) {
  const st = window.__st;

  // ── Format state ────────────────────────────────────────────────────────────
  const [fmtState, setFmtState] = useStEd({
    bold: false, italic: false, underline: false, strikeThrough: false,
    justifyLeft: true, justifyCenter: false, justifyRight: false, justifyFull: false,
    insertOrderedList: false, insertUnorderedList: false,
  });
  const [styleVal,   setStyleVal]   = useStEd('p');
  const [fontFamily, setFontFamily] = useStEd('Georgia, "Times New Roman", serif');
  const [fontSize,   setFontSize]   = useStEd('12');
  const [textColor,  setTextColor]  = useStEd('#1E293B');
  const [hlColor,    setHlColor]    = useStEd('#FEF08A');

  // ── Editor state ────────────────────────────────────────────────────────────
  const [zoom,       setZoom]       = useStEd(100);
  const [wordCount,  setWordCount]  = useStEd(0);
  const [charCount,  setCharCount]  = useStEd(0);
  const [autoSaved,  setAutoSaved]  = useStEd(Date.now());
  const [timerPaused,setTimerPaused]= useStEd(false);

  // ── Bates ───────────────────────────────────────────────────────────────────
  const [batesOpen,  setBatesOpen]  = useStEd(false);

  // ── Find & Replace ──────────────────────────────────────────────────────────
  const [findOpen,      setFindOpen]      = useStEd(false);
  const [findQuery,     setFindQuery]     = useStEd('');
  const [replaceQuery,  setReplaceQuery]  = useStEd('');
  const [matchCount,    setMatchCount]    = useStEd(0);
  const [matchIndex,    setMatchIndex]    = useStEd(0);
  const [caseSens,      setCaseSens]      = useStEd(false);
  const [wholeWord,     setWholeWord]     = useStEd(false);

  // ── Track Changes ───────────────────────────────────────────────────────────
  const [trackChanges,   setTrackChanges]   = useStEd(false);
  const [pendingChanges, setPendingChanges] = useStEd([]);

  // ── Refs ────────────────────────────────────────────────────────────────────
  const editorRef     = useRefEd(null);
  const isMounted     = useRefEd(false);

  // ── Core helpers ────────────────────────────────────────────────────────────

  const updateFmtState = () => {
    try {
      setFmtState({
        bold:                document.queryCommandState('bold'),
        italic:              document.queryCommandState('italic'),
        underline:           document.queryCommandState('underline'),
        strikeThrough:       document.queryCommandState('strikeThrough'),
        justifyLeft:         document.queryCommandState('justifyLeft'),
        justifyCenter:       document.queryCommandState('justifyCenter'),
        justifyRight:        document.queryCommandState('justifyRight'),
        justifyFull:         document.queryCommandState('justifyFull'),
        insertOrderedList:   document.queryCommandState('insertOrderedList'),
        insertUnorderedList: document.queryCommandState('insertUnorderedList'),
      });
      const fsz = document.queryCommandValue('fontSize');
      if (fsz && HTML_TO_PT[fsz]) setFontSize(HTML_TO_PT[fsz]);
    } catch(_) {}
  };

  const execCmd = (cmd, val) => {
    if (!editorRef.current) return;
    editorRef.current.focus();
    document.execCommand(cmd, false, val === undefined ? null : val);
    updateFmtState();
  };

  const applyStyle = (tag) => {
    execCmd('formatBlock', tag);
    setStyleVal(tag);
  };

  const applyFont = (val) => {
    setFontFamily(val);
    execCmd('fontName', val);
  };

  const applySize = (pt) => {
    const sz = PT_TO_HTML[pt] || 3;
    setFontSize(String(pt));
    execCmd('fontSize', sz);
  };

  const handleInput = () => {
    if (!editorRef.current) return;
    const text = editorRef.current.innerText || '';
    const wc = text.trim() ? text.trim().split(/\s+/).length : 0;
    const cc = text.replace(/\s/g, '').length;
    setWordCount(wc);
    setCharCount(cc);
    if (onStatsUpdate) onStatsUpdate({ words: wc, chars: cc, pages: Math.max(1, Math.ceil(wc / 500)) });
  };

  // ── Find & Replace ──────────────────────────────────────────────────────────

  const stripMarks = () => {
    if (!editorRef.current) return;
    editorRef.current.querySelectorAll('mark.find-hl').forEach(m => {
      m.replaceWith(document.createTextNode(m.textContent));
    });
  };

  const runFind = (q, cs, ww) => {
    if (!editorRef.current) return;
    stripMarks();
    if (!q) { setMatchCount(0); setMatchIndex(0); return; }

    const esc  = q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    const pat  = ww ? `\\b${esc}\\b` : esc;
    const rx   = new RegExp(pat, cs ? 'g' : 'gi');
    let counter = 0;

    const walker = document.createTreeWalker(editorRef.current, 4 /* SHOW_TEXT */, null, false);
    const nodes = [];
    let n;
    while ((n = walker.nextNode())) nodes.push(n);

    nodes.forEach(tn => {
      const txt = tn.textContent;
      rx.lastIndex = 0;
      if (!rx.test(txt)) return;
      rx.lastIndex = 0;

      const frag = document.createDocumentFragment();
      let last = 0, m;
      while ((m = rx.exec(txt)) !== null) {
        if (m.index > last) frag.appendChild(document.createTextNode(txt.slice(last, m.index)));
        const mark = document.createElement('mark');
        mark.className = 'find-hl';
        mark.id = `fhl-${++counter}`;
        mark.style.cssText = 'background:#FEF08A;border-radius:2px;padding:0 1px;';
        mark.textContent = m[0];
        frag.appendChild(mark);
        last = m.index + m[0].length;
      }
      if (last < txt.length) frag.appendChild(document.createTextNode(txt.slice(last)));
      if (tn.parentNode) tn.parentNode.replaceChild(frag, tn);
    });

    setMatchCount(counter);
    if (counter > 0) { const idx = 1; setMatchIndex(idx); scrollToMatch(idx); }
    else setMatchIndex(0);
  };

  const scrollToMatch = (idx) => {
    const el = document.getElementById(`fhl-${idx}`);
    if (el) el.scrollIntoView({ behavior: 'smooth', block: 'center' });
    setMatchIndex(idx);
  };

  const nextMatch = () => {
    setMatchIndex(prev => {
      const next = prev >= matchCount ? 1 : prev + 1;
      scrollToMatch(next);
      return next;
    });
  };

  const prevMatch = () => {
    setMatchIndex(prev => {
      const p = prev <= 1 ? matchCount : prev - 1;
      scrollToMatch(p);
      return p;
    });
  };

  const doReplace = () => {
    const el = document.getElementById(`fhl-${matchIndex}`);
    if (el) { el.replaceWith(document.createTextNode(replaceQuery)); runFind(findQuery, caseSens, wholeWord); }
  };

  const doReplaceAll = () => {
    if (!editorRef.current) return;
    editorRef.current.querySelectorAll('mark.find-hl').forEach(m => m.replaceWith(document.createTextNode(replaceQuery)));
    setMatchCount(0); setMatchIndex(0);
  };

  const closeFind = () => {
    stripMarks();
    setMatchCount(0); setMatchIndex(0); setFindOpen(false);
  };

  // ── Comments ────────────────────────────────────────────────────────────────

  const insertComment = () => {
    const sel = window.getSelection();
    if (!sel || sel.rangeCount === 0 || sel.isCollapsed) {
      alert('Select text first, then click Comment.');
      return;
    }
    const text = window.prompt('Comment text:');
    if (!text) return;
    const cid = `cmt-${Date.now()}`;
    const anchorText = sel.toString();
    try {
      const range = sel.getRangeAt(0);
      const span = document.createElement('span');
      span.setAttribute('data-cid', cid);
      span.style.cssText = 'background:rgba(201,168,76,0.3);border-bottom:2px solid #C9A84C;cursor:pointer;';
      range.surroundContents(span);
    } catch (_) {
      execCmd('hiliteColor', 'rgba(201,168,76,0.3)');
    }
    if (onCommentAdd) onCommentAdd({ id: cid, text, author: 'MK', time: 'Just now', resolved: false, anchorText });
  };

  const insertTable = () => execCmd('insertHTML', TABLE_3X3_HTML);

  const insertLink = () => {
    const url = window.prompt('URL:', 'https://');
    if (url) execCmd('createLink', url);
  };

  // ── Track Changes ───────────────────────────────────────────────────────────

  const acceptAllChanges = () => {
    if (!editorRef.current) return;
    editorRef.current.querySelectorAll('[data-tc="insert"]').forEach(el =>
      el.replaceWith(document.createTextNode(el.textContent))
    );
    editorRef.current.querySelectorAll('[data-tc="delete"]').forEach(el => el.remove());
    setPendingChanges([]);
  };

  const rejectAllChanges = () => {
    if (!editorRef.current) return;
    editorRef.current.querySelectorAll('[data-tc="insert"]').forEach(el => el.remove());
    editorRef.current.querySelectorAll('[data-tc="delete"]').forEach(el =>
      el.replaceWith(document.createTextNode(el.textContent))
    );
    setPendingChanges([]);
  };

  // ── useEffect hooks ─────────────────────────────────────────────────────────

  // Seed initial content once
  useEffEd(() => {
    if (!isMounted.current && editorRef.current) {
      editorRef.current.innerHTML = EDITOR_INITIAL_HTML;
      isMounted.current = true;
      handleInput();
    }
  }, []);

  // Selection → format state sync
  useEffEd(() => {
    const h = () => updateFmtState();
    document.addEventListener('selectionchange', h);
    return () => document.removeEventListener('selectionchange', h);
  }, []);

  // Track changes — intercept beforeinput
  useEffEd(() => {
    const el = editorRef.current;
    if (!el) return;
    const handler = (e) => {
      if (!trackChanges) return;
      if (e.inputType === 'insertText' && e.data) {
        e.preventDefault();
        const safe = e.data.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
        document.execCommand('insertHTML', false,
          `<span data-tc="insert" style="color:#059669;text-decoration:underline;background:rgba(5,150,105,0.1);">${safe}</span>`);
        setPendingChanges(prev => [...prev, { id: Date.now(), type: 'insert', text: e.data, author: 'M. Kirkland' }]);
      } else if (e.inputType === 'deleteContentBackward' || e.inputType === 'deleteContentForward') {
        e.preventDefault();
        const sel = window.getSelection();
        if (!sel || !sel.rangeCount) return;
        const range = sel.getRangeAt(0);
        if (range.collapsed) {
          const tn = range.startContainer;
          if (tn.nodeType !== Node.TEXT_NODE) return;
          const isBack = e.inputType === 'deleteContentBackward';
          const start = isBack ? Math.max(0, range.startOffset - 1) : range.startOffset;
          const end   = isBack ? range.startOffset : Math.min(tn.length, range.startOffset + 1);
          if (start === end) return;
          const ch = tn.textContent.slice(start, end);
          const nr = document.createRange();
          nr.setStart(tn, start); nr.setEnd(tn, end);
          sel.removeAllRanges(); sel.addRange(nr);
          document.execCommand('insertHTML', false,
            `<span data-tc="delete" style="color:#B91C1C;text-decoration:line-through;background:rgba(185,28,28,0.08);">${ch}</span>`);
          setPendingChanges(prev => [...prev, { id: Date.now(), type: 'delete', text: ch, author: 'M. Kirkland' }]);
        } else {
          const del = range.toString();
          document.execCommand('insertHTML', false,
            `<span data-tc="delete" style="color:#B91C1C;text-decoration:line-through;background:rgba(185,28,28,0.08);">${del}</span>`);
          setPendingChanges(prev => [...prev, { id: Date.now(), type: 'delete', text: del, author: 'M. Kirkland' }]);
        }
      }
    };
    el.addEventListener('beforeinput', handler);
    return () => el.removeEventListener('beforeinput', handler);
  }, [trackChanges]);

  // Auto-save every 30s
  useEffEd(() => {
    const id = setInterval(() => setAutoSaved(Date.now()), 30000);
    return () => clearInterval(id);
  }, []);

  // ── Render helpers ──────────────────────────────────────────────────────────

  const saveInfo = (() => {
    const diff = Math.floor((Date.now() - autoSaved) / 1000);
    if (diff < 8)  return { label: '● Saved just now', color: st.emerald };
    const mins = Math.floor(diff / 60);
    return { label: `● Saved ${mins || 1}m ago`, color: TspEd.color.text.tertiary };
  })();

  const pageEst  = Math.max(1, Math.ceil(wordCount / 500));
  const readMin  = Math.max(1, Math.ceil(wordCount / 238));
  const scalePx  = (px) => Math.round(px * zoom / 100);

  const Sep = ({ k }) => (
    <div key={k} style={{ width:'1px', height:'18px', background: TspEd.color.border.light, margin:'0 3px', flexShrink:0 }} />
  );

  const tbBtn = (label, active, onClick, title, extraStyle) => (
    <button key={label+title} title={title} onClick={onClick}
      style={{ padding:'3px 7px', fontSize:'11px', fontWeight:600, borderRadius:'4px', border:'none', cursor:'pointer',
               fontFamily: TspEd.font.family, transition:'all 0.1s',
               background: active ? st.goldBg : 'transparent',
               color:      active ? st.gold   : TspEd.color.text.secondary,
               ...extraStyle }}>
      {label}
    </button>
  );

  // ── JSX ─────────────────────────────────────────────────────────────────────

  return (
    <div style={{ display:'flex', flexDirection:'column', minWidth:0, flex:1 }}>

      {/* ══ Toolbar Row 1 ════════════════════════════════════════════════════ */}
      <div style={{ background: TspEd.color.bg.card, border:`1px solid ${TspEd.color.border.light}`,
                    borderBottom:'none', display:'flex', alignItems:'center', flexWrap:'wrap', gap:'0' }}>

        {/* Mode switcher */}
        <div style={{ display:'flex', padding:'5px 6px', borderRight:`1px solid ${TspEd.color.border.light}`, gap:'1px', flexShrink:0 }}>
          {[['normal','Normal',TspEd.color.text.secondary],['redline','Redline',st.crimson],['outline','Outline',st.cobalt]].map(([m,lbl,c]) => (
            <button key={m} onClick={() => onModeChange && onModeChange(m)}
              style={{ padding:'3px 9px', fontSize:'11px', fontWeight:500, borderRadius:'4px', border:'none', cursor:'pointer',
                       fontFamily: TspEd.font.family, background: mode===m ? `${c}18` : 'transparent',
                       color: mode===m ? c : TspEd.color.text.tertiary, transition:'all 0.12s' }}>
              {lbl}
            </button>
          ))}
        </div>

        {/* Styles / Font / Size */}
        <div style={{ display:'flex', alignItems:'center', gap:'4px', padding:'4px 8px', borderRight:`1px solid ${TspEd.color.border.light}`, flexShrink:0 }}>
          <select value={styleVal} onChange={e => applyStyle(e.target.value)}
            style={{ padding:'3px 5px', fontSize:'11px', borderRadius:'4px', border:`1px solid ${TspEd.color.border.light}`,
                     background: TspEd.color.bg.secondary, color: TspEd.color.text.primary,
                     fontFamily: TspEd.font.family, cursor:'pointer', width:'88px' }}>
            <option value="p">Normal</option>
            <option value="h1">Heading 1</option>
            <option value="h2">Heading 2</option>
            <option value="h3">Heading 3</option>
            <option value="blockquote">Block Quote</option>
            <option value="pre">Code</option>
          </select>
          <select value={fontFamily} onChange={e => applyFont(e.target.value)}
            style={{ padding:'3px 5px', fontSize:'11px', borderRadius:'4px', border:`1px solid ${TspEd.color.border.light}`,
                     background: TspEd.color.bg.secondary, color: TspEd.color.text.primary,
                     fontFamily: TspEd.font.family, cursor:'pointer', width:'100px' }}>
            {FONT_OPTIONS.map(([val, label]) => <option key={val} value={val}>{label}</option>)}
          </select>
          <select value={fontSize} onChange={e => applySize(e.target.value)}
            style={{ padding:'3px 4px', fontSize:'11px', borderRadius:'4px', border:`1px solid ${TspEd.color.border.light}`,
                     background: TspEd.color.bg.secondary, color: TspEd.color.text.primary,
                     fontFamily: TspEd.font.mono, cursor:'pointer', width:'46px', textAlign:'center' }}>
            {SIZE_OPTIONS.map(s => <option key={s} value={s}>{s}</option>)}
          </select>
        </div>

        {/* B I U S */}
        <div style={{ display:'flex', alignItems:'center', padding:'4px 4px', borderRight:`1px solid ${TspEd.color.border.light}`, gap:'1px', flexShrink:0 }}>
          {tbBtn('B', fmtState.bold,          () => execCmd('bold'),          'Bold (Ctrl+B)',          { fontWeight:900, fontStyle:'normal', textDecoration:'none', fontSize:'12px' })}
          {tbBtn('I', fmtState.italic,        () => execCmd('italic'),        'Italic (Ctrl+I)',        { fontStyle:'italic', textDecoration:'none', fontWeight:500 })}
          {tbBtn('U', fmtState.underline,     () => execCmd('underline'),     'Underline (Ctrl+U)',     { textDecoration:'underline', fontStyle:'normal' })}
          {tbBtn('S', fmtState.strikeThrough, () => execCmd('strikeThrough'), 'Strikethrough',          { textDecoration:'line-through', fontStyle:'normal' })}
        </div>

        {/* Text color + highlight */}
        <div style={{ display:'flex', alignItems:'center', gap:'5px', padding:'4px 8px', borderRight:`1px solid ${TspEd.color.border.light}`, flexShrink:0 }}>
          <label title="Text color" style={{ display:'flex', flexDirection:'column', alignItems:'center', gap:'1px', cursor:'pointer', position:'relative' }}>
            <span style={{ fontSize:'12px', fontWeight:700, color: TspEd.color.text.secondary, lineHeight:1 }}>A</span>
            <div style={{ height:'3px', width:'14px', background: textColor, borderRadius:'1px' }} />
            <input type="color" value={textColor}
              onChange={e => { setTextColor(e.target.value); execCmd('foreColor', e.target.value); }}
              style={{ position:'absolute', opacity:0, width:1, height:1, pointerEvents:'none' }}
              id="se-txtcolor" />
            <span onClick={() => document.getElementById('se-txtcolor').click()} style={{ position:'absolute', inset:0, cursor:'pointer' }} />
          </label>
          <label title="Highlight" style={{ display:'flex', flexDirection:'column', alignItems:'center', gap:'1px', cursor:'pointer', position:'relative' }}>
            <span style={{ fontSize:'11px', color: TspEd.color.text.secondary, lineHeight:1 }}>▣</span>
            <div style={{ height:'3px', width:'14px', background: hlColor, borderRadius:'1px' }} />
            <input type="color" value={hlColor}
              onChange={e => { setHlColor(e.target.value); execCmd('hiliteColor', e.target.value); }}
              style={{ position:'absolute', opacity:0, width:1, height:1, pointerEvents:'none' }}
              id="se-hlcolor" />
            <span onClick={() => document.getElementById('se-hlcolor').click()} style={{ position:'absolute', inset:0, cursor:'pointer' }} />
          </label>
        </div>

        {/* Alignment */}
        <div style={{ display:'flex', alignItems:'center', padding:'4px 4px', borderRight:`1px solid ${TspEd.color.border.light}`, gap:'1px', flexShrink:0 }}>
          {[['≡L','justifyLeft',fmtState.justifyLeft,'Align left'],
            ['≡C','justifyCenter',fmtState.justifyCenter,'Center'],
            ['≡R','justifyRight',fmtState.justifyRight,'Align right'],
            ['≡J','justifyFull',fmtState.justifyFull,'Justify']].map(([lbl,cmd,act,ttl]) =>
            tbBtn(lbl, act, () => execCmd(cmd), ttl, { fontSize:'10px', padding:'3px 5px' })
          )}
        </div>

        {/* Lists + Indent */}
        <div style={{ display:'flex', alignItems:'center', padding:'4px 4px', borderRight:`1px solid ${TspEd.color.border.light}`, gap:'1px', flexShrink:0 }}>
          {[['•','insertUnorderedList',fmtState.insertUnorderedList,'Bullet list',{fontSize:'14px'}],
            ['1.','insertOrderedList', fmtState.insertOrderedList, 'Numbered list',{}],
            ['→|','indent',           false,                       'Indent',{}],
            ['|←','outdent',          false,                       'Outdent',{}]].map(([lbl,cmd,act,ttl,es]) =>
            tbBtn(lbl, act, () => execCmd(cmd), ttl, { padding:'3px 5px', ...es })
          )}
        </div>

        {/* Insert */}
        <div style={{ display:'flex', alignItems:'center', padding:'4px 4px', borderRight:`1px solid ${TspEd.color.border.light}`, gap:'1px', flexShrink:0 }}>
          {tbBtn('⊞',  false, insertTable,   'Insert table',   { fontSize:'13px', padding:'3px 6px' })}
          {tbBtn('', false, insertLink,    'Insert link',    { fontSize:'11px' })}
          {tbBtn('◉',  false, insertComment, 'Add comment',    { fontSize:'12px' })}
        </div>

        {/* Bates */}
        <div style={{ position:'relative', padding:'4px 6px', borderRight:`1px solid ${TspEd.color.border.light}`, flexShrink:0 }}>
          <button onClick={() => setBatesOpen(!batesOpen)}
            style={{ padding:'3px 8px', fontSize:'11px', fontWeight:500, borderRadius:'4px', cursor:'pointer',
                     fontFamily: TspEd.font.family, transition:'all 0.1s',
                     border:`1px solid ${batesOpen ? st.gold : TspEd.color.border.light}`,
                     background: batesOpen ? st.goldBg : 'transparent',
                     color:      batesOpen ? st.gold   : TspEd.color.text.secondary }}>
            ⊟ Bates
          </button>
          {batesOpen && (
            <div style={{ position:'absolute', top:'34px', left:0, background: TspEd.color.bg.card,
                          border:`1px solid ${TspEd.color.border.light}`, borderRadius:'8px',
                          padding:'12px 14px', width:'200px', zIndex:500, boxShadow:'0 8px 24px rgba(0,0,0,0.14)' }}>
              <div style={{ fontSize:'10px', fontWeight:600, color: TspEd.color.text.tertiary,
                            textTransform:'uppercase', letterSpacing:'0.07em', marginBottom:'8px' }}>Bates Configuration</div>
              {[['Prefix','MER-'],['Start No.','000001'],['Padding','6 digits'],['Position','Bottom right']].map(([k,v]) => (
                <div key={k} style={{ display:'flex', justifyContent:'space-between', marginBottom:'6px',
                                      paddingBottom:'6px', borderBottom:`1px solid ${TspEd.color.border.light}` }}>
                  <span style={{ fontSize:'11px', color: TspEd.color.text.secondary }}>{k}</span>
                  <span style={{ fontSize:'11px', fontWeight:600, color: st.gold, fontFamily: TspEd.font.mono }}>{v}</span>
                </div>
              ))}
              <div style={{ display:'flex', gap:'6px', marginTop:'4px' }}>
                <button style={{ ...st.btnSecondary, flex:1, fontSize:'11px' }} onClick={() => setBatesOpen(false)}>Preview</button>
                <button style={{ ...st.btnPrimary,   flex:1, fontSize:'11px' }} onClick={() => setBatesOpen(false)}>Apply</button>
              </div>
            </div>
          )}
        </div>

        <div style={{ flex:1 }} />

        {/* Track / Find / Save */}
        <div style={{ display:'flex', alignItems:'center', gap:'4px', padding:'4px 10px', flexShrink:0 }}>
          <button onClick={() => setTrackChanges(!trackChanges)}
            style={{ padding:'3px 9px', fontSize:'11px', fontWeight:500, borderRadius:'4px', cursor:'pointer',
                     fontFamily: TspEd.font.family,
                     border:`1px solid ${trackChanges ? st.gold : TspEd.color.border.light}`,
                     background: trackChanges ? st.goldBg : 'transparent',
                     color:      trackChanges ? st.gold   : TspEd.color.text.secondary }}>
            ◆ Track
          </button>
          <button onClick={() => findOpen ? closeFind() : setFindOpen(true)}
            style={{ padding:'3px 9px', fontSize:'11px', fontWeight:500, borderRadius:'4px', cursor:'pointer',
                     fontFamily: TspEd.font.family,
                     border:`1px solid ${findOpen ? st.cobalt : TspEd.color.border.light}`,
                     background: findOpen ? st.cobaltBg : 'transparent',
                     color:      findOpen ? st.cobalt   : TspEd.color.text.secondary }}>
            cmd Find
          </button>
          <button style={{ ...st.btnPrimary, fontSize:'11px', padding:'4px 12px' }}
            onClick={() => setAutoSaved(Date.now())}>↑ Save</button>
        </div>
      </div>

      {/* ══ Track Changes contextual row ══════════════════════════════════════ */}
      {trackChanges && (
        <div style={{ background: st.goldBg, border:`1px solid ${st.goldBorder}`, borderTop:'none',
                      padding:'5px 14px', display:'flex', alignItems:'center', gap:'10px' }}>
          <span style={{ fontSize:'11px', fontWeight:600, color: st.goldDeep }}>◆ Track Changes ON</span>
          {pendingChanges.length > 0 && (
            <span style={{ ...st.tag, background: st.goldBg, color: st.gold, border:`1px solid ${st.goldBorder}` }}>
              {pendingChanges.length} pending
            </span>
          )}
          <div style={{ flex:1 }} />
          <button onClick={acceptAllChanges}
            style={{ padding:'3px 10px', fontSize:'11px', fontWeight:600, borderRadius:'4px', cursor:'pointer',
                     fontFamily: TspEd.font.family, border:`1px solid ${st.emerald}55`,
                     background:'rgba(5,150,105,0.08)', color: st.emerald }}>
            ok Accept All
          </button>
          <button onClick={rejectAllChanges}
            style={{ padding:'3px 10px', fontSize:'11px', fontWeight:600, borderRadius:'4px', cursor:'pointer',
                     fontFamily: TspEd.font.family, border:`1px solid ${st.crimson}55`,
                     background:'rgba(185,28,28,0.08)', color: st.crimson }}>
            x Reject All
          </button>
        </div>
      )}

      {/* ══ Find & Replace panel ══════════════════════════════════════════════ */}
      {findOpen && (
        <div style={{ background: TspEd.color.bg.secondary, border:`1px solid ${TspEd.color.border.light}`,
                      borderTop:'none', padding:'8px 12px', display:'flex', alignItems:'center', gap:'8px', flexWrap:'wrap' }}>
          <input value={findQuery} autoFocus
            onChange={e => { setFindQuery(e.target.value); runFind(e.target.value, caseSens, wholeWord); }}
            placeholder="Find…"
            style={{ width:'160px', padding:'5px 9px', fontSize:'12px', borderRadius:'5px',
                     border:`1px solid ${TspEd.color.border.light}`, background: TspEd.color.bg.card,
                     color: TspEd.color.text.primary, fontFamily: TspEd.font.family, outline:'none' }} />
          <span style={{ fontSize:'11px', color: TspEd.color.text.tertiary, fontFamily: TspEd.font.mono, minWidth:'52px' }}>
            {findQuery ? (matchCount > 0 ? `${matchIndex} / ${matchCount}` : 'No match') : ''}
          </span>
          <button onClick={prevMatch} style={{ ...st.btnGhost, padding:'3px 9px', fontSize:'12px' }}>←</button>
          <button onClick={nextMatch} style={{ ...st.btnGhost, padding:'3px 9px', fontSize:'12px' }}>→</button>
          <div style={{ width:'1px', height:'18px', background: TspEd.color.border.light }} />
          <input value={replaceQuery} onChange={e => setReplaceQuery(e.target.value)}
            placeholder="Replace with…"
            style={{ width:'160px', padding:'5px 9px', fontSize:'12px', borderRadius:'5px',
                     border:`1px solid ${TspEd.color.border.light}`, background: TspEd.color.bg.card,
                     color: TspEd.color.text.primary, fontFamily: TspEd.font.family, outline:'none' }} />
          <button onClick={doReplace}    style={{ ...st.btnSecondary, fontSize:'11px' }}>Replace</button>
          <button onClick={doReplaceAll} style={{ ...st.btnSecondary, fontSize:'11px' }}>All</button>
          <div style={{ width:'1px', height:'18px', background: TspEd.color.border.light }} />
          <label style={{ display:'flex', alignItems:'center', gap:'4px', fontSize:'11px', color: TspEd.color.text.secondary, cursor:'pointer' }}>
            <input type="checkbox" checked={caseSens}
              onChange={e => { setCaseSens(e.target.checked); if (findQuery) runFind(findQuery, e.target.checked, wholeWord); }} />
            Aa
          </label>
          <label style={{ display:'flex', alignItems:'center', gap:'4px', fontSize:'11px', color: TspEd.color.text.secondary, cursor:'pointer' }}>
            <input type="checkbox" checked={wholeWord}
              onChange={e => { setWholeWord(e.target.checked); if (findQuery) runFind(findQuery, caseSens, e.target.checked); }} />
            \bWord
          </label>
          <button onClick={closeFind}
            style={{ ...st.btnGhost, marginLeft:'auto', fontSize:'14px', padding:'0 8px', color: TspEd.color.text.tertiary }}><Icons.X size={11}/></button>
        </div>
      )}

      {/* ══ Paper scroll area ════════════════════════════════════════════════ */}
      <div style={{ background:'#F0EFEB', overflowY:'auto', overflowX:'auto', flex:1, minHeight:'380px',
                    border:`1px solid ${TspEd.color.border.light}`, borderTop:'none', borderBottom:'none' }}>
        <div style={{ padding:'28px 0 40px', display:'flex', justifyContent:'center',
                      minHeight:`${scalePx(1056 + 80)}px` }}>
          <div style={{
            width: `${scalePx(816)}px`,
            minHeight: `${scalePx(1056)}px`,
            background: '#FAFAF7',
            padding: `${scalePx(72)}px ${scalePx(90)}px`,
            boxShadow: '0 2px 16px rgba(0,0,0,0.14), 0 1px 4px rgba(0,0,0,0.06)',
            flexShrink: 0,
          }}>
            <div
              ref={editorRef}
              contentEditable={true}
              suppressContentEditableWarning={true}
              spellCheck={true}
              onInput={handleInput}
              onMouseUp={updateFmtState}
              onKeyUp={updateFmtState}
              style={{
                outline: 'none',
                minHeight: `${scalePx(912)}px`,
                fontFamily: fontFamily,
                fontSize: `${scalePx(12)}pt`,
                lineHeight: 1.8,
                color: '#1E293B',
              }}
            />
          </div>
        </div>
      </div>

      {/* ══ Status bar ═══════════════════════════════════════════════════════ */}
      <div style={{ background: TspEd.color.bg.card, border:`1px solid ${TspEd.color.border.light}`,
                    borderTop:'none', borderRadius:`0 0 ${TspEd.radius.lg} ${TspEd.radius.lg}`,
                    padding:'5px 14px', display:'flex', gap:'10px', alignItems:'center', flexWrap:'wrap' }}>

        <span style={{ fontSize:'10px', color: TspEd.color.text.tertiary }}>
          <span style={{ fontWeight:500, color: TspEd.color.text.secondary }}>Words:</span> {wordCount.toLocaleString()}
        </span>
        <span style={{ fontSize:'10px', color: TspEd.color.text.tertiary }}>
          <span style={{ fontWeight:500, color: TspEd.color.text.secondary }}>Chars:</span> {charCount.toLocaleString()}
        </span>
        <span style={{ fontSize:'10px', color: TspEd.color.text.tertiary }}>~{pageEst} {pageEst===1?'page':'pages'}</span>
        <span style={{ fontSize:'10px', color: TspEd.color.text.tertiary }}>~{readMin} min read</span>
        <span style={{ fontSize:'10px', fontWeight:500, color: saveInfo.color, whiteSpace:'nowrap' }}>{saveInfo.label}</span>

        <div style={{ flex:1 }} />

        {/* Timer */}
        <div onClick={() => setTimerPaused(!timerPaused)}
          style={{ display:'inline-flex', alignItems:'center', gap:'4px', padding:'2px 8px', borderRadius:'4px',
                   cursor:'pointer', transition:'all 0.1s',
                   background: timerPaused ? TspEd.color.bg.secondary : 'rgba(5,150,105,0.07)',
                   border:`1px solid ${timerPaused ? TspEd.color.border.light : st.emerald+'44'}` }}>
          <span style={{ fontSize:'10px', color: timerPaused ? TspEd.color.text.tertiary : st.emerald }}>
            {timerPaused ? '▶' : ''}
          </span>
          <span style={{ fontSize:'10px', fontFamily: TspEd.font.mono, fontWeight:600,
                         color: timerPaused ? TspEd.color.text.tertiary : st.emerald }}>00:31:47</span>
          <span style={{ fontSize:'10px', color: TspEd.color.text.tertiary }}>· #4421</span>
        </div>

        {/* Zoom */}
        <div style={{ display:'flex', alignItems:'center', gap:'2px' }}>
          <button onClick={() => setZoom(z => Math.max(50, z - 25))}
            style={{ ...st.btnGhost, padding:'1px 6px', fontSize:'14px', lineHeight:1, fontWeight:700 }}>−</button>
          <select value={zoom} onChange={e => setZoom(Number(e.target.value))}
            style={{ padding:'2px 4px', fontSize:'10px', borderRadius:'3px', cursor:'pointer',
                     border:`1px solid ${TspEd.color.border.light}`, background: TspEd.color.bg.secondary,
                     color: TspEd.color.text.primary, fontFamily: TspEd.font.mono, width:'52px' }}>
            {[50,75,100,125,150,175,200].map(z => <option key={z} value={z}>{z}%</option>)}
          </select>
          <button onClick={() => setZoom(z => Math.min(200, z + 25))}
            style={{ ...st.btnGhost, padding:'1px 6px', fontSize:'14px', lineHeight:1, fontWeight:700 }}>+</button>
        </div>

        <span style={{ fontSize:'10px', color: TspEd.color.text.tertiary, padding:'1px 6px', borderRadius:'3px',
                       background: TspEd.color.bg.secondary, border:`1px solid ${TspEd.color.border.light}` }}>EN</span>
      </div>
    </div>
  );
}

window.StudioEditor = StudioEditor;
