/* app-paper.jsx — 방향 A: 종이 처방전(충실 재현) + 정답 확인 아코디언 */

// caseSignature 생성 — 반복 사례 식별용 hash
function ocrSimpleHash(str) {
  let h = 5381;
  for (let i = 0; i < str.length; i++) {
    h = ((h << 5) + h) ^ str.charCodeAt(i);
  }
  return (h >>> 0).toString(36);
}

function buildOcrCaseSignature(analysis) {
  const drugs = [
    ...(analysis.matchedCvDrugs || []),
    ...(analysis.recognizedNonCvDrugs || []),
  ];
  let tokens = drugs
    .map(d => {
      const g = (d.generic || '').toLowerCase().replace(/\s+/g, '');
      const dose = (d.dose || '').toLowerCase().replace(/\s+/g, '');
      return g + (dose ? '+' + dose : '');
    })
    .filter(Boolean)
    .sort();

  if (tokens.length < 2) {
    tokens = (analysis.rawDrugs || [])
      .map(d => (d.original_name || d.rawName || d.name || '').toLowerCase().replace(/\s+/g, ''))
      .filter(Boolean)
      .sort();
  }

  return ocrSimpleHash(tokens.join('|'));
}

// drug_class → 질환명 + 역할 설명 (OCR 비CV 약물 표시용)
const NON_CV_CLASS_DISPLAY = {
  // 당뇨
  'SGLT-2i': { disease: '제2형 당뇨병', role: 'SGLT-2 억제제 — 혈당 강하 및 심혈관·신장 보호' },
  'Sodium-Glucose cotransporter-2 inhibitors (SGLT-2 inh)': { disease: '제2형 당뇨병', role: 'SGLT-2 억제제 — 혈당 강하 및 심혈관·신장 보호' },
  'SGLT-2i+Biguanide 복합제': { disease: '제2형 당뇨병', role: 'SGLT-2i + Metformin 복합 — 이중 혈당 강하' },
  'DPP-4i': { disease: '제2형 당뇨병', role: 'DPP-4 억제제 — 인크레틴 계열 혈당 강하' },
  'Dipeptidyl peptidase-4 inhibitor (DPP-4 inh)': { disease: '제2형 당뇨병', role: 'DPP-4 억제제 — 인크레틴 계열 혈당 강하' },
  'DPP-4i+SGLT-2i 복합제': { disease: '제2형 당뇨병', role: 'DPP-4i + SGLT-2i 복합 — 이중 혈당 강하' },
  'GLP-1 RA': { disease: '제2형 당뇨병', role: 'GLP-1 수용체 작용제 — 혈당 강하 + 체중 감소' },
  'GLP-1 유사체': { disease: '제2형 당뇨병', role: 'GLP-1 수용체 작용제 — 혈당 강하 + 체중 감소' },
  'GIP/GLP-1 이중 수용체 작용제': { disease: '제2형 당뇨병', role: 'GIP/GLP-1 이중 작용제 — 혈당 강하 + 체중 감소' },
  'Biguanide': { disease: '제2형 당뇨병', role: 'Biguanide — 간 당 생성 억제, 1차 경구 혈당 강하' },
  'Biguanides': { disease: '제2형 당뇨병', role: 'Biguanide — 간 당 생성 억제, 1차 경구 혈당 강하' },
  '복합 경구혈당강하제': { disease: '제2형 당뇨병', role: '복합 경구 혈당 강하제' },
  'Sulfonylurea': { disease: '제2형 당뇨병', role: '설폰요소제 — 인슐린 분비 촉진' },
  'Sulfonylurea(SU)': { disease: '제2형 당뇨병', role: '설폰요소제 — 인슐린 분비 촉진' },
  'Meglitinide': { disease: '제2형 당뇨병', role: 'Meglitinide — 식후 인슐린 분비 촉진' },
  'Thiazolidinedione(TZD)': { disease: '제2형 당뇨병', role: 'TZD — 인슐린 감수성 개선' },
  'α-glucosidase i': { disease: '제2형 당뇨병', role: 'α-글루코시다제 억제제 — 식후 혈당 상승 억제' },
  // BPH / 비뇨기
  'α1-차단제': { disease: '전립선비대증', role: 'α1 차단제 — 전립선·방광경부 평활근 이완, 배뇨 증상 완화' },
  '교감신경 억제제 (α-blocker)': { disease: '전립선비대증', role: 'α 차단제 — 전립선·방광경부 이완, 배뇨 증상 완화' },
  '5-ARI': { disease: '전립선비대증', role: '5α-환원효소 억제제 — 전립선 크기 감소, 진행 억제' },
  '5-α-reductase i': { disease: '전립선비대증', role: '5α-환원효소 억제제 — 전립선 크기 감소, 진행 억제' },
  'a-blocker+5-α-reductase i': { disease: '전립선비대증', role: 'α 차단제 + 5α-환원효소 억제제 복합 — 배뇨 증상 완화 + 진행 억제' },
  'β3 작용제(OAB)': { disease: '과민성 방광', role: 'β3 작용제 — 방광 이완, 절박뇨·빈뇨 완화' },
  '항콜린제(OAB)': { disease: '과민성 방광', role: '항콜린제 — 방광 과민성 억제, 절박뇨 완화' },
  // 정신신경
  'SSRI': { disease: '우울증 / 불안장애', role: 'SSRI — 세로토닌 재흡수 억제 항우울·항불안' },
  'SNRI': { disease: '우울증 / 불안장애', role: 'SNRI — 세로토닌·노르에피네프린 재흡수 억제' },
  'NaSSA/TeCA': { disease: '우울증', role: 'NaSSA — 노르에피네프린·세로토닌 강화 항우울' },
  '항우울제': { disease: '우울증', role: '항우울제' },
  'Z-drug': { disease: '불면증', role: 'Z-drug — 수면 개시·유지 보조 (GABA 작용)' },
  '수면보조제': { disease: '불면증', role: '수면 개시·유지 보조' },
  'BZD': { disease: '불안장애 / 수면장애', role: '벤조디아제핀 — 불안 완화 및 수면 보조' },
  'BZD 단시간': { disease: '불안장애 / 수면장애', role: '단시간 작용 BZD — 수면 보조' },
  'BZD 중장시간': { disease: '불안장애 / 수면장애', role: '중·장시간 작용 BZD — 불안 완화' },
  '기분안정제': { disease: '양극성 장애', role: '기분 안정제 — 기분 삽화 예방 및 치료' },
  '기분조절제': { disease: '양극성 장애', role: '기분 조절제' },
  // 소화기
  'P-CAB': { disease: '위식도역류 / 소화성궤양', role: 'P-CAB — 칼륨 이온 경쟁적 위산 억제, 빠른 효과 발현' },
  'Potassium-Competitive Acid Blocker (P-CAB)': { disease: '위식도역류 / 소화성궤양', role: 'P-CAB — 칼륨 이온 경쟁적 위산 억제, 빠른 효과 발현' },
  'PPI': { disease: '위식도역류 / 소화성궤양', role: 'PPI — 프로톤 펌프 억제, 위산 분비 차단' },
  'Proton Pump Inhibitor (PPI)': { disease: '위식도역류 / 소화성궤양', role: 'PPI — 프로톤 펌프 억제, 위산 분비 차단' },
  'Histamine H2 Receptor Antagonist (H2 blocker)': { disease: '위식도역류 / 소화성궤양', role: 'H2 차단제 — 위산 분비 감소' },
  '위장운동촉진제': { disease: '기능성 소화불량 / 위장 운동 장애', role: '위장 운동 촉진 — 구역·팽만·역류 완화' },
  '소화관 운동 조절': { disease: '기능성 소화불량 / 위장 운동 장애', role: '소화관 운동 조절' },
  '위장관 운동 조절제': { disease: '기능성 소화불량 / 위장 운동 장애', role: '위장 운동 촉진 — 구역·팽만 완화' },
  '위장관운동조절제': { disease: '기능성 소화불량 / 위장 운동 장애', role: '위장 운동 촉진 — 구역·팽만 완화' },
  '점액용해제': { disease: '호흡기 점액 과다', role: '점액 용해 — 기도 분비물 점도 감소, 기침 완화' },
  '점액 용해제 (황기반 -steine 류)': { disease: '호흡기 점액 과다', role: '점액 용해 — 기도 분비물 점도 감소, 기침 완화' },
  '거담제': { disease: '호흡기 점액 과다', role: '거담 — 기도 분비물 배출 촉진' },
  '정장제': { disease: '장내 세균 불균형', role: '유산균 제제 — 장내 균총 정상화, 소화기 증상 완화' },
  '제산제': { disease: '위산 과다 / 속쓰림', role: '제산제 — 위산 중화, 속쓰림 완화' },
  // 인지기능
  '콜린에스테라제억제제': { disease: '치매 / 인지기능 저하', role: '콜린에스테라제 억제 — 아세틸콜린 분해 억제, 인지 기능 유지' },
  'Anticholine-estrase': { disease: '치매 / 인지기능 저하', role: '콜린에스테라제 억제 — 아세틸콜린 분해 억제, 인지 기능 유지' },
  '콜린성 인지개선제': { disease: '인지기능 저하', role: '콜린성 전구체 — 아세틸콜린 합성 보조, 인지 개선' },
  'Ach 전달 촉진제': { disease: '인지기능 저하', role: 'ACh 전달 촉진 — 콜린성 신경 전달 보조' },
  'Ach 합성 촉진제': { disease: '인지기능 저하', role: 'ACh 합성 촉진 — 콜린성 신경 전달 보조' },
  '뇌기능개선제': { disease: '인지기능 저하', role: '뇌 기능 개선제' },
};

function getNonCvDisplay(drug) {
  const dc = drug.drug_class || '';
  if (NON_CV_CLASS_DISPLAY[dc]) return NON_CV_CLASS_DISPLAY[dc];
  // 부분 일치 fallback (예: "SGLT-2i 복합제" → SGLT-2i)
  const partial = Object.keys(NON_CV_CLASS_DISPLAY).find(k => dc.startsWith(k) || k.startsWith(dc));
  if (partial) return NON_CV_CLASS_DISPLAY[partial];
  return { disease: drug.major || '비CV 약물', role: dc };
}

function buildOcrSavePayload(analysis, uid, source) {
  const safeAnalysis = window.rxHelpers && window.rxHelpers.sanitizeOcrAnalysisForStorage
    ? window.rxHelpers.sanitizeOcrAnalysisForStorage(analysis)
    : analysis;
  return {
    actorUid: uid,
    source: source,
    createdAt: firebase.firestore.FieldValue.serverTimestamp(),
    caseSignature: buildOcrCaseSignature(safeAnalysis),
    reviewStatus: 'saved',
    rawDrugs: safeAnalysis.rawDrugs || [],
    matchedCvDrugs: safeAnalysis.matchedCvDrugs || [],
    recognizedNonCvDrugs: safeAnalysis.recognizedNonCvDrugs || [],
    unmatchedDrugs: safeAnalysis.unmatchedDrugs || [],
    candidateContexts: safeAnalysis.candidateContexts || [],
    combinationScenarios: safeAnalysis.combinationScenarios || [],
    doseCounselingNotes: safeAnalysis.doseCounselingNotes || [],
    ocrProvider: 'google_vision',
  };
}

function AppPaper() {
  const s = useRxSession('__all');
  const { plan, currentUser } = React.useContext(window.AuthContext || {});
  const dxLabels = s.pattern.diseases;
  const [drawerOpen, setDrawerOpen] = React.useState(false);
  const [isLoadingOcr, setIsLoadingOcr] = React.useState(false);
  const [ocrStage, setOcrStage] = React.useState(null); // {label, pct}
  const [ocrDebugInfo, setOcrDebugInfo] = React.useState(null); // 관리자 전용 타이밍/모델 정보
  const ocrTimerRef = React.useRef(null);
  const [ocrResult, setOcrResult] = React.useState(null);
  const [ocrSaveStatus, setOcrSaveStatus] = React.useState(null); // null|'saving'|'saved'|'pii_blocked'|'failed'
  const [selectedScenarioIdx, setSelectedScenarioIdx] = React.useState(null);
  const [ocrImageBase64, setOcrImageBase64] = React.useState(null);
  const [sonnetRetried, setSonnetRetried] = React.useState(false);

  // admin-ocr 탭 직접 접근 가드
  React.useEffect(() => {
    if (s.tab === 'admin-ocr' && plan !== 'admin') {
      s.setTab('rx');
    }
  }, [s.tab, plan]);

  // EXIF orientation 파싱 (순수 JS, 외부 라이브러리 없음)
  const getExifOrientation = (buffer) => {
    const v = new DataView(buffer);
    if (v.getUint16(0) !== 0xFFD8) return 1; // JPEG 아님
    let o = 2;
    while (o < buffer.byteLength - 4) {
      if (v.getUint8(o) !== 0xFF) break;
      const marker = v.getUint16(o);
      const len = v.getUint16(o + 2);
      if (marker === 0xFFE1 && len >= 16 && v.getUint32(o + 4) === 0x45786966) {
        const tiff = o + 10;
        const le = v.getUint16(tiff) === 0x4949;
        const ifd = tiff + v.getUint32(tiff + 4, le);
        const count = v.getUint16(ifd, le);
        for (let i = 0; i < count && i < 20; i++) {
          const p = ifd + 2 + i * 12;
          if (v.getUint16(p, le) === 0x0112) return v.getUint16(p + 8, le);
        }
        return 1;
      }
      o += 2 + len;
    }
    return 1;
  };

  // fetch 대기 중 진행바를 target까지 점근적으로 증가시켜 '멈춤' 오해 제거
  const startOcrEstimate = (target = 88) => {
    if (ocrTimerRef.current) clearInterval(ocrTimerRef.current);
    ocrTimerRef.current = setInterval(() => {
      setOcrStage(prev => {
        if (!prev || prev.pct >= target) return prev;
        const next = prev.pct + Math.max(0.4, (target - prev.pct) * 0.04);
        return { ...prev, pct: Math.min(target, next) };
      });
    }, 200);
  };
  const stopOcrEstimate = () => {
    if (ocrTimerRef.current) {
      clearInterval(ocrTimerRef.current);
      ocrTimerRef.current = null;
    }
  };

  const handleOcr = async (e) => {
    const file = e.target.files?.[0];
    if (!file) return;

    setIsLoadingOcr(true);
    setOcrDebugInfo(null);
    setOcrStage({ label: '이미지 준비 중', pct: 5 });
    const tTotal = Date.now();
    try {
      const imageBase64 = await new Promise((resolve, reject) => {
        // 1단계: ArrayBuffer로 EXIF orientation 읽기
        const bufReader = new FileReader();
        bufReader.onerror = reject;
        bufReader.onload = (bufEv) => {
          const orientation = getExifOrientation(bufEv.target.result);

          // 2단계: DataURL로 이미지 로드 후 캔버스 회전 보정
          const dataReader = new FileReader();
          dataReader.onerror = reject;
          dataReader.onload = (dataEv) => {
            const img = new Image();
            img.onerror = reject;
            img.onload = () => {
              const swap = orientation === 6 || orientation === 8;
              const rawW = img.naturalWidth;
              const rawH = img.naturalHeight;

              // 올바른 표시 크기 계산 (rotation 후)
              let dispW = swap ? rawH : rawW;
              let dispH = swap ? rawW : rawH;
              const MAX_DIM = 1600;
              const scale = (dispW > MAX_DIM || dispH > MAX_DIM)
                ? MAX_DIM / Math.max(dispW, dispH)
                : 1;
              const cw = Math.round(dispW * scale);
              const ch = Math.round(dispH * scale);
              const dw = Math.round(rawW * scale);
              const dh = Math.round(rawH * scale);

              const canvas = document.createElement('canvas');
              canvas.width = cw;
              canvas.height = ch;
              const ctx = canvas.getContext('2d');

              // EXIF 회전 보정
              if (orientation === 3) {
                ctx.translate(cw, ch); ctx.rotate(Math.PI);
              } else if (orientation === 6) {
                ctx.translate(cw, 0); ctx.rotate(Math.PI / 2);
              } else if (orientation === 8) {
                ctx.translate(0, ch); ctx.rotate(-Math.PI / 2);
              }
              ctx.drawImage(img, 0, 0, dw, dh);
              resolve(canvas.toDataURL('image/jpeg', 0.8));
            };
            img.src = dataEv.target.result;
          };
          dataReader.readAsDataURL(file);
        };
        bufReader.readAsArrayBuffer(file);
      });

      setOcrImageBase64(imageBase64);
      setSonnetRetried(false);
      const tImagePrep = Date.now() - tTotal;
      setOcrStage({ label: 'AI가 처방전을 읽는 중', pct: 20 });
      startOcrEstimate(88);
      const tApiStart = Date.now();
      const res = await fetch('/api/ocr', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ imageBase64, provider: 'google_vision' }),
      });
      stopOcrEstimate();
      const tApiMs = Date.now() - tApiStart;
      setOcrStage({ label: '약품 정보 정리 중', pct: 90 });
      const data = await res.json();
      if (!res.ok) {
        const code = data.code ? ` (${data.code})` : '';
        throw new Error((data.message || data.error || 'OCR 요청에 실패했습니다.') + code);
      }

      let parsedDrugs = data.result || [];
      if (parsedDrugs.length === 0) throw new Error('약품을 인식하지 못했습니다.');

      // 1차 매칭
      setOcrStage({ label: '심혈관 약물 분석 중', pct: 96 });
      const tCvStart = Date.now();
      let analysis = rxHelpers.analyzeOcrDrugsForCv(parsedDrugs);

      // unmatched가 있으면 rawName 텍스트만 Sonnet으로 자동 교정
      const unmatchedRaw = (analysis.unmatchedDrugs || [])
        .map(u => u.original_name || '')
        .filter(Boolean);
      if (
        unmatchedRaw.length > 0 &&
        window.PHARMINFO_OCR_AUTO_CORRECT_ONLY === true
      ) {
        try {
          // 각 unmatched rawName마다 유사도 상위 15개 후보만 힌트로 전달 (전체 slice 금지)
          const seen = new Set();
          const drugList = unmatchedRaw.flatMap(raw =>
            (rxHelpers.fuzzyCandidates ? rxHelpers.fuzzyCandidates(raw, 15) : [])
          ).filter(n => n && !seen.has(n) && seen.add(n));
          const corrRes = await fetch('/api/ocr', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ correct_only: true, rawNames: unmatchedRaw, drugList }),
          });
          if (corrRes.ok) {
            const corrData = await corrRes.json();
            const corrected = corrData.corrected || [];
            parsedDrugs = parsedDrugs.map(drug => {
              const key = drug.rawName || drug.name || drug.brand || '';
              const idx = unmatchedRaw.indexOf(key);
              if (idx >= 0 && corrected[idx] && corrected[idx] !== key) {
                return { ...drug, name: corrected[idx] };
              }
              return drug;
            });
            analysis = rxHelpers.analyzeOcrDrugsForCv(parsedDrugs);
          }
        } catch (e) { console.error('[OCR Correction Error]:', e); }
      }

      const mappedPattern = rxHelpers.createCustomPatternFromOcr(parsedDrugs, analysis);
      const tCvMs = Date.now() - tCvStart;
      setOcrStage({ label: '완료', pct: 100 });
      setSelectedScenarioIdx(null);
      if (plan === 'admin') {
        setOcrDebugInfo({
          totalMs: Date.now() - tTotal,
          imagePrepMs: tImagePrep,
          apiCallMs: tApiMs,
          cvAnalysisMs: tCvMs,
          serverMeta: data.ocr_meta || null,
        });
      }
      setOcrResult(mappedPattern);
      setOcrSaveStatus(null);

      // OCR 개선센터 자동 저장 (plan과 무관하게 공통 경로)
      if (currentUser) {
        const source = plan === 'admin' ? 'admin' : 'user';
        const payload = buildOcrSavePayload(analysis, currentUser.uid, source);
        const preflight = rxHelpers.qualityFeedbackPreflight(JSON.stringify(payload));
        if (!preflight.allowed) {
          setOcrSaveStatus('pii_blocked');
        } else {
          setOcrSaveStatus('saving');
          firebase.firestore().collection('ocr_review_cases').add(payload)
            .then(() => setOcrSaveStatus('saved'))
            .catch(err2 => {
              console.error('[OCR Save]:', err2);
              setOcrSaveStatus('failed');
            });
        }
      }
    } catch (err) {
      alert(err.message);
    } finally {
      stopOcrEstimate();
      setIsLoadingOcr(false);
      setOcrStage(null);
      e.target.value = '';
    }
  };

  const applyOcrResult = () => {
    if (ocrResult) {
      s.setCustomPattern(ocrResult);
      s.setRevealed(false);
      setOcrResult(null);
    }
  };

  const revertOcrDrug = (idx) => {
    setOcrResult(prev => {
      if (!prev) return prev;
      const newDrugs = [...prev.drugs];
      const target = newDrugs[idx];
      newDrugs[idx] = {
        ...target,
        brand: target.original_name, // revert to original
        corrected: false
      };
      return { ...prev, drugs: newDrugs };
    });
  };

  const submitOcrFeedback = () => {
    if (plan === 'admin') {
      s.setTab('admin-ocr');
      return;
    }
    const detail = window.prompt('서비스 개선을 위해 확인이 필요한 약물명을 적어주세요.');
    const payloadText = JSON.stringify({
      detail: detail || '',
      raw: ocrResult && ocrResult.ocr_analysis ? ocrResult.ocr_analysis.rawDrugs : [],
      matched: ocrResult && ocrResult.ocr_analysis ? ocrResult.ocr_analysis.matchedCvDrugs : [],
      recognizedNonCv: ocrResult && ocrResult.ocr_analysis ? ocrResult.ocr_analysis.recognizedNonCvDrugs : [],
      unmatched: ocrResult && ocrResult.ocr_analysis ? ocrResult.ocr_analysis.unmatchedDrugs : [],
    });
    const preflight = rxHelpers.qualityFeedbackPreflight(payloadText);
    if (!preflight.allowed) {
      alert(preflight.message);
      return;
    }
    alert('품질개선 전송 API 연결 전입니다. 현재 화면에서는 민감정보 차단 사전검사만 수행했습니다.');
  };

  const ocrReferenceLabel = (drug) => {
    if (!drug || !drug.representativeBrand || drug.representativeBrand === drug.displayName) return '';
    const label = drug.row && drug.row.is_reference === true ? '오리지널' : '대표품목';
    return `(${label}: ${drug.representativeBrand})`;
  };

  const ocrConfidenceBadge = (drug) => {
    if (!drug) return null;
    if (drug.ingredientConfidence === 'medium') {
      return <span style={{ fontSize: 11, color: RX.amber, background: RX.amberBg, padding: '2px 6px', borderRadius: 4 }}>확인 필요</span>;
    }
    return <span style={{ fontSize: 11, color: RX.sub, border: `1px solid ${RX.line}`, borderRadius: 4, padding: '2px 5px' }}>{drug.ingredientConfidence}</span>;
  };

  const ocrAiCorrectionBadge = (drug) => {
    const c = drug && drug.aiCorrection;
    if (!c) return null;
    const model = String(c.modelUsed || '').includes('sonnet') ? 'Sonnet' : 'Haiku';
    const color = c.confidence === 'high' ? RX.teal : (c.confidence === 'low' ? RX.rose : RX.amber);
    return (
      <span style={{ fontSize: 11, color, background: c.escalated ? RX.amberBg : RX.tealTint, padding: '2px 6px', borderRadius: 4 }}>
        AI 교정: {model} {c.confidence || ''}
      </span>
    );
  };

  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: RX.shell, fontFamily: '"Pretendard Variable", Pretendard, system-ui, sans-serif', position: 'relative' }}>
      <AppTabs active={s.tab} onChange={s.setTab} onMenu={() => setDrawerOpen(true)} onOcr={handleOcr} />
      
      {isLoadingOcr && (
        <div style={{ position: 'absolute', inset: 0, background: 'rgba(255,255,255,0.82)', zIndex: 999, display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column', padding: 24 }}>
          <div style={{ width: '100%', maxWidth: 360 }}>
            <div style={{ fontSize: 16, fontWeight: 800, color: RX.teal, textAlign: 'center' }}>처방전 분석 중</div>
            <div style={{ marginTop: 16, height: 10, borderRadius: 6, background: RX.lineSoft, overflow: 'hidden' }}>
              <div style={{
                width: `${Math.round((ocrStage && ocrStage.pct) || 0)}%`,
                height: '100%', background: RX.teal, borderRadius: 6,
                transition: 'width 0.25s ease',
              }} />
            </div>
            <div style={{ marginTop: 8, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
              <span style={{ fontSize: 13, color: RX.sub }}>{(ocrStage && ocrStage.label) || '준비 중'}</span>
              <span style={{ fontSize: 13, fontWeight: 700, color: RX.teal }}>{Math.round((ocrStage && ocrStage.pct) || 0)}%</span>
            </div>
          </div>
        </div>
      )}

      {/* OCR Result Modal Popup */}
      {ocrResult && (
        <div style={{
          position: 'fixed', inset: 0, zIndex: 1000, 
          background: 'rgba(0,0,0,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20
        }}>
          <div style={{
            background: '#fff', borderRadius: 14, width: '100%', maxWidth: 760, 
            display: 'flex', flexDirection: 'column', overflow: 'hidden', boxShadow: '0 10px 30px rgba(0,0,0,0.2)'
          }}>
            <div style={{ padding: '16px 20px', borderBottom: `1px solid ${RX.line}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center', background: RX.tealTint }}>
              <div style={{ fontSize: 16, fontWeight: 800, color: RX.tealDark }}>스캔 결과 확인</div>
              <button onClick={() => setOcrResult(null)} style={{ background: 'transparent', border: 'none', cursor: 'pointer', color: RX.teal, fontSize: 24, lineHeight: 1 }}>&times;</button>
            </div>
            
            <div style={{ padding: '18px 20px', overflowY: 'auto', maxHeight: '70vh' }}>
              {/* 관리자 전용 OCR 디버그 패널 */}
              {plan === 'admin' && ocrDebugInfo && (() => {
                const m = ocrDebugInfo.serverMeta || {};
                const fmt = (ms) => ms != null ? (ms >= 1000 ? `${(ms/1000).toFixed(1)}s` : `${ms}ms`) : '-';
                const provider = m.provider || 'unknown';
                const isGV = provider === 'google_vision';
                const timing = m.timing || {};

                // 사용 모델 표시
                let modelLabel = '-';
                if (isGV) {
                  const { haiku, sonnet } = m.usedModels || {};
                  if (haiku && sonnet) modelLabel = 'Haiku + Sonnet (병렬)';
                  else if (haiku) modelLabel = 'Haiku only';
                  else if (sonnet) modelLabel = 'Sonnet only';
                  else modelLabel = 'Regex only (L1)';
                } else {
                  modelLabel = m.modelUsed === 'sonnet' ? 'Sonnet (Haiku fallback)' : 'Haiku';
                }

                const lc = m.layerCounts || {};
                const layerText = lc.L1 != null ? `L1=${lc.L1} L2=${lc.L2} L3=${lc.L3}` : '';

                const rows = [
                  ['전체 소요', fmt(ocrDebugInfo.totalMs)],
                  ['이미지 준비', fmt(ocrDebugInfo.imagePrepMs)],
                  ['OCR API 호출', fmt(ocrDebugInfo.apiCallMs)],
                  isGV
                    ? ['  └ Google Vision', fmt(timing.googleVisionMs)]
                    : ['  └ Haiku OCR', fmt(timing.haikuMs)],
                  isGV
                    ? ['  └ AI 구조화', fmt(timing.aiStructureMs)]
                    : timing.sonnetMs != null ? ['  └ Sonnet fallback', fmt(timing.sonnetMs)] : null,
                  ['CV 약물 분석', fmt(ocrDebugInfo.cvAnalysisMs)],
                ].filter(Boolean);

                return (
                  <div style={{ background: '#0f172a', color: '#94a3b8', borderRadius: 8, padding: '10px 14px', marginBottom: 14, fontFamily: 'monospace', fontSize: 12 }}>
                    <div style={{ color: '#38bdf8', fontWeight: 700, marginBottom: 6 }}>🔧 Admin OCR Debug</div>
                    <div style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: '2px 16px' }}>
                      {rows.map(([label, val], i) => (
                        <React.Fragment key={i}>
                          <span style={{ color: label.startsWith('  └') ? '#64748b' : '#94a3b8' }}>{label}</span>
                          <span style={{ color: '#f1f5f9', textAlign: 'right' }}>{val}</span>
                        </React.Fragment>
                      ))}
                    </div>
                    <div style={{ marginTop: 8, borderTop: '1px solid #1e293b', paddingTop: 6, display: 'flex', gap: 12, flexWrap: 'wrap' }}>
                      <span>모델: <span style={{ color: '#34d399' }}>{modelLabel}</span></span>
                      {layerText && <span>레이어: <span style={{ color: '#fbbf24' }}>{layerText}</span></span>}
                      <span>provider: <span style={{ color: '#a78bfa' }}>{provider}</span></span>
                    </div>
                  </div>
                );
              })()}
              <div style={{ fontSize: 13, color: RX.sub, marginBottom: 14 }}>OCR 약물명을 CV 상품명 DB와 대조해 교정하고, 가능한 처방 맥락을 점수순으로 정리했습니다.</div>

              {/* ── 질환 조합 후보 (버튼 선택 전엔 처방의도·약제설명 숨김) ── */}
              {(ocrResult.ocr_analysis?.combinationScenarios?.length > 0) && (() => {
                const rawScenarios = ocrResult.ocr_analysis.combinationScenarios;
                const comboReviewStatus = ocrResult.ocr_meta?.comboHint?.review_status;
                const scenarios = [...rawScenarios].sort((a, b) =>
                  (a.review_status === 'owner_approved' ? -1 : 0) - (b.review_status === 'owner_approved' ? -1 : 0)
                );
                const sel = selectedScenarioIdx != null ? scenarios[selectedScenarioIdx] : null;
                const scenarioLabel = (sc) => sc.diseases.map(d => d.diseaseLabel).join(' + ');

                const getRichPattern = (diseaseKey) => {
                  const ctx = (ocrResult.candidate_contexts || []).find(c => c.diseaseKey === diseaseKey && c.bestPattern?.patternId);
                  if (!ctx) return null;
                  const idx = window.rxHelpers.idxOf(ctx.diseaseKey, ctx.bestPattern.patternId);
                  if (idx < 0) return null;
                  return window.rxHelpers.getPattern(ctx.diseaseKey, idx);
                };

                const ScenarioDiseaseCard = ({ scenario }) => (
                  <div>
                    {scenario.diseases.map((d, di) => (
                      <div key={d.diseaseKey} style={{ marginBottom: di < scenario.diseases.length - 1 ? 10 : 0 }}>
                        <div style={{ fontSize: 13, fontWeight: 800, color: RX.tealDark, marginBottom: 5 }}>{d.diseaseLabel}</div>
                        {d.drugs.map((drug, dri) => (
                          <div key={dri} style={{ paddingLeft: 8, borderLeft: `2px solid ${RX.tealLine}`, marginBottom: 5 }}>
                            <div style={{ fontSize: 13, fontWeight: 700, color: RX.ink }}>{drug.brand} · {drug.roleLabel}</div>
                            {drug.roleDescription && <div style={{ fontSize: 11, color: RX.sub, marginTop: 2, lineHeight: 1.4 }}>{drug.roleDescription}</div>}
                          </div>
                        ))}
                        {d.drugs.length === 0 && <div style={{ fontSize: 12, color: RX.faint, paddingLeft: 8 }}>배정된 약물 없음</div>}
                      </div>
                    ))}
                  </div>
                );

                return (
                  <div style={{ marginBottom: 14 }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
                      <div style={{ fontSize: 13, color: RX.tealDark, fontWeight: 800 }}>이 약제 조합으로 가능한 질환</div>
                      {comboReviewStatus === 'ai_draft' && (
                        <span style={{ fontSize: 11, background: RX.amber, color: '#fff', borderRadius: 4, padding: '1px 6px' }}>검수 중 · AI 초안</span>
                      )}
                    </div>
                    <div style={{ fontSize: 12, color: RX.sub, marginBottom: 10 }}>아래 조합 중 하나를 선택하면 처방 의도와 약제별 역할이 열립니다.</div>
                    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginBottom: sel ? 14 : 0 }}>
                      {scenarios.map((sc, i) => {
                        const active = selectedScenarioIdx === i;
                        const trustBadge = sc.trustLevel === 'high' ? '🟢' : sc.trustLevel === 'medium' ? '🟡' : '🔴';
                        return (
                          <button key={i}
                            onClick={() => setSelectedScenarioIdx(active ? null : i)}
                            style={{
                              fontSize: 13, fontWeight: 800, cursor: 'pointer',
                              padding: '9px 15px', borderRadius: 22,
                              border: `1.5px solid ${active ? RX.teal : RX.tealLine}`,
                              background: active ? RX.teal : RX.tealTint,
                              color: active ? '#fff' : RX.tealDark,
                              transition: 'all 0.15s ease',
                            }}>
                            {trustBadge} {scenarioLabel(sc)} ({sc.score}%)
                          </button>
                        );
                      })}
                    </div>

                    {sel && (() => {
                      const primaryKey = sel.diseases[0]?.diseaseKey;
                      const richPattern = primaryKey ? getRichPattern(primaryKey) : null;
                      return (
                        <div>
                          {richPattern && (
                            <div style={{ marginBottom: 10 }}>
                              <DxBlock pattern={richPattern} />
                              <IntentBlock pattern={richPattern} />
                              <GuidelineBlock pattern={richPattern} />
                            </div>
                          )}
                          <div style={{ border: `1px solid ${RX.tealLine}`, background: RX.tealTint, borderRadius: 8, padding: 12 }}>
                            <div style={{ fontSize: 12, color: RX.tealDark, fontWeight: 800, marginBottom: 6 }}>
                              {scenarioLabel(sel)} · 가능성 {sel.score}%
                            </div>
                            <ScenarioDiseaseCard scenario={sel} />
                          </div>
                        </div>
                      );
                    })()}
                  </div>
                );
              })()}

              {(() => {
                // 선택된 시나리오 기준 brand → role 룩업맵 (선택 전엔 역할 설명 숨김)
                const cvRoleMap = {};
                const selScenario = selectedScenarioIdx != null
                  ? ocrResult.ocr_analysis?.combinationScenarios?.[selectedScenarioIdx]
                  : null;
                (selScenario?.diseases || []).forEach(d => {
                  d.drugs.forEach(dr => { cvRoleMap[dr.brand] = dr; });
                });
                const cvDrugs = ocrResult.ocr_analysis?.matchedCvDrugs || [];
                return (
                  <div style={{ marginBottom: 12 }}>
                    <div style={{ fontSize: 12, color: RX.tealDark, fontWeight: 800, marginBottom: 6 }}>CV 약물</div>
                    <div style={{ background: RX.paper, border: `1px solid ${RX.line}`, borderRadius: 8, overflow: 'hidden' }}>
                      {cvDrugs.map((drug, idx) => {
                        const roleInfo = cvRoleMap[drug.displayName || drug.brand] || {};
                        return (
                          <div key={idx} style={{ padding: '11px 13px', borderBottom: idx < cvDrugs.length - 1 ? `1px solid ${RX.line}` : 'none' }}>
                            <div style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 6 }}>
                              <span style={{ fontSize: 14, fontWeight: 800, color: RX.ink }}>{drug.displayName || drug.brand || '약품명 없음'}</span>
                              {drug.corrected && (
                                <span style={{ fontSize: 11, color: RX.rose, background: RX.roseBg, padding: '2px 6px', borderRadius: 4 }}>
                                  {drug.original_name} → 교정
                                </span>
                              )}
                              {ocrAiCorrectionBadge(drug)}
                              {ocrConfidenceBadge(drug)}
                            </div>
                            {ocrReferenceLabel(drug) && (
                              <div style={{ fontSize: 11, color: RX.faint, marginTop: 3 }}>{ocrReferenceLabel(drug)}</div>
                            )}
                            {drug.aiCorrection?.reason && (
                              <div style={{ fontSize: 11, color: RX.faint, marginTop: 3 }}>AI 교정 근거: {drug.aiCorrection.reason}</div>
                            )}
                            <div style={{ fontSize: 12, color: RX.sub, marginTop: 3 }}>
                              {drug.generic} · {drug.drug_class}
                              {!drug.drug_class && (
                                <span style={{ marginLeft: 6, fontSize: 11, background: RX.rose, color: '#fff', borderRadius: 4, padding: '1px 5px' }}>분류 확인 필요</span>
                              )}
                            </div>
                            {roleInfo.roleLabel && (
                              <div style={{ fontSize: 12, fontWeight: 700, color: RX.tealDark, marginTop: 4 }}>{roleInfo.roleLabel}</div>
                            )}
                            {roleInfo.roleDescription && (
                              <div style={{ fontSize: 12, color: RX.sub, marginTop: 3, lineHeight: 1.55 }}>{roleInfo.roleDescription}</div>
                            )}
                            <div style={{ display: 'flex', gap: 10, fontSize: 12, color: RX.sub, marginTop: 4, flexWrap: 'wrap' }}>
                              {drug.dose && <span>용량 <b style={{ color: RX.ink }}>{drug.dose}</b> ({drug.doseConfidence})</span>}
                              {drug.frequency && <span>1일 <b style={{ color: RX.ink }}>{drug.frequency}</b></span>}
                              {drug.duration && <span>총 <b style={{ color: RX.ink }}>{drug.duration}</b>일</span>}
                            </div>
                          </div>
                        );
                      })}
                      {cvDrugs.length === 0 && (
                        <div style={{ padding: 12, fontSize: 13, color: RX.sub }}>CV 약물로 확정된 항목이 없습니다.</div>
                      )}
                    </div>
                  </div>
                );
              })()}

              {ocrResult.ocr_analysis?.doseCounselingNotes?.length > 0 && (
                <div style={{ marginTop: 12, border: `1px solid ${RX.amber}`, background: RX.amberBg, borderRadius: 8, padding: 11 }}>
                  <div style={{ fontSize: 12, color: RX.amber, fontWeight: 800, marginBottom: 6 }}>용량 기반 복약지도</div>
                  {ocrResult.ocr_analysis.doseCounselingNotes.map((note, idx) => (
                    <div key={idx} style={{ fontSize: 13, color: RX.ink, lineHeight: 1.45, marginTop: idx ? 6 : 0 }}>
                      <b>{note.brand}</b>: {note.meaning} · {note.counseling}
                    </div>
                  ))}
                </div>
              )}

              <div style={{ marginTop: 12 }}>
                <div style={{ fontSize: 12, color: RX.sub, fontWeight: 800, marginBottom: 8 }}>함께 처방된 약물 (비CV)</div>
                {(() => {
                  const nonCvDrugs = ocrResult.ocr_analysis?.recognizedNonCvDrugs || [];
                  if (nonCvDrugs.length === 0) return (
                    <div style={{ border: `1px solid ${RX.line}`, borderRadius: 8, padding: 12, fontSize: 13, color: RX.sub, marginBottom: 10 }}>
                      비CV로 분리된 약물이 없습니다.
                    </div>
                  );
                  // 질환별 그룹핑
                  const groups = {};
                  nonCvDrugs.forEach(drug => {
                    const { disease } = getNonCvDisplay(drug);
                    if (!groups[disease]) groups[disease] = [];
                    groups[disease].push(drug);
                  });
                  return (
                    <div style={{ border: `1px solid ${RX.line}`, borderRadius: 8, background: '#fff', overflow: 'hidden', marginBottom: 10 }}>
                      {Object.entries(groups).map(([disease, drugs], gi) => (
                        <div key={disease} style={{ padding: '10px 12px', borderBottom: gi < Object.keys(groups).length - 1 ? `1px solid ${RX.line}` : 'none' }}>
                          <div style={{ fontSize: 12, fontWeight: 800, color: RX.sub, marginBottom: 6 }}>{disease}</div>
                          {drugs.map((drug, dri) => {
                            const { role } = getNonCvDisplay(drug);
                            return (
                              <div key={dri} style={{ paddingLeft: 8, borderLeft: `2px solid ${RX.line}`, marginBottom: dri < drugs.length - 1 ? 8 : 0 }}>
                                <div style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 6 }}>
                                  <span style={{ fontSize: 13, fontWeight: 700, color: RX.ink }}>{drug.displayName || drug.brand || drug.original_name || '약품명 없음'}</span>
                                  {ocrAiCorrectionBadge(drug)}
                                  {ocrConfidenceBadge(drug)}
                                </div>
                                {ocrReferenceLabel(drug) && (
                                  <div style={{ fontSize: 11, color: RX.faint, marginTop: 2 }}>{ocrReferenceLabel(drug)}</div>
                                )}
                                {drug.aiCorrection?.reason && (
                                  <div style={{ fontSize: 11, color: RX.faint, marginTop: 2 }}>AI 교정 근거: {drug.aiCorrection.reason}</div>
                                )}
                                <div style={{ fontSize: 12, color: RX.tealDark, fontWeight: 600, marginTop: 3 }}>{role}</div>
                                {drug.generic && <div style={{ fontSize: 11, color: RX.faint, marginTop: 2 }}>{drug.generic}</div>}
                              </div>
                            );
                          })}
                        </div>
                      ))}
                    </div>
                  );
                })()}

                <div style={{ marginBottom: 6 }}>
                  <div style={{ fontSize: 12, color: RX.sub, fontWeight: 800 }}>확인 필요 약물</div>
                </div>
                <div style={{ border: `1px solid ${RX.line}`, borderRadius: 8, background: '#fff', overflow: 'hidden' }}>
                  {(ocrResult.ocr_analysis?.unmatchedDrugs || []).map((drug, idx) => (
                    <div key={idx} style={{ padding: '10px 12px', borderBottom: idx < ocrResult.ocr_analysis.unmatchedDrugs.length - 1 ? `1px solid ${RX.line}` : 'none' }}>
                      <div style={{ fontSize: 13, fontWeight: 700, color: RX.ink }}>{drug.original_name || '약품명 없음'}</div>
                      <div style={{ fontSize: 12, color: RX.sub, marginTop: 3 }}>
                        {drug.unmatchedReason === 'db_not_found' && 'DB에 등록되지 않은 약물입니다.'}
                        {drug.unmatchedReason === 'ocr_low_confidence' && `OCR 오독 가능성 있음 (유사도 낮음)`}
                        {drug.unmatchedReason === 'non_cv_drug' && '심혈관 약물이 아닌 것으로 분류됨'}
                        {(!drug.unmatchedReason || drug.unmatchedReason === 'ambiguous') && 'CV DB 고확신 매칭 없음. 필요 시 약물명을 적어 서비스 개선 정보로 남겨주세요.'}
                      </div>
                    </div>
                  ))}
                  {(ocrResult.ocr_analysis?.unmatchedDrugs || []).length === 0 && (
                    <div style={{ padding: 12, fontSize: 13, color: RX.sub }}>확인 필요 약물이 없습니다.</div>
                  )}
                </div>
              </div>
            </div>

            <div style={{ padding: '12px 20px 16px', borderTop: `1px solid ${RX.line}`, background: '#fafafa' }}>
              {ocrSaveStatus === 'saving' && (
                <div style={{ fontSize: 11, color: RX.faint, textAlign: 'center', marginBottom: 8 }}>개선센터에 저장 중...</div>
              )}
              {ocrSaveStatus === 'saved' && (
                <div style={{ fontSize: 11, color: RX.teal, textAlign: 'center', marginBottom: 8 }}>개선센터에 자동 저장됨</div>
              )}
              {ocrSaveStatus === 'pii_blocked' && (
                <div style={{ fontSize: 11, color: RX.rose, textAlign: 'center', marginBottom: 8 }}>민감정보 감지로 개선센터 저장 안 됨</div>
              )}
              {ocrSaveStatus === 'failed' && (
                <div style={{ fontSize: 11, color: RX.amber, textAlign: 'center', marginBottom: 8 }}>저장 실패: 네트워크 확인 필요</div>
              )}
              <div style={{ display: 'flex', gap: 12 }}>
                <button onClick={() => setOcrResult(null)} style={{
                  flex: 1, padding: '12px', borderRadius: 8, background: '#fff', color: RX.sub, border: `1px solid ${RX.line}`, cursor: 'pointer', font: 'inherit', fontSize: 15, fontWeight: 600
                }}>닫기</button>
                <button onClick={submitOcrFeedback} style={{
                  flex: 1.2, padding: '12px', borderRadius: 8, background: '#fff', color: RX.tealDark, border: `1px solid ${RX.tealLine}`, cursor: 'pointer', font: 'inherit', fontSize: 14, fontWeight: 700
                }}>개선 정보 남기기</button>
                <button onClick={applyOcrResult} style={{
                  flex: 2, padding: '12px', borderRadius: 8, background: RX.teal, color: '#fff', border: 'none', cursor: 'pointer', font: 'inherit', fontSize: 15, fontWeight: 700, boxShadow: '0 4px 12px rgba(26,108,91,0.2)'
                }}>이 처방 분석하기</button>
              </div>
            </div>
          </div>
        </div>
      )}

      {s.tab === 'drug' ? <AppDrug /> : s.tab === 'gl' ? <AppGl /> : (s.tab === 'admin-ocr' && plan === 'admin') ? <AppAdminOcr /> : (
        <div style={{ flex: 1, overflowY: 'auto' }}>
          <SelectorBar subspec={s.subspec} onSubspec={s.onSubspec} diseaseKey={s.diseaseKey} caseIdx={s.caseIdx} onDisease={s.onDisease} onCase={s.onCase} onRandom={s.onRandom} />
          <div style={{ padding: '12px 12px 0' }}>
            {/* prompt */}
            {!s.revealed && (
              <div style={{ display: 'flex', gap: 8, alignItems: 'flex-start', background: RX.tealTint, border: `1px solid ${RX.tealLine}`, borderRadius: 11, padding: '10px 12px', marginBottom: 11 }}>
                <svg width="17" height="17" viewBox="0 0 18 18" fill="none" style={{ flexShrink: 0, marginTop: 1 }}><circle cx="9" cy="9" r="7.3" stroke={RX.teal} strokeWidth="1.4" /><path d="M9 8v4.5M9 5.4v.7" stroke={RX.teal} strokeWidth="1.6" strokeLinecap="round" /></svg>
                <div style={{ fontSize: 12, color: RX.tealDark, lineHeight: 1.55, fontWeight: 600 }}>처방전만 보고 <b>환자의 질환</b>과 <b>의사의 처방 의도</b>를 추론해 보세요. 약을 누르면 개별 설명이 열립니다.</div>
              </div>
            )}

            <RxForm pattern={s.pattern} onDrugClick={s.setDrug} activeDrug={s.drug} revealedDx={s.revealed ? dxLabels : null} />

            {/* reveal control */}
            {!s.revealed ? (
              <button onClick={() => s.setRevealed(true)} style={{
                width: '100%', marginTop: 13, padding: '15px', borderRadius: 13,
                background: RX.teal, color: '#fff', border: 'none', cursor: 'pointer',
                font: 'inherit', fontSize: 16, fontWeight: 800, letterSpacing: '-.01em',
                boxShadow: '0 6px 18px rgba(26,108,91,0.32)', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
              }}>
                <svg width="18" height="18" viewBox="0 0 20 20" fill="none"><path d="M2.5 10S5.5 4.5 10 4.5 17.5 10 17.5 10 14.5 15.5 10 15.5 2.5 10 2.5 10z" stroke="#fff" strokeWidth="1.6" /><circle cx="10" cy="10" r="2.4" stroke="#fff" strokeWidth="1.6" /></svg>
                정답 확인
              </button>
            ) : (
              <button onClick={() => s.setRevealed(false)} style={{
                width: '100%', marginTop: 13, padding: '11px', borderRadius: 11,
                background: RX.paper, color: RX.sub, border: `1px solid ${RX.line}`, cursor: 'pointer',
                font: 'inherit', fontSize: 13.5, fontWeight: 700,
              }}>다시 가리고 추론하기</button>
            )}

            {/* reveal blocks */}
            {s.revealed && (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 11, marginTop: 11, animation: 'rxFade .32s ease' }}>
                <DxBlock pattern={s.pattern} />
                <DifferentialDxBlock pattern={s.pattern} />
                <IntentBlock pattern={s.pattern} />
                <AdjunctContextBlock pattern={s.pattern} />
                <GroupsOverview pattern={s.pattern} onDrugClick={s.setDrug} />
                <CautionBlock pattern={s.pattern} />
                <GuidelineBlock pattern={s.pattern} />
              </div>
            )}
            <Disclaimer />
          </div>
        </div>
      )}

      {s.drug != null && <DrugPopup pattern={s.pattern} drugIndex={s.drug} onClose={() => s.setDrug(null)} />}
      <ProfileDrawer open={drawerOpen} onClose={() => setDrawerOpen(false)} />
    </div>
  );
}

window.AppPaper = AppPaper;
