// Neurify — functional clinical calculators (full toolset from spec)
const { useState: cUseS } = React;
function nowHHMM() { const d = new Date(); return String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0'); }
const num = (v) => { const n = parseFloat(String(v).replace(',', '.')); return isFinite(n) ? n : null; };
const r1 = (n) => Math.round(n * 10) / 10;

// shared option arrays
const YN = [['Nein', 0], ['Ja', 1]];
const MRC6 = [['5 · Normal', 5], ['4 · Gegen Widerstand', 4], ['3 · Gegen Schwerkraft', 3], ['2 · Schwerkraft elim.', 2], ['1 · Spur', 1], ['0 · Keine', 0]];
const ALS5 = [['4 · Normal', 4], ['3', 3], ['2', 2], ['1', 1], ['0 · Verlust', 0]];
const MGADL4 = [['0 · Normal', 0], ['1 · Leicht', 1], ['2 · Mäßig', 2], ['3 · Schwer', 3]];
const HIT6OPT = [['Nie', 6], ['Selten', 8], ['Manchmal', 10], ['Sehr oft', 11], ['Immer', 13]];
const REFLEX5 = [['0 · Fehlt', 0], ['1 · Abgeschwächt', 1], ['2 · Normal', 2], ['3 · Lebhaft', 3], ['4 · Klonus', 4]];

const CORTEX_CALCS = {
  // ══════════════ 1 · AKUTNEUROLOGIE & SCHLAGANFALL ══════════════
  nihss: {
    name: 'NIHSS', sub: 'Schlaganfall-Schweregrad', kind: 'sum', max: 42, train: true,
    items: [
      { id: '1a', name: '1a · Bewusstseinslage', opts: [['Wach', 0], ['Benommen', 1], ['Stuporös', 2], ['Komatös', 3]] },
      { id: '1b', name: '1b · Orientierungsfragen', opts: [['Beide richtig', 0], ['Eine richtig', 1], ['Keine richtig', 2]] },
      { id: '1c', name: '1c · Aufforderungen', opts: [['Beide befolgt', 0], ['Eine befolgt', 1], ['Keine befolgt', 2]] },
      { id: '2', name: '2 · Blickwendung', opts: [['Normal', 0], ['Partielle Blickparese', 1], ['Forcierte Deviation', 2]] },
      { id: '3', name: '3 · Gesichtsfeld', opts: [['Normal', 0], ['Partielle Hemianopsie', 1], ['Komplette Hemianopsie', 2], ['Bilateral', 3]] },
      { id: '4', name: '4 · Fazialisparese', opts: [['Normal', 0], ['Gering', 1], ['Partiell', 2], ['Komplett', 3]] },
      { id: '5a', name: '5a · Arm links', opts: [['Kein Absinken', 0], ['Absinken', 1], ['Gegen Schwerkraft', 2], ['Kein Halten', 3], ['Keine Bewegung', 4]] },
      { id: '5b', name: '5b · Arm rechts', opts: [['Kein Absinken', 0], ['Absinken', 1], ['Gegen Schwerkraft', 2], ['Kein Halten', 3], ['Keine Bewegung', 4]] },
      { id: '6a', name: '6a · Bein links', opts: [['Kein Absinken', 0], ['Absinken', 1], ['Gegen Schwerkraft', 2], ['Kein Halten', 3], ['Keine Bewegung', 4]] },
      { id: '6b', name: '6b · Bein rechts', opts: [['Kein Absinken', 0], ['Absinken', 1], ['Gegen Schwerkraft', 2], ['Kein Halten', 3], ['Keine Bewegung', 4]] },
      { id: '7', name: '7 · Extremitätenataxie', opts: [['Fehlt', 0], ['Eine Extremität', 1], ['Zwei Extremitäten', 2]] },
      { id: '8', name: '8 · Sensibilität', opts: [['Normal', 0], ['Leicht–mäßig', 1], ['Schwer–komplett', 2]] },
      { id: '9', name: '9 · Sprache / Aphasie', opts: [['Keine', 0], ['Leicht–mäßig', 1], ['Schwer', 2], ['Global / mutistisch', 3]] },
      { id: '10', name: '10 · Dysarthrie', opts: [['Normal', 0], ['Leicht–mäßig', 1], ['Schwer / anarthrisch', 2]] },
      { id: '11', name: '11 · Extinktion', opts: [['Keine', 0], ['Eine Modalität', 1], ['Schwer / mehrere', 2]] },
    ],
    interpret(t) {
      if (t === 0) return { label: 'Kein messbares Defizit', level: 'good' };
      if (t <= 4) return { label: 'Leichter Schlaganfall', level: 'mild' };
      if (t <= 15) return { label: 'Moderater Schlaganfall', level: 'mod' };
      if (t <= 20) return { label: 'Moderat bis schwer', level: 'high' };
      return { label: 'Schwerer Schlaganfall', level: 'sev' };
    },
    doc(v, t, b) {
      const lead = this.items.filter(it => (v[it.id] || 0) > 0).map(it => it.name.replace(/^\S+\s·\s/, '')).slice(0, 4);
      return `NIHSS: ${t} Punkte um ${nowHHMM()} Uhr. Führende Defizite: ${lead.length ? lead.join(', ') : 'keine'}. ${b.label}.`;
    },
  },
  gcs: {
    name: 'GCS', sub: 'Glasgow Coma Scale', kind: 'gcs', max: 15,
    items: [
      { id: 'eye', name: 'Augenöffnung (E)', opts: [['Spontan', 4], ['Auf Ansprache', 3], ['Auf Schmerzreiz', 2], ['Keine', 1]] },
      { id: 'verbal', name: 'Verbale Antwort (V)', opts: [['Orientiert', 5], ['Verwirrt', 4], ['Inadäquate Wörter', 3], ['Unverständliche Laute', 2], ['Keine', 1], ['Intubiert (T)', null]] },
      { id: 'motor', name: 'Motorische Antwort (M)', opts: [['Befolgt Aufforderung', 6], ['Gezielte Abwehr', 5], ['Ungezielte Beugung', 4], ['Pathologische Beugung', 3], ['Strecksynergismen', 2], ['Keine', 1]] },
    ],
    interpret(t) {
      if (t >= 13) return { label: 'Leichte Bewusstseinsstörung', level: 'good' };
      if (t >= 9) return { label: 'Mittelgradige Bewusstseinsstörung', level: 'mod' };
      return { label: 'Schwere Bewusstseinsstörung', level: 'sev' };
    },
    flags(v, t) {
      const out = [];
      if (v.verbal === null) out.push({ level: 'warn', msg: 'Verbale Komponente nicht beurteilbar (intubiert).' });
      if (t <= 8) out.push({ level: 'sev', msg: 'GCS ≤ 8: Atemweg und Intensivpflichtigkeit prüfen.' });
      return out;
    },
    doc(v, t) { const vv = v.verbal === null ? 'T' : v.verbal; return `GCS: E${v.eye} V${vv} M${v.motor} = ${t}/15${v.verbal === null ? ' (V n.b.)' : ''}.`; },
  },
  mrs: {
    name: 'mRS', sub: 'Modifizierte Rankin-Skala', kind: 'grade', prefix: 'mRS ',
    items: [{ id: 'g', name: 'Funktioneller Status', vertical: true, opts: [
      ['Keine Symptome', 0], ['Symptome ohne relevante Einschränkung', 1], ['Leichte Einschränkung, selbstständig', 2],
      ['Mäßige Einschränkung, braucht Hilfe, geht allein', 3], ['Kann nicht ohne Hilfe gehen', 4], ['Bettlägerig, ständig pflegebedürftig', 5], ['Tod', 6],
    ] }],
    interpret(t) { if (t <= 1) return { label: 'Kein bis minimales Defizit', level: 'good' }; if (t <= 3) return { label: 'Mäßige Behinderung', level: 'mod' }; return { label: 'Schwere Behinderung / Tod', level: 'sev' }; },
    doc(v, t) { return `Modifizierte Rankin-Skala: mRS ${t} — ${this.items[0].opts.find(o => o[1] === t)[0]}.`; },
  },
  barthel: {
    name: 'Barthel-Index', sub: 'Selbstständigkeit (ADL)', kind: 'sum', max: 100,
    items: [
      { id: 'feed', name: 'Essen', opts: [['Selbstständig', 10], ['Hilfe', 5], ['Unselbstständig', 0]] },
      { id: 'bath', name: 'Baden / Duschen', opts: [['Selbstständig', 5], ['Abhängig', 0]] },
      { id: 'groom', name: 'Körperpflege', opts: [['Selbstständig', 5], ['Abhängig', 0]] },
      { id: 'dress', name: 'Ankleiden', opts: [['Selbstständig', 10], ['Teilweise', 5], ['Abhängig', 0]] },
      { id: 'bowel', name: 'Stuhlkontrolle', opts: [['Kontinent', 10], ['Gelegentlich', 5], ['Inkontinent', 0]] },
      { id: 'blad', name: 'Harnkontrolle', opts: [['Kontinent', 10], ['Gelegentlich', 5], ['Inkontinent', 0]] },
      { id: 'toilet', name: 'Toilettenbenutzung', opts: [['Selbstständig', 10], ['Teilweise', 5], ['Abhängig', 0]] },
      { id: 'transfer', name: 'Transfer Bett–Stuhl', opts: [['Selbstständig', 15], ['Geringe Hilfe', 10], ['Erhebliche Hilfe', 5], ['Nicht möglich', 0]] },
      { id: 'mobil', name: 'Mobilität', opts: [['> 50 m selbst.', 15], ['> 50 m mit Hilfe', 10], ['Rollstuhl', 5], ['Immobil', 0]] },
      { id: 'stairs', name: 'Treppensteigen', opts: [['Selbstständig', 10], ['Mit Hilfe', 5], ['Nicht möglich', 0]] },
    ],
    interpret(t) { if (t >= 85) return { label: 'Geringe / keine Hilfsbedürftigkeit', level: 'good' }; if (t >= 35) return { label: 'Mäßige Hilfsbedürftigkeit', level: 'mod' }; return { label: 'Schwere Pflegeabhängigkeit', level: 'sev' }; },
    doc(v, t, b) { return `Barthel-Index: ${t}/100 — ${b.label}.`; },
  },
  aspects: {
    name: 'ASPECTS', sub: 'Frühe Ischämiezeichen im CT', kind: 'aspects', train: true,
    items: ['C · Caudatum', 'L · Lentiformkern', 'IC · Capsula interna', 'I · Inselrinde', 'M1', 'M2', 'M3', 'M4', 'M5', 'M6'].map((n, i) => ({ id: 'r' + i, name: n, type: 'check' })),
    interpret(t) { if (t >= 8) return { label: 'Geringe frühe Ischämiezeichen', level: 'good' }; if (t >= 6) return { label: 'Mäßige Frühveränderungen', level: 'mod' }; return { label: 'Ausgedehnte frühe Ischämiezeichen', level: 'sev' }; },
    flags(v, t) { return t <= 5 ? [{ level: 'sev', msg: 'ASPECTS ≤ 5: ausgedehnte Frühzeichen — Therapie im klinisch-radiologischen Kontext.' }] : []; },
    doc(v, t) { const aff = this.items.filter(it => v[it.id]).map(it => it.name.split(' · ')[0]); return `ASPECTS: ${t}/10. Betroffene Regionen: ${aff.length ? aff.join(', ') : 'keine'}.`; },
  },
  ich: {
    name: 'ICH-Score', sub: 'Intrazerebrale Blutung · Prognose', kind: 'sum', max: 6,
    items: [
      { id: 'gcs', name: 'GCS', opts: [['13–15', 0], ['5–12', 1], ['3–4', 2]] },
      { id: 'age', name: 'Alter', opts: [['< 80 Jahre', 0], ['≥ 80 Jahre', 1]] },
      { id: 'vol', name: 'Blutungsvolumen (ABC/2)', opts: [['< 30 ml', 0], ['≥ 30 ml', 1]] },
      { id: 'infra', name: 'Lokalisation', opts: [['Supratentoriell', 0], ['Infratentoriell', 1]] },
      { id: 'ivh', name: 'Intraventrikuläre Blutung', opts: [['Keine', 0], ['Vorhanden', 1]] },
    ],
    interpret(t) { if (t <= 1) return { label: 'Günstigere Prognose', level: 'good' }; if (t <= 3) return { label: 'Intermediäre Prognose', level: 'mod' }; return { label: 'Ungünstige Prognose', level: 'sev' }; },
    doc(v, t) { return `ICH-Score: ${t}/6. Prognosescore, keine alleinige Therapieentscheidung.`; },
  },
  hunthess: {
    name: 'Hunt-Hess-Skala', sub: 'SAB-Schweregrad', kind: 'grade', prefix: 'Grad ',
    items: [{ id: 'g', name: 'Klinischer Befund', vertical: true, opts: [
      ['Asymptomatisch / leichter Kopfschmerz', 1], ['Mäßig–starker Kopfschmerz, Meningismus, kein Defizit', 2],
      ['Somnolenz, Verwirrtheit oder leichtes Defizit', 3], ['Sopor, mäßige–schwere Hemiparese', 4], ['Koma, Decerebrationshaltung', 5],
    ] }],
    interpret(t) { if (t <= 2) return { label: 'Guter Grad — günstigere Prognose', level: 'good' }; if (t === 3) return { label: 'Intermediärer Grad', level: 'mod' }; if (t === 4) return { label: 'Schlechter Grad', level: 'high' }; return { label: 'Sehr schlechter Grad', level: 'sev' }; },
    flags(v, t) { return t >= 4 ? [{ level: 'sev', msg: 'Hoher Grad: intensivmedizinische Versorgung erforderlich.' }] : []; },
    doc(v, t) { return `SAB-Schweregrad nach Hunt-Hess: Grad ${t}.`; },
  },
  wfns: {
    name: 'WFNS-Skala', sub: 'SAB-Schweregrad (GCS)', kind: 'grade', prefix: 'Grad ',
    items: [{ id: 'g', name: 'GCS & fokales Defizit', vertical: true, opts: [
      ['GCS 15, kein fokales Defizit', 1], ['GCS 13–14, kein fokales Defizit', 2], ['GCS 13–14, mit fokalem Defizit', 3], ['GCS 7–12', 4], ['GCS 3–6', 5],
    ] }],
    interpret(t) { if (t <= 2) return { label: 'Guter Grad', level: 'good' }; if (t === 3) return { label: 'Intermediärer Grad', level: 'mod' }; return { label: 'Schlechter Grad', level: 'sev' }; },
    doc(v, t) { return `SAB-Schweregrad nach WFNS: Grad ${t}.`; },
  },
  abcd2: {
    name: 'ABCD²-Score', sub: 'Schlaganfallrisiko nach TIA', kind: 'sum', max: 7,
    items: [
      { id: 'age', name: 'Alter', opts: [['< 60 Jahre', 0], ['≥ 60 Jahre', 1]] },
      { id: 'bp', name: 'Blutdruck', opts: [['< 140/90 mmHg', 0], ['≥ 140/90 mmHg', 1]] },
      { id: 'clin', name: 'Klinik', opts: [['Andere Symptome', 0], ['Sprachstörung ohne Parese', 1], ['Einseitige Parese', 2]] },
      { id: 'dur', name: 'Symptomdauer', opts: [['< 10 min', 0], ['10–59 min', 1], ['≥ 60 min', 2]] },
      { id: 'dm', name: 'Diabetes mellitus', opts: YN },
    ],
    interpret(t) { if (t <= 3) return { label: 'Niedrigeres Risiko', level: 'good' }; if (t <= 5) return { label: 'Moderates Risiko', level: 'mod' }; return { label: 'Hohes Risiko', level: 'sev' }; },
    doc(v, t, b) { return `ABCD²-Score: ${t}/7 — ${b.label}. Dringliche TIA-Abklärung nicht ersetzen.`; },
  },

  // ══════════════ 2 · MS & NEUROIMMUNOLOGIE ══════════════
  edss: {
    name: 'EDSS', sub: 'Behinderung bei MS', kind: 'grade', prefix: 'EDSS ', train: true,
    items: [{ id: 'g', name: 'Geh- & Funktionsstatus', vertical: true, opts: [
      ['Normaler Befund', 0], ['Minimale Zeichen, keine Behinderung', 1.5], ['Leichte Behinderung, keine Gehlimitation', 3.0],
      ['Gehlimitation, geht ohne Hilfe', 5.0], ['Geht mit einseitiger Hilfe (~100 m)', 6.0], ['Geht mit beidseitiger Hilfe', 6.5],
      ['Rollstuhl erforderlich', 7.5], ['An Bett/Stuhl gebunden', 8.5], ['Tod durch MS', 10],
    ] }],
    interpret(t) { if (t <= 2.5) return { label: 'Geringe Behinderung', level: 'good' }; if (t <= 5.5) return { label: 'Mäßige Behinderung, gehfähig', level: 'mod' }; if (t < 10) return { label: 'Schwere Behinderung', level: 'sev' }; return { label: 'Tod durch MS', level: 'sev' }; },
    flags() { return [{ level: 'warn', msg: 'EDSS ist gehstreckenlastig — Kognition, Fatigue, Armfunktion nur eingeschränkt abgebildet. Ärztlich bestätigen.' }]; },
    doc(v, t) { return `EDSS: ${t} (Richtbereich). Ärztliche Bestätigung erforderlich.`; },
  },
  msfc: {
    name: 'MSFC-Verlauf', sub: 'MS · funktioneller Verlauf', kind: 'num', display: 'text',
    items: [
      { id: 't25', name: 'Timed 25-Foot Walk', type: 'num', unit: 's' },
      { id: 'hpt', name: '9-Hole Peg Test (Mittel)', type: 'num', unit: 's' },
      { id: 'cog', name: 'Kognitiver Test (SDMT/PASAT)', type: 'num', unit: 'Pkt' },
    ],
    score(v) { return v.t25; },
    interpret(v) { return v.t25 == null && v.hpt == null && v.cog == null ? { label: 'Werte eingeben', level: 'info' } : { label: 'Verlaufswert dokumentiert', level: 'info' }; },
    doc(v) { return `MSFC-Verlauf: T25FW ${v.t25 ?? '–'} s, 9HPT ${v.hpt ?? '–'} s, kognitiv ${v.cog ?? '–'}. Mit Vorwert vergleichen.`; },
  },
  ninehole: {
    name: '9-Hole Peg Test', sub: 'MS · Armfunktion', kind: 'num', display: 'text',
    items: [
      { id: 'r1', name: 'Rechts Versuch 1', type: 'num', unit: 's' }, { id: 'r2', name: 'Rechts Versuch 2', type: 'num', unit: 's' },
      { id: 'l1', name: 'Links Versuch 1', type: 'num', unit: 's' }, { id: 'l2', name: 'Links Versuch 2', type: 'num', unit: 's' },
    ],
    score(v) { return v.r1; },
    avg(v, a, b) { return v[a] != null && v[b] != null ? r1((v[a] + v[b]) / 2) : null; },
    interpret(v) { const r = this.avg(v, 'r1', 'r2'), l = this.avg(v, 'l1', 'l2'); return (r == null && l == null) ? { label: 'Werte eingeben', level: 'info' } : { label: `Rechts ${r ?? '–'} s · Links ${l ?? '–'} s`, level: 'info' }; },
    doc(v) { return `9-Hole Peg Test: rechts ${this.avg(v, 'r1', 'r2') ?? '–'} s, links ${this.avg(v, 'l1', 'l2') ?? '–'} s (Mittel). Mit Vorwert vergleichen.`; },
  },
  t25fw: {
    name: 'Timed 25-Foot Walk', sub: 'MS · Gehfunktion', kind: 'num', display: 'text',
    items: [{ id: 't1', name: 'Versuch 1', type: 'num', unit: 's' }, { id: 't2', name: 'Versuch 2', type: 'num', unit: 's' }],
    score(v) { return v.t1; },
    interpret(v) { const a = (v.t1 != null && v.t2 != null) ? r1((v.t1 + v.t2) / 2) : null; return a == null ? { label: 'Werte eingeben', level: 'info' } : { label: `Mittelwert ${a} s`, level: 'info' }; },
    doc(v) { const a = (v.t1 != null && v.t2 != null) ? r1((v.t1 + v.t2) / 2) : '–'; return `Timed 25-Foot Walk: ${v.t1 ?? '–'} s / ${v.t2 ?? '–'} s, Mittel ${a} s.`; },
  },
  sdmt: {
    name: 'SDMT', sub: 'MS · Kognition', kind: 'num', display: 'num', unit: ' korrekt',
    items: [{ id: 'n', name: 'Korrekte Zuordnungen in 90 s', type: 'num', unit: '' }],
    score(v) { return v.n; },
    interpret(v) { return v.n == null ? { label: 'Wert eingeben', level: 'info' } : { label: 'Verlaufswert — im Trend beurteilen', level: 'info' }; },
    doc(v) { return `SDMT: ${v.n ?? '–'} korrekte Zuordnungen in 90 s. Norm nur mit lokal validierten Daten.`; },
  },

  // ══════════════ 3 · PARKINSON & BEWEGUNGSSTÖRUNGEN ══════════════
  hoehn: {
    name: 'Hoehn & Yahr', sub: 'Parkinson-Staging', kind: 'grade', prefix: 'Stadium ',
    items: [{ id: 'g', name: 'Stadium', vertical: true, opts: [
      ['1 · Einseitig', 1], ['1.5 · Einseitig + axial', 1.5], ['2 · Beidseitig, kein Gleichgewichtsverlust', 2],
      ['2.5 · Leicht beidseitig, Pull-Test erholt', 2.5], ['3 · Posturale Instabilität, selbstständig', 3], ['4 · Schwer, steht/geht ohne Hilfe', 4], ['5 · Rollstuhl / bettlägerig', 5],
    ] }],
    interpret(t) { if (t <= 2) return { label: 'Frühes Stadium', level: 'good' }; if (t <= 3) return { label: 'Mittleres Stadium', level: 'mod' }; return { label: 'Fortgeschrittenes Stadium', level: 'sev' }; },
    doc(v, t) { return `Parkinson-Syndrom: Hoehn & Yahr Stadium ${t}.`; },
  },
  updrs: {
    name: 'MDS-UPDRS', sub: 'Parkinson · Gesamtscore', kind: 'num', display: 'num', unit: ' Pkt', train: true,
    items: [
      { id: 'p1', name: 'Teil I · Nicht-motorisch', type: 'num', unit: '' }, { id: 'p2', name: 'Teil II · Alltag', type: 'num', unit: '' },
      { id: 'p3', name: 'Teil III · Motorik', type: 'num', unit: '' }, { id: 'p4', name: 'Teil IV · Komplikationen', type: 'num', unit: '' },
    ],
    score(v) { return ['p1', 'p2', 'p3', 'p4'].reduce((a, k) => a + (v[k] || 0), 0); },
    interpret(v) { const any = ['p1', 'p2', 'p3', 'p4'].some(k => v[k] != null); return any ? { label: 'Summe der Teilscores — im Trend beurteilen', level: 'info' } : { label: 'Teilscores eingeben', level: 'info' }; },
    doc(v) { return `MDS-UPDRS: I ${v.p1 ?? '–'}, II ${v.p2 ?? '–'}, III ${v.p3 ?? '–'}, IV ${v.p4 ?? '–'}, gesamt ${this.score(v)}.`; },
  },
  schwab: {
    name: 'Schwab & England', sub: 'Parkinson · Alltag', kind: 'grade', prefix: '', suffix: ' %',
    items: [{ id: 'g', name: 'Alltagsfunktion', vertical: true, opts: [
      ['100 % · Völlig unabhängig', 100], ['90 % · Unabhängig, langsamer', 90], ['80 % · Unabhängig mit Mühe', 80], ['70 % · Mehr Zeit, Schwierigkeiten', 70],
      ['60 % · Teilweise abhängig', 60], ['50 % · Stärker abhängig', 50], ['40 % · Sehr abhängig', 40], ['30 % · Viel Hilfe nötig', 30], ['20 % · Schwer behindert', 20], ['10 % · Total abhängig', 10], ['0 % · Vegetativ', 0],
    ] }],
    interpret(t) { if (t >= 80) return { label: 'Weitgehend selbstständig', level: 'good' }; if (t >= 50) return { label: 'Teilweise abhängig', level: 'mod' }; return { label: 'Stark abhängig', level: 'sev' }; },
    doc(v, t) { return `Schwab & England ADL: ${t} %.`; },
  },
  aims: {
    name: 'AIMS', sub: 'Unwillkürliche Bewegungen', kind: 'num', display: 'num', unit: '/28',
    items: [{ id: 'mv', name: 'Bewegungs-Score (Items 1–7, je 0–4)', type: 'num', unit: '' }, { id: 'gl', name: 'Globale Schwere (Item 8, 0–4)', type: 'num', unit: '' }],
    score(v) { return v.mv; },
    interpret(v) { if (v.mv == null) return { label: 'Werte eingeben', level: 'info' }; if (v.mv === 0) return { label: 'Keine Dyskinesien', level: 'good' }; if (v.mv <= 8) return { label: 'Leichte Dyskinesien', level: 'mild' }; if (v.mv <= 16) return { label: 'Mäßige Dyskinesien', level: 'mod' }; return { label: 'Ausgeprägte Dyskinesien', level: 'sev' }; },
    doc(v) { return `AIMS: Bewegungs-Score ${v.mv ?? '–'}/28, globale Einschätzung ${v.gl ?? '–'}/4.`; },
  },

  // ══════════════ 4 · DEMENZ & KOGNITION ══════════════
  cogselect: {
    name: 'Test-Selector', sub: 'Kognitives Screening wählen', kind: 'pick',
    items: [{ id: 'ctx', name: 'Klinischer Kontext', vertical: true, opts: [
      ['Akute fluktuierende Verwirrtheit', 'delir'], ['Leichte kognitive Klage / Exekutivstörung', 'moca'], ['Etablierte mittelgradige Demenz', 'mmst'], ['Visuell-räumliches / exekutives Screening', 'uhr'], ['Detaillierte Differenzialdiagnose', 'neuropsy'],
    ] }],
    rec: { delir: ['Delir-Screening, somatische Abklärung', 'sev'], moca: ['MoCA oder DemTect', 'good'], mmst: ['MMST/MMSE + Funktionsassessment', 'mod'], uhr: ['Uhrentest als Zusatz', 'good'], neuropsy: ['Neuropsychologie / CERAD-Plus', 'mod'] },
    interpret(v) { const r = this.rec[v.ctx]; return r ? { label: r[0], level: r[1] } : { label: 'Kontext wählen', level: 'info' }; },
    doc(v) { const r = this.rec[v.ctx]; return `Kognitives Screening empfohlen: ${r ? r[0] : '–'}.`; },
  },
  moca: {
    name: 'MoCA', sub: 'Montreal Cognitive Assessment', kind: 'num', display: 'num', unit: '/30', train: true,
    items: [{ id: 'tot', name: 'Gesamtpunktzahl', type: 'num', unit: '/30' }, { id: 'edu', name: 'Bildung ≤ 12 Jahre (+1)', type: 'check' }],
    score(v) { let s = v.tot; if (s != null && v.edu) s = Math.min(30, s + 1); return s; },
    interpret(v) { const s = this.score(v); if (s == null) return { label: 'Punktzahl eingeben', level: 'info' }; if (s >= 26) return { label: 'Unauffällig (Cut-off < 26)', level: 'good' }; return { label: 'Auffällig — im Kontext bewerten', level: 'mod' }; },
    doc(v) { return `MoCA: ${v.tot ?? '–'}/30${v.edu ? ', bildungskorrigiert ' + this.score(v) + '/30' : ''}. Interpretation im klinischen Kontext.`; },
  },
  mmse: {
    name: 'MMST / MMSE', sub: 'Mini-Mental-Status', kind: 'num', display: 'num', unit: '/30',
    items: [{ id: 'tot', name: 'Gesamtpunktzahl', type: 'num', unit: '/30' }],
    score(v) { return v.tot; },
    interpret(v) { const s = v.tot; if (s == null) return { label: 'Punktzahl eingeben', level: 'info' }; if (s >= 24) return { label: 'Keine / leichte Einschränkung', level: 'good' }; if (s >= 18) return { label: 'Leichte–mittelgradige Einschränkung', level: 'mod' }; return { label: 'Schwere kognitive Einschränkung', level: 'sev' }; },
    doc(v) { return `MMST/MMSE: ${v.tot ?? '–'}/30. Sprache/Bildung/Delir berücksichtigt.`; },
  },
  clock: {
    name: 'Uhrentest', sub: 'Visuokonstruktion / Exekutive', kind: 'grade', prefix: '',
    items: [{ id: 'g', name: 'Gesamteindruck', vertical: true, opts: [['Unauffällig — Kreis, Zahlen, Zeiger korrekt', 0], ['Leicht auffällig — kleine Planungsfehler', 1], ['Auffällig — relevante Fehler', 2]] }],
    interpret(t) { if (t === 0) return { label: 'Unauffällig', level: 'good' }; if (t === 1) return { label: 'Leicht auffällig', level: 'mod' }; return { label: 'Auffällig — visuell-räumlich/exekutiv', level: 'sev' }; },
    doc(v, t, b) { return `Uhrentest: ${b.label}. Hinweis auf visuell-räumliche/exekutive Störung: ${t > 0 ? 'ja' : 'nein'}.`; },
  },
  demtect: {
    name: 'DemTect', sub: 'Demenzscreening', kind: 'num', display: 'num', unit: '/18',
    items: [{ id: 'tot', name: 'Gesamtpunktzahl (alterskorrigiert)', type: 'num', unit: '/18' }],
    score(v) { return v.tot; },
    interpret(v) { const s = v.tot; if (s == null) return { label: 'Punktzahl eingeben', level: 'info' }; if (s >= 13) return { label: 'Altersgemäße Leistung', level: 'good' }; if (s >= 9) return { label: 'Leichte kognitive Beeinträchtigung', level: 'mod' }; return { label: 'Demenzverdacht', level: 'sev' }; },
    doc(v) { return `DemTect: ${v.tot ?? '–'}/18.`; },
  },
  gds: {
    name: 'GDS / FAST', sub: 'Demenz-Staging', kind: 'grade', prefix: 'GDS ',
    items: [{ id: 'g', name: 'Stadium', vertical: true, opts: [
      ['1 · Keine kognitive Einbuße', 1], ['2 · Subjektive Klagen', 2], ['3 · Leichte objektive Einbuße', 3], ['4 · Mäßig, komplexe Aufgaben betroffen', 4], ['5 · Hilfe bei Alltagswahl nötig', 5], ['6 · Hilfe bei Basis-ADL', 6], ['7 · Verlust Sprache/Mobilität', 7],
    ] }],
    interpret(t) { if (t <= 3) return { label: 'Kein bis leichtes Stadium', level: 'good' }; if (t <= 5) return { label: 'Mittleres Stadium', level: 'mod' }; return { label: 'Schweres Stadium', level: 'sev' }; },
    doc(v, t) { return `Demenzstadium: GDS/FAST ${t}.`; },
  },

  // ══════════════ 5 · KOPFSCHMERZ & MIGRÄNE ══════════════
  hit6: {
    name: 'HIT-6', sub: 'Kopfschmerzbelastung', kind: 'sum', max: 78,
    items: ['Starke Schmerzen', 'Alltag eingeschränkt', 'Hinlegen-Wunsch', 'Zu müde für Aktivitäten', 'Genervt / gereizt', 'Konzentration eingeschränkt'].map((n, i) => ({ id: 'q' + i, name: (i + 1) + ' · ' + n, opts: HIT6OPT })),
    interpret(t) { if (t <= 49) return { label: 'Geringe Auswirkung', level: 'good' }; if (t <= 55) return { label: 'Leichte/moderate Auswirkung', level: 'mild' }; if (t <= 59) return { label: 'Erhebliche Auswirkung', level: 'mod' }; return { label: 'Schwere Auswirkung', level: 'sev' }; },
    doc(v, t, b) { return `HIT-6: ${t} Punkte — ${b.label}.`; },
  },
  midas: {
    name: 'MIDAS', sub: 'Migräne · Beeinträchtigung', kind: 'num', display: 'num', unit: ' Pkt',
    items: [
      { id: 'a', name: 'Fehltage Arbeit/Schule', type: 'num', unit: 'd' }, { id: 'b', name: 'Reduzierte Produktivität Arbeit/Schule', type: 'num', unit: 'd' },
      { id: 'c', name: 'Fehltage Haushalt', type: 'num', unit: 'd' }, { id: 'd', name: 'Reduzierte Produktivität Haushalt', type: 'num', unit: 'd' }, { id: 'e', name: 'Verpasste soziale Aktivitäten', type: 'num', unit: 'd' },
    ],
    score(v) { return ['a', 'b', 'c', 'd', 'e'].reduce((s, k) => s + (v[k] || 0), 0); },
    interpret(v) { const t = this.score(v); const any = ['a', 'b', 'c', 'd', 'e'].some(k => v[k] != null); if (!any) return { label: 'Tage eingeben', level: 'info' }; if (t <= 5) return { label: 'Grad I — geringe Beeinträchtigung', level: 'good' }; if (t <= 10) return { label: 'Grad II — leichte Beeinträchtigung', level: 'mild' }; if (t <= 20) return { label: 'Grad III — mäßige Beeinträchtigung', level: 'mod' }; return { label: 'Grad IV — schwere Beeinträchtigung', level: 'sev' }; },
    doc(v) { return `MIDAS: ${this.score(v)} Punkte.`; },
  },
  mmd: {
    name: 'Monatliche Migränetage', sub: 'Migräne-Verlauf', kind: 'num', display: 'text',
    items: [
      { id: 'mig', name: 'Migränetage im Zeitraum', type: 'num', unit: 'd' }, { id: 'hd', name: 'Kopfschmerztage im Zeitraum', type: 'num', unit: 'd' },
      { id: 'med', name: 'Akutmedikationstage', type: 'num', unit: 'd' }, { id: 'mo', name: 'Anzahl Monate', type: 'num', unit: 'Mon', def: 3 },
    ],
    perMonth(v, k) { return (v[k] != null && v.mo) ? r1(v[k] / v.mo) : null; },
    score(v) { return this.perMonth(v, 'mig'); },
    interpret(v) { const m = this.perMonth(v, 'mig'); if (m == null) return { label: 'Werte eingeben', level: 'info' }; return m >= 4 ? { label: `${m}/Monat — Prophylaxe prüfen`, level: 'mod' } : { label: `${m} Migränetage/Monat`, level: 'good' }; },
    flags(v) { const m = this.perMonth(v, 'mig'); return (m != null && m >= 4) ? [{ level: 'warn', msg: 'Prophylaxeindikation prüfen (Schwere, Dauer, Akutmedikation, Präferenz).' }] : []; },
    doc(v) { return `Migräneverlauf: ${this.perMonth(v, 'mig') ?? '–'} Migränetage/Monat, ${this.perMonth(v, 'hd') ?? '–'} Kopfschmerztage/Monat, Akutmedikation an ${this.perMonth(v, 'med') ?? '–'} Tagen/Monat.`; },
  },
  moh: {
    name: 'Medikamenten­übergebrauch', sub: 'MOH-Check', kind: 'picknum', display: 'text',
    items: [
      { id: 'trip', name: 'Triptane (Tage/Monat)', type: 'num', unit: 'd' }, { id: 'comb', name: 'Kombinations­analgetika', type: 'num', unit: 'd' },
      { id: 'opio', name: 'Opioide / Ergotamine', type: 'num', unit: 'd' }, { id: 'simp', name: 'Einfache Analgetika', type: 'num', unit: 'd' },
    ],
    risk(v) { return (v.trip || 0) >= 10 || (v.comb || 0) >= 10 || (v.opio || 0) >= 10 || (v.simp || 0) >= 15; },
    interpret(v) { const any = ['trip', 'comb', 'opio', 'simp'].some(k => v[k] != null); if (!any) return { label: 'Tage eingeben', level: 'info' }; return this.risk(v) ? { label: 'Risiko für Medikamentenübergebrauch', level: 'sev' } : { label: 'Kein Übergebrauch-Schwellenwert erreicht', level: 'good' }; },
    flags(v) { return this.risk(v) ? [{ level: 'sev', msg: 'Schwelle erreicht: Beratung zu Medikamentenübergebrauchskopfschmerz.' }] : []; },
    doc(v) { return `Akutmedikation: Triptane ${v.trip ?? '–'}, Kombi ${v.comb ?? '–'}, Opioid/Ergot ${v.opio ?? '–'}, einfach ${v.simp ?? '–'} Tage/Monat. Übergebrauch: ${this.risk(v) ? 'ja' : 'nein'}.`; },
  },
  snnoop: {
    name: 'SNNOOP10', sub: 'Kopfschmerz · Red Flags', kind: 'check', unit: ' Red Flags',
    items: [
      'Systemische Symptome / Fieber / Tumor / HIV', 'Neurologisches Defizit', 'Plötzlicher Beginn (Thunderclap)', 'Höheres Alter, neuer Kopfschmerz (> 50 J.)',
      'Musteränderung des Kopfschmerzes', 'Lageabhängig (positional)', 'Durch Valsalva / Belastung ausgelöst', 'Papillenödem', 'Schwangerschaft / Wochenbett', 'Schmerzhaftes rotes Auge / autonome Zeichen',
    ].map((n, i) => ({ id: 'f' + i, name: n, type: 'check' })),
    interpret(t) { return t === 0 ? { label: 'Keine Red Flags — klinischen Kontext beachten', level: 'good' } : { label: 'Red Flag(s) — sekundären Kopfschmerz ausschließen', level: 'sev' }; },
    flags(v, t) { return t > 0 ? [{ level: 'sev', msg: 'Mindestens eine Red Flag: sekundären Kopfschmerz aktiv ausschließen.' }] : []; },
    doc(v, t) { const pos = this.items.filter(it => v[it.id]).map(it => it.name); return `SNNOOP10: ${t} Red Flag(s). ${pos.length ? 'Positiv: ' + pos.join('; ') + '.' : 'Keine angegeben.'}`; },
  },

  // ══════════════ 6 · EPILEPSIE & STATUS ══════════════
  setimer: {
    name: 'Status-epilepticus-Phase', sub: 'Zeitkritische Strukturierung', kind: 'num', display: 'text',
    items: [{ id: 'min', name: 'Anfallsdauer', type: 'num', unit: 'min' }],
    score(v) { return v.min; },
    interpret(v) { const m = v.min; if (m == null) return { label: 'Dauer eingeben', level: 'info' }; if (m < 5) return { label: 'Frühphase (< 5 min)', level: 'good' }; if (m < 10) return { label: 'Benzodiazepinphase (5–10 min)', level: 'mod' }; if (m < 30) return { label: 'Zweitlinientherapie (10–30 min)', level: 'high' }; return { label: 'Refraktärer Status (> 30 min)', level: 'sev' }; },
    flags(v) { return (v.min != null && v.min >= 5) ? [{ level: 'warn', msg: 'Therapie nach lokaler SOP — keine Dosierungen ohne Freigabe.' }] : []; },
    doc(v) { const b = this.interpret(v); return `Status epilepticus: Dauer ${v.min ?? '–'} min. Phase: ${b.label}. Therapie nach lokaler SOP.`; },
  },
  ilae: {
    name: 'ILAE-Klassifikation', sub: 'Anfallstyp', kind: 'pick',
    items: [
      { id: 'onset', name: 'Beginn', opts: [['Fokal', 'fokal'], ['Generalisiert', 'generalisiert'], ['Unbekannt', 'unbekannt']] },
      { id: 'aware', name: 'Bewusstsein (bei fokal)', opts: [['Bewusst erlebt', 'bewusst'], ['Beeinträchtigt', 'beeinträchtigt'], ['Unbekannt', 'unbekannt']] },
      { id: 'motor', name: 'Motorische Zeichen', opts: [['Motorisch', 'motorisch'], ['Nicht-motorisch', 'nicht-motorisch']] },
      { id: 'bilat', name: 'Übergang bilateral tonisch-klonisch', opts: YN },
    ],
    interpret(v) { if (!v.onset) return { label: 'Beginn wählen', level: 'info' }; let s = v.onset; if (v.onset === 'fokal') s += ` · ${v.aware || '?'} · ${v.motor || '?'}` + (v.bilat ? ' → bilateral t-k' : ''); return { label: s, level: 'mod' }; },
    doc(v) { return `Anfallsklassifikation nach ILAE: ${this.interpret(v).label}.`; },
  },
  stess: {
    name: 'STESS', sub: 'Status epilepticus · Prognose', kind: 'sum', max: 6,
    items: [
      { id: 'age', name: 'Alter', opts: [['< 65 Jahre', 0], ['≥ 65 Jahre', 2]] },
      { id: 'cons', name: 'Bewusstseinslage', opts: [['Wach / orientiert', 0], ['Somnolent / verwirrt', 1], ['Stuporös / komatös', 2]] },
      { id: 'type', name: 'Schwerster Anfallstyp', opts: [['Fokal / Absence', 0], ['Generalisiert konvulsiv', 1], ['Nonkonvulsiv im Koma', 2]] },
      { id: 'hist', name: 'Anfallsanamnese', opts: [['Vorbekannte Anfälle', 0], ['Keine Anamnese', 1]] },
    ],
    interpret(t) { return t < 3 ? { label: 'Günstigere Prognose', level: 'good' } : { label: 'Ungünstigere Prognose', level: 'sev' }; },
    doc(v, t, b) { return `STESS: ${t} Punkte — ${b.label}. Prognosescore, kein Therapiealgorithmus.`; },
  },

  // ══════════════ 7 · NEUROMUSKULÄRE ERKRANKUNGEN ══════════════
  alsfrs: {
    name: 'ALSFRS-R', sub: 'ALS-Funktionsskala', kind: 'sum', max: 48,
    items: [
      { id: 'speech', name: 'Sprache', opts: ALS5 }, { id: 'saliv', name: 'Speichelfluss', opts: ALS5 }, { id: 'swal', name: 'Schlucken', opts: ALS5 },
      { id: 'write', name: 'Handschrift', opts: ALS5 }, { id: 'cut', name: 'Essen schneiden', opts: ALS5 }, { id: 'dress', name: 'Ankleiden / Hygiene', opts: ALS5 },
      { id: 'turn', name: 'Umdrehen im Bett', opts: ALS5 }, { id: 'walk', name: 'Gehen', opts: ALS5 }, { id: 'stairs', name: 'Treppensteigen', opts: ALS5 },
      { id: 'dysp', name: 'Dyspnoe', opts: ALS5 }, { id: 'orth', name: 'Orthopnoe', opts: ALS5 }, { id: 'resp', name: 'Respiratorische Insuffizienz', opts: ALS5 },
    ],
    interpret(t) { if (t === 48) return { label: 'Keine Einschränkung', level: 'good' }; if (t >= 37) return { label: 'Leichte Einschränkung', level: 'mild' }; if (t >= 25) return { label: 'Mäßige Einschränkung', level: 'mod' }; return { label: 'Schwere Einschränkung', level: 'sev' }; },
    doc(v, t) { const b = v.speech + v.saliv + v.swal, f = v.write + v.cut + v.dress, g = v.turn + v.walk + v.stairs, r = v.dysp + v.orth + v.resp; return `ALSFRS-R: ${t}/48. Bulbär ${b}/12, Feinmotorik ${f}/12, Grobmotorik ${g}/12, Atmung ${r}/12.`; },
  },
  mgfa: {
    name: 'MGFA-Klassifikation', sub: 'Myasthenia gravis', kind: 'grade', prefix: 'Klasse ',
    items: [{ id: 'g', name: 'Klinische Ausprägung', vertical: true, opts: [['I · Rein okulär', 1], ['II · Leicht generalisiert', 2], ['III · Mäßig generalisiert', 3], ['IV · Schwer generalisiert', 4], ['V · Intubation erforderlich', 5]] }],
    interpret(t) { if (t <= 1) return { label: 'Okulär begrenzt', level: 'good' }; if (t <= 3) return { label: 'Generalisiert', level: 'mod' }; return { label: 'Schwer / krisennah', level: 'sev' }; },
    flags(v, t) { return t >= 5 ? [{ level: 'sev', msg: 'Klasse V: myasthene Krise — Atemwegssicherung.' }] : []; },
    doc(v, t) { return `Myasthenia gravis: MGFA Klasse ${t}.`; },
  },
  mgadl: {
    name: 'MG-ADL', sub: 'Myasthenie · Alltag', kind: 'sum', max: 24,
    items: [
      { id: 'talk', name: 'Sprechen', opts: MGADL4 }, { id: 'chew', name: 'Kauen', opts: MGADL4 }, { id: 'swal', name: 'Schlucken', opts: MGADL4 }, { id: 'breath', name: 'Atmung', opts: MGADL4 },
      { id: 'hyg', name: 'Zähneputzen / Kämmen', opts: MGADL4 }, { id: 'rise', name: 'Aufstehen vom Stuhl', opts: MGADL4 }, { id: 'dipl', name: 'Doppelbilder', opts: MGADL4 }, { id: 'ptos', name: 'Ptosis', opts: MGADL4 },
    ],
    interpret(t) { if (t === 0) return { label: 'Keine Symptome', level: 'good' }; if (t <= 5) return { label: 'Leichte Symptomatik', level: 'mild' }; if (t <= 12) return { label: 'Mäßige Symptomatik', level: 'mod' }; return { label: 'Schwere Symptomatik', level: 'sev' }; },
    flags(v) { return (v.breath >= 2 || v.swal >= 2) ? [{ level: 'sev', msg: 'Bulbäre / respiratorische Verschlechterung: Krise ausschließen.' }] : []; },
    doc(v, t) { return `MG-ADL: ${t}/24. Bulbär: ${v.swal >= 2 ? 'ja' : 'nein'}, respiratorisch: ${v.breath >= 2 ? 'ja' : 'nein'}.`; },
  },
  qmg: {
    name: 'QMG', sub: 'Quantitativer MG-Score', kind: 'num', display: 'num', unit: '/39', train: true,
    items: [{ id: 'tot', name: 'Gesamtpunktzahl (13 Items, je 0–3)', type: 'num', unit: '/39' }],
    score(v) { return v.tot; },
    interpret(v) { const s = v.tot; if (s == null) return { label: 'Punktzahl eingeben', level: 'info' }; if (s <= 8) return { label: 'Leicht', level: 'good' }; if (s <= 16) return { label: 'Mäßig', level: 'mod' }; return { label: 'Schwer', level: 'sev' }; },
    doc(v) { return `QMG: ${v.tot ?? '–'}/39. Atemfunktion separat beachten.`; },
  },
  incat: {
    name: 'INCAT', sub: 'CIDP · Behinderung', kind: 'sum', max: 10,
    items: [
      { id: 'arm', name: 'Armfunktion', opts: [['0 · Keine Einschränkung', 0], ['1 · Symptome, kein Defizit', 1], ['2 · Beeinträchtigt, eine Aufgabe', 2], ['3 · Beide Arme, einige Aufgaben', 3], ['4 · Beide, viele Aufgaben', 4], ['5 · Keine zielgerichtete Funktion', 5]] },
      { id: 'leg', name: 'Beinfunktion', opts: [['0 · Gehen unbeeinträchtigt', 0], ['1 · Symptome, geht ohne Hilfe', 1], ['2 · Geht mit einseitiger Hilfe', 2], ['3 · Geht mit beidseitiger Hilfe', 3], ['4 · Rollstuhl, steht/geht etwas', 4], ['5 · An Rollstuhl/Bett gebunden', 5]] },
    ],
    interpret(t) { if (t <= 2) return { label: 'Geringe Behinderung', level: 'good' }; if (t <= 6) return { label: 'Mäßige Behinderung', level: 'mod' }; return { label: 'Schwere Behinderung', level: 'sev' }; },
    doc(v, t) { return `INCAT: Arme ${v.arm}/5, Beine ${v.leg}/5, gesamt ${t}/10.`; },
  },
  mrcsum: {
    name: 'MRC Sum Score', sub: 'Globale Kraft (12 Gruppen)', kind: 'sum', max: 60,
    items: [
      { id: 'sa_l', name: 'Schulterabduktion links', opts: MRC6 }, { id: 'sa_r', name: 'Schulterabduktion rechts', opts: MRC6 },
      { id: 'eb_l', name: 'Ellbogenbeugung links', opts: MRC6 }, { id: 'eb_r', name: 'Ellbogenbeugung rechts', opts: MRC6 },
      { id: 'we_l', name: 'Handgelenkstreckung links', opts: MRC6 }, { id: 'we_r', name: 'Handgelenkstreckung rechts', opts: MRC6 },
      { id: 'hf_l', name: 'Hüftbeugung links', opts: MRC6 }, { id: 'hf_r', name: 'Hüftbeugung rechts', opts: MRC6 },
      { id: 'ke_l', name: 'Kniestreckung links', opts: MRC6 }, { id: 'ke_r', name: 'Kniestreckung rechts', opts: MRC6 },
      { id: 'df_l', name: 'Fußhebung links', opts: MRC6 }, { id: 'df_r', name: 'Fußhebung rechts', opts: MRC6 },
    ],
    interpret(t) { if (t === 60) return { label: 'Normale Kraft', level: 'good' }; if (t >= 48) return { label: 'Leichte Schwäche', level: 'mild' }; if (t >= 36) return { label: 'Mäßige Schwäche', level: 'mod' }; return { label: 'Ausgeprägte Schwäche', level: 'sev' }; },
    doc(v, t, b) { return `MRC Sum Score: ${t}/60 — ${b.label}. Trend wichtiger als Einzelwert.`; },
  },

  // ══════════════ 8 · SCHWINDEL, GANG & STURZ ══════════════
  hints: {
    name: 'HINTS+', sub: 'Akutes vestibuläres Syndrom', kind: 'check', train: true, unit: ' zentrale Zeichen',
    items: ['Kopfimpulstest normal (keine Korrektursakkade)', 'Nystagmus richtungswechselnd oder vertikal', 'Skew deviation vorhanden', 'Neue einseitige Hörminderung (HINTS+)'].map((n, i) => ({ id: 'z' + i, name: n, type: 'check' })),
    interpret(t) { return t === 0 ? { label: 'Eher peripheres Muster', level: 'good' } : { label: 'Zentral verdächtig', level: 'sev' }; },
    flags(v, t) { return t > 0 ? [{ level: 'sev', msg: 'Zentrales Zeichen: Bildgebung / Schlaganfallabklärung erwägen.' }] : []; },
    note: 'Nur bei akutem vestibulärem Syndrom mit Dauerschwindel und Spontannystagmus.',
    doc(v, t) { return `HINTS+: ${t} zentrale(s) Zeichen — ${t > 0 ? 'zentral verdächtig' : 'eher peripher'}.`; },
  },
  dixhallpike: {
    name: 'Dix-Hallpike', sub: 'BPLS / BPPV', kind: 'pick',
    items: [
      { id: 'side', name: 'Getestete Seite', opts: [['Rechts', 'rechts'], ['Links', 'links']] },
      { id: 'ny', name: 'Nystagmusbefund', opts: [['Latenz + transient, upbeat-torsional', 'bpls'], ['Atypisch / persistierend', 'zentral'], ['Kein Nystagmus', 'negativ']] },
    ],
    res: { bpls: ['Posteriorer Bogengang-BPLS wahrscheinlich', 'mod'], zentral: ['Atypisch — zentrale Ursache prüfen', 'sev'], negativ: ['Nicht diagnostisch', 'info'] },
    interpret(v) { const r = this.res[v.ny]; return r ? { label: r[0], level: r[1] } : { label: 'Befund wählen', level: 'info' }; },
    doc(v) { const r = this.res[v.ny]; return `Dix-Hallpike ${v.side || '–'}: ${r ? r[0] : '–'}.`; },
  },
  tug: {
    name: 'Timed Up and Go', sub: 'Gang / Sturzrisiko', kind: 'num', display: 'num', unit: ' s',
    items: [{ id: 's', name: 'Gemessene Zeit', type: 'num', unit: 's' }],
    score(v) { return v.s; },
    interpret(v) { const s = v.s; if (s == null) return { label: 'Zeit eingeben', level: 'info' }; if (s < 10) return { label: 'Meist unauffällig', level: 'good' }; if (s <= 20) return { label: 'Leicht eingeschränkte Mobilität', level: 'mild' }; if (s <= 30) return { label: 'Relevante Mobilitätseinschränkung', level: 'mod' }; return { label: 'Erhöhtes Sturz-/Hilfebedarfsrisiko', level: 'sev' }; },
    doc(v) { return `Timed-Up-and-Go: ${v.s ?? '–'} Sekunden.`; },
  },
  berg: {
    name: 'Berg Balance Scale', sub: 'Gleichgewicht / Sturz', kind: 'num', display: 'num', unit: '/56',
    items: [{ id: 'tot', name: 'Gesamtpunktzahl (14 Items, je 0–4)', type: 'num', unit: '/56' }],
    score(v) { return v.tot; },
    interpret(v) { const s = v.tot; if (s == null) return { label: 'Punktzahl eingeben', level: 'info' }; if (s >= 41) return { label: 'Geringeres Sturzrisiko', level: 'good' }; if (s >= 21) return { label: 'Mittleres Sturzrisiko', level: 'mod' }; return { label: 'Hohes Sturzrisiko', level: 'sev' }; },
    doc(v) { return `Berg Balance Scale: ${v.tot ?? '–'}/56.`; },
  },
  dhi: {
    name: 'DHI', sub: 'Dizziness Handicap Inventory', kind: 'num', display: 'num', unit: '/100',
    items: [{ id: 'tot', name: 'Gesamtpunktzahl (25 Items: Ja 4 / Manchmal 2 / Nein 0)', type: 'num', unit: '/100' }],
    score(v) { return v.tot; },
    interpret(v) { const s = v.tot; if (s == null) return { label: 'Punktzahl eingeben', level: 'info' }; if (s <= 30) return { label: 'Leichte Beeinträchtigung', level: 'good' }; if (s <= 60) return { label: 'Mäßige Beeinträchtigung', level: 'mod' }; return { label: 'Schwere Beeinträchtigung', level: 'sev' }; },
    doc(v) { return `DHI: ${v.tot ?? '–'}/100.`; },
  },

  // ══════════════ 9 · SCHMERZ, RADIKULOPATHIE & NEUROPATHIE ══════════════
  nrs: {
    name: 'NRS / VAS', sub: 'Schmerzintensität', kind: 'num', display: 'num', unit: '/10',
    items: [{ id: 'p', name: 'Schmerzintensität (0–10)', type: 'num', unit: '/10' }],
    score(v) { return v.p; },
    interpret(v) { const s = v.p; if (s == null) return { label: 'Wert eingeben', level: 'info' }; if (s === 0) return { label: 'Kein Schmerz', level: 'good' }; if (s <= 3) return { label: 'Leichter Schmerz', level: 'mild' }; if (s <= 6) return { label: 'Mäßiger Schmerz', level: 'mod' }; return { label: 'Starker Schmerz', level: 'sev' }; },
    doc(v) { return `Schmerzintensität NRS: ${v.p ?? '–'}/10.`; },
  },
  dn4: {
    name: 'DN4', sub: 'Neuropathischer Schmerz', kind: 'check', threshold: 4, unit: '/10',
    items: ['Brennen', 'Schmerzhafte Kälte', 'Elektrisierende Schläge', 'Kribbeln', 'Ameisenlaufen', 'Taubheit', 'Juckreiz', 'Hypästhesie Berührung', 'Hypästhesie Nadel', 'Schmerz durch Bürsten (Allodynie)'].map((n, i) => ({ id: 'd' + i, name: (i + 1) + ' · ' + n, type: 'check' })),
    interpret(t) { return t >= 4 ? { label: 'Hinweis auf neuropathischen Schmerz', level: 'sev' } : { label: 'Kein klarer Hinweis (Cut-off ≥ 4)', level: 'good' }; },
    doc(v, t) { return `DN4: ${t}/10. Hinweis auf neuropathischen Schmerz: ${t >= 4 ? 'ja' : 'nein'}.`; },
  },
  paindetect: {
    name: 'painDETECT', sub: 'Neuropathischer Schmerz', kind: 'num', display: 'num', unit: ' Pkt',
    items: [{ id: 'tot', name: 'Gesamtpunktzahl', type: 'num', unit: '' }],
    score(v) { return v.tot; },
    interpret(v) { const s = v.tot; if (s == null) return { label: 'Punktzahl eingeben', level: 'info' }; if (s <= 12) return { label: 'Neuropathisch unwahrscheinlich', level: 'good' }; if (s <= 18) return { label: 'Unklar', level: 'mod' }; return { label: 'Neuropathisch wahrscheinlich', level: 'sev' }; },
    doc(v) { return `painDETECT: ${v.tot ?? '–'} Punkte.`; },
  },
  mrc: {
    name: 'MRC Einzelmuskelkraft', sub: 'Kraftgrad', kind: 'grade', prefix: 'MRC ',
    items: [{ id: 'g', name: 'Kraftgrad', vertical: true, opts: [['5 · Normale Kraft', 5], ['4 · Gegen Widerstand', 4], ['3 · Gegen Schwerkraft', 3], ['2 · Schwerkraft eliminiert', 2], ['1 · Sicht-/tastbare Spur', 1], ['0 · Keine Kontraktion', 0]] }],
    interpret(t) { if (t >= 4) return { label: 'Erhaltene Kraft', level: 'good' }; if (t >= 2) return { label: 'Mäßige Parese', level: 'mod' }; return { label: 'Schwere Parese / Plegie', level: 'sev' }; },
    doc(v, t) { return `Kraftgrad nach MRC: ${t}/5.`; },
  },
  reflex: {
    name: 'Reflexstatus', sub: 'Strukturierte Dokumentation', kind: 'pick',
    items: [
      { id: 'bsr', name: 'BSR (Bizeps)', opts: REFLEX5 }, { id: 'rpr', name: 'RPR (Radius)', opts: REFLEX5 }, { id: 'tsr', name: 'TSR (Trizeps)', opts: REFLEX5 },
      { id: 'psr', name: 'PSR (Patella)', opts: REFLEX5 }, { id: 'asr', name: 'ASR (Achilles)', opts: REFLEX5 },
      { id: 'babl', name: 'Babinski links', opts: [['Negativ', 'neg'], ['Positiv', 'pos']] }, { id: 'babr', name: 'Babinski rechts', opts: [['Negativ', 'neg'], ['Positiv', 'pos']] },
    ],
    interpret(v) { const filled = ['bsr', 'rpr', 'tsr', 'psr', 'asr'].filter(k => v[k] != null).length; const pyr = v.babl === 'pos' || v.babr === 'pos'; if (!filled && v.babl == null) return { label: 'Reflexe erfassen', level: 'info' }; return pyr ? { label: 'Pyramidenbahnzeichen positiv', level: 'sev' } : { label: 'Reflexstatus dokumentiert', level: 'good' }; },
    doc(v) { const g = (k) => v[k] != null ? v[k] : '–'; return `Reflexstatus: BSR ${g('bsr')}, RPR ${g('rpr')}, TSR ${g('tsr')}, PSR ${g('psr')}, ASR ${g('asr')}. Babinski links ${v.babl || '–'}, rechts ${v.babr || '–'}.`; },
  },
  sensory: {
    name: 'Sensibilitätsmuster', sub: 'Lokalisationshilfe', kind: 'pick',
    items: [
      { id: 'mod', name: 'Modalität', opts: [['Berührung', 'taktil'], ['Schmerz/Temperatur', 'spinothal'], ['Vibration/Lage', 'hinterstrang'], ['Gemischt', 'gemischt']] },
      { id: 'dist', name: 'Verteilung', opts: [['Dermatom', 'radikulär'], ['Peripherer Nerv', 'peripher'], ['Strumpf-/Handschuh', 'polyneuro'], ['Hemisensorisch', 'zentral'], ['Dissoziiert (Schmerz/Temp)', 'spinothal']] },
    ],
    loc: { radikulär: ['Radikuläres Muster', 'mod'], peripher: ['Peripherer Nerv', 'mod'], polyneuro: ['Polyneuropathie', 'mod'], zentral: ['Zentral möglich', 'sev'], spinothal: ['Spinothalamisch / syringomyelisch möglich', 'sev'] },
    interpret(v) { const r = this.loc[v.dist]; return r ? { label: r[0], level: r[1] } : { label: 'Verteilung wählen', level: 'info' }; },
    doc(v) { const r = this.loc[v.dist]; return `Sensibilitätsstörung: Modalität ${v.mod || '–'}, Verteilung ${v.dist || '–'}, Lokalisation wahrscheinlich: ${r ? r[0] : '–'}.`; },
  },
};
for (const k in CORTEX_CALCS) CORTEX_CALCS[k].id = k;

// categories for the Tools page + floating menu
const CORTEX_CALC_CATS = [
  { id: 'akut', label: 'Akutneurologie & Schlaganfall', ic: 'bolt', ids: ['nihss', 'gcs', 'mrs', 'barthel', 'aspects', 'ich', 'hunthess', 'wfns', 'abcd2'] },
  { id: 'ms', label: 'Multiple Sklerose & Neuroimmunologie', ic: 'science', ids: ['edss', 'msfc', 'ninehole', 't25fw', 'sdmt'] },
  { id: 'park', label: 'Parkinson & Bewegungsstörungen', ic: 'fitness', ids: ['hoehn', 'updrs', 'schwab', 'aims'] },
  { id: 'dem', label: 'Demenz & Kognition', ic: 'psychology', ids: ['cogselect', 'moca', 'mmse', 'clock', 'demtect', 'gds'] },
  { id: 'kopf', label: 'Kopfschmerz & Migräne', ic: 'monitor_heart', ids: ['hit6', 'midas', 'mmd', 'moh', 'snnoop'] },
  { id: 'epi', label: 'Epilepsie & Status epilepticus', ic: 'timer', ids: ['setimer', 'ilae', 'stess'] },
  { id: 'nm', label: 'Neuromuskuläre Erkrankungen', ic: 'healing', ids: ['alsfrs', 'mgfa', 'mgadl', 'qmg', 'incat', 'mrcsum'] },
  { id: 'schw', label: 'Schwindel, Gang & Sturz', ic: 'hearing', ids: ['hints', 'dixhallpike', 'tug', 'berg', 'dhi'] },
  { id: 'schmerz', label: 'Schmerz, Radikulopathie & Neuropathie', ic: 'vibration', ids: ['nrs', 'dn4', 'paindetect', 'mrc', 'reflex', 'sensory'] },
];

// ─────────── calculator modal ───────────
function CalcModal({ calc, fav, onToggleFav, onClose }) {
  const radioInit = () => { const o = {}; calc.items.forEach(it => { if (!it.type || it.type === 'radio') o[it.id] = 0; }); return o; };
  const [sel, setSel] = cUseS(radioInit);
  const [checks, setChecks] = cUseS({});
  const [nums, setNums] = cUseS({});
  const reset = () => { setSel(radioInit()); setChecks({}); setNums({}); };

  const isCheckKind = calc.kind === 'check' || calc.kind === 'aspects';
  const isNumKind = calc.kind === 'num' || calc.kind === 'picknum';
  const isPick = calc.kind === 'pick';

  // build value object
  const vals = {};
  calc.items.forEach(it => {
    if (it.type === 'check') vals[it.id] = !!checks[it.id];
    else if (it.type === 'num') vals[it.id] = nums[it.id] === undefined || nums[it.id] === '' ? null : num(nums[it.id]);
    else vals[it.id] = it.opts[sel[it.id]][1];
  });

  // score + main display
  let score = null, main = '';
  if (calc.kind === 'gcs') { score = (vals.eye || 0) + (vals.verbal || 0) + (vals.motor || 0); main = score + '/15'; }
  else if (calc.kind === 'aspects') { const cnt = calc.items.filter(it => vals[it.id]).length; score = 10 - cnt; main = score + '/10'; }
  else if (calc.kind === 'check') { score = calc.items.filter(it => vals[it.id]).length; main = score + (calc.unit || ''); }
  else if (calc.kind === 'grade') { score = vals[calc.items[0].id]; main = (calc.prefix || '') + score + (calc.suffix || ''); }
  else if (isNumKind) { score = calc.score ? calc.score(vals) : null; }
  else if (isPick) { score = null; }
  else { score = calc.items.reduce((a, it) => a + (vals[it.id] || 0), 0); main = score + '/' + calc.max; }

  const band = isPick || isNumKind ? calc.interpret(vals) : calc.interpret(score, vals);
  const flags = calc.flags ? calc.flags(vals, score) : [];
  const docText = calc.doc(vals, score, band);
  const textDisplay = isPick || calc.display === 'text';

  if (isNumKind && !textDisplay) main = (score == null ? '—' : (Number.isInteger(score) ? score : r1(score))) + (calc.unit || '');

  const copy = () => {
    const done = () => window.dispatchEvent(new CustomEvent('cx-toast', { detail: { msg: 'Dokumentation kopiert' } }));
    if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(docText).then(done).catch(done);
    else done();
  };

  return (
    <div className="calc-modal">
      <div className="cm-top">
        <button className="cm-close" onClick={onClose} aria-label="Schließen"><Ic n="close" size={22} /></button>
        <div className="cm-ttl"><b>{calc.name}</b><span>{calc.sub}</span></div>
        {onToggleFav && <button className={`cm-fav${fav ? ' on' : ''}`} onClick={onToggleFav} aria-label={fav ? 'Favorit entfernen' : 'Zu Favoriten'}><Ic n={fav ? 'star' : 'star_border'} size={22} /></button>}
        <button className="cm-reset" onClick={reset}>Zurücksetzen</button>
      </div>

      <div className="cm-score">
        {textDisplay
          ? <div className={`cm-textband ${band.level}`}>{band.label}</div>
          : <><div className="cm-num"><span>{main}</span></div><div className={`cm-band ${band.level}`}>{band.label}</div></>}
      </div>

      <div className="cm-scroll">
        {flags.map((f, i) => <div className={`cm-flag ${f.level}`} key={i}><Ic n="warning" size={18} /> {f.msg}</div>)}
        {calc.note && <div className="cm-note"><Ic n="info" size={16} /> {calc.note}</div>}
        {calc.train && <div className="cm-note"><Ic n="info" size={16} /> Nur bei geschulter Anwendung zuverlässig.</div>}

        {calc.items.map(it => {
          if (it.type === 'check') {
            return (
              <button key={it.id} className={`cm-check${vals[it.id] ? ' on' : ''}`} onClick={() => setChecks(c => ({ ...c, [it.id]: !c[it.id] }))}>
                <span className="cm-box">{vals[it.id] && <Ic n="check" size={15} />}</span>
                <span className="cm-clabel">{it.name}</span>
              </button>
            );
          }
          if (it.type === 'num') {
            return (
              <div className="cm-item" key={it.id}>
                <div className="cm-numrow">
                  <span className="cm-item-h">{it.name}</span>
                  <span className="cm-numfield">
                    <input type="number" inputMode="decimal" value={nums[it.id] ?? ''} placeholder="—" onChange={e => setNums(n => ({ ...n, [it.id]: e.target.value }))} />
                    {it.unit && <i>{it.unit}</i>}
                  </span>
                </div>
              </div>
            );
          }
          return (
            <div className="cm-item" key={it.id}>
              <div className="cm-item-h">{it.name}</div>
              <div className={`cm-opts${it.vertical ? ' vert' : ''}`}>
                {it.opts.map(([label, val], i) => (
                  <button key={i} className={`cm-opt${sel[it.id] === i ? ' on' : ''}`} onClick={() => setSel(s => ({ ...s, [it.id]: i }))}>
                    {!isPick && <span className="cm-optv">{val === null ? 'T' : val}</span>}
                    <span className="cm-optl">{label}</span>
                  </button>
                ))}
              </div>
            </div>
          );
        })}

        <div className="cm-doc">
          <div className="cm-doc-h"><span>Dokumentation</span><button onClick={copy}><Ic n="link" size={15} /> Kopieren</button></div>
          <p className="cm-doc-t">{docText}</p>
        </div>
        <div className="cm-disc">Dieses Tool unterstützt die strukturierte klinische Einschätzung und ersetzt keine ärztliche Beurteilung, keine Leitlinie und keine lokale SOP.</div>
        <div style={{ height: 20 }} />
      </div>
    </div>
  );
}

window.CORTEX_CALCS = CORTEX_CALCS;
window.CORTEX_CALC_CATS = CORTEX_CALC_CATS;
window.CalcModal = CalcModal;
