import { useState, useEffect, useCallback, useRef } from "react";

// ═══════════════════════════════════════════
// STORAGE (window.storage in Claude, localStorage when deployed)
// ═══════════════════════════════════════════

const SKEY = 'gd-v10';
const ACCESS_KEY = 'beug-access-v1';
const Storage = {
  async get() {
    try {
      if (typeof window !== 'undefined' && window.storage?.get) {
        const r = await window.storage.get(SKEY);
        if (r?.value) return JSON.parse(r.value);
      }
    } catch (e) {}
    try {
      const v = localStorage.getItem(SKEY);
      if (v) return JSON.parse(v);
    } catch (e) {}
    return null;
  },
  async set(state) {
    const json = JSON.stringify(state);
    try {
      if (typeof window !== 'undefined' && window.storage?.set) {
        await window.storage.set(SKEY, json);
        return;
      }
    } catch (e) {}
    try { localStorage.setItem(SKEY, json); } catch (e) {}
  }
};

const Access = {
  get() {
    try {
      const v = localStorage.getItem(ACCESS_KEY);
      return v ? JSON.parse(v) : null;
    } catch (e) {
      return null;
    }
  },
  set(plan = 'paid') {
    const value = { paid: true, plan, unlockedAt: new Date().toISOString() };
    try { localStorage.setItem(ACCESS_KEY, JSON.stringify(value)); } catch (e) {}
    return value;
  }
};

// ═══════════════════════════════════════════
// DATA
// ═══════════════════════════════════════════

const DEF = {
  masculine:{nominative:'der',accusative:'den',dative:'dem',genitive:'des'},
  feminine: {nominative:'die',accusative:'die',dative:'der',genitive:'der'},
  neuter:   {nominative:'das',accusative:'das',dative:'dem',genitive:'des'},
  plural:   {nominative:'die',accusative:'die',dative:'den',genitive:'der'},
};
const INDEF = {
  masculine:{nominative:'ein', accusative:'einen',dative:'einem',genitive:'eines'},
  feminine: {nominative:'eine',accusative:'eine', dative:'einer',genitive:'einer'},
  neuter:   {nominative:'ein', accusative:'ein',  dative:'einem',genitive:'eines'},
  plural:   {nominative:'keine',accusative:'keine',dative:'keinen',genitive:'keiner'},
};
const STRONG = {
  masculine:{nominative:'r',accusative:'n',dative:'m',genitive:'n'},
  feminine: {nominative:'e',accusative:'e',dative:'r',genitive:'r'},
  neuter:   {nominative:'s',accusative:'s',dative:'m',genitive:'n'},
  plural:   {nominative:'e',accusative:'e',dative:'n',genitive:'r'},
};
const WEAK = {
  masculine:{nominative:'e',accusative:'n',dative:'n',genitive:'n'},
  feminine: {nominative:'e',accusative:'e',dative:'n',genitive:'n'},
  neuter:   {nominative:'e',accusative:'e',dative:'n',genitive:'n'},
  plural:   {nominative:'n',accusative:'n',dative:'n',genitive:'n'},
};

const PREPS = {
  durch:{c:'accusative',en:'through'}, für:{c:'accusative',en:'for'},
  gegen:{c:'accusative',en:'against'}, ohne:{c:'accusative',en:'without'},
  um:{c:'accusative',en:'around'}, bis:{c:'accusative',en:'until'},
  aus:{c:'dative',en:'out of'}, bei:{c:'dative',en:'at'},
  mit:{c:'dative',en:'with'}, nach:{c:'dative',en:'to/after'},
  seit:{c:'dative',en:'since'}, von:{c:'dative',en:'from'},
  zu:{c:'dative',en:'to'}, außer:{c:'dative',en:'besides'},
  gegenüber:{c:'dative',en:'opposite'},
  trotz:{c:'genitive',en:'despite'}, während:{c:'genitive',en:'during'},
  wegen:{c:'genitive',en:'because of'}, statt:{c:'genitive',en:'instead of'},
  anstatt:{c:'genitive',en:'instead of'}, außerhalb:{c:'genitive',en:'outside'},
  innerhalb:{c:'genitive',en:'inside'},
  an:{c:'wechsel',en:'at/on',m:'to',l:'at'},
  auf:{c:'wechsel',en:'on',m:'onto',l:'on'},
  hinter:{c:'wechsel',en:'behind',m:'behind',l:'behind'},
  in:{c:'wechsel',en:'in/into',m:'into',l:'in'},
  neben:{c:'wechsel',en:'next to',m:'next to',l:'next to'},
  über:{c:'wechsel',en:'over/above',m:'over',l:'above'},
  unter:{c:'wechsel',en:'under',m:'under',l:'under'},
  vor:{c:'wechsel',en:'in front of',m:'in front of',l:'in front of'},
  zwischen:{c:'wechsel',en:'between',m:'between',l:'between'},
};
const PREPS_BY_CASE = {
  accusative:['durch','für','gegen','ohne','um','bis'],
  dative:['aus','bei','mit','nach','seit','von','zu','außer','gegenüber'],
  genitive:['trotz','während','wegen','statt','anstatt','außerhalb','innerhalb'],
  wechsel:['an','auf','hinter','neben','unter','über','in','vor','zwischen'],
};
const COMMON_PREPS = ['mit','von','zu','in','an','auf','für','durch','ohne','um','aus','bei','nach','vor','über','wegen'];
const RARE_PREPS = ['gegen','bis','seit','außer','gegenüber','hinter','neben','unter','zwischen','trotz','während','statt','anstatt','außerhalb','innerhalb'];
const ALL_PREPS = [...COMMON_PREPS, ...RARE_PREPS];

const WECHSEL_EX = {
  an:       {noun:'Tür',    g:'feminine',  en:'door',   mot:'to',           loc:'at'},
  auf:      {noun:'Tisch',  g:'masculine', en:'table',  mot:'onto',         loc:'on'},
  hinter:   {noun:'Haus',   g:'neuter',    en:'house',  mot:'behind',       loc:'behind'},
  in:       {noun:'Park',   g:'masculine', en:'park',   mot:'into',         loc:'in'},
  neben:    {noun:'Tisch',  g:'masculine', en:'table',  mot:'next to',      loc:'next to'},
  über:     {noun:'Brücke', g:'feminine',  en:'bridge', mot:'over',         loc:'above'},
  unter:    {noun:'Brücke', g:'feminine',  en:'bridge', mot:'under',        loc:'under'},
  vor:      {noun:'Haus',   g:'neuter',    en:'house',  mot:'in front of',  loc:'in front of'},
  zwischen: {noun:'Bäume',  g:'plural',    en:'trees',  mot:'between',      loc:'between', plDat:'n'},
};
function wechselExample(prepKey, isMotion) {
  const ex = WECHSEL_EX[prepKey];
  const cas = isMotion ? 'accusative' : 'dative';
  const art = DEF[ex.g][cas];
  let n = ex.noun;
  if (ex.g === 'plural' && !isMotion && ex.plDat) n += ex.plDat;
  return { de: `${prepKey} ${art} ${n}`, en: `${isMotion ? ex.mot : ex.loc} the ${ex.en}` };
}

const NOUNS = [
  {w:'Hund',g:'masculine',en:'dog'},{w:'Mann',g:'masculine',en:'man'},
  {w:'Tisch',g:'masculine',en:'table'},{w:'Park',g:'masculine',en:'park'},
  {w:'Arzt',g:'masculine',en:'doctor'},{w:'Freund',g:'masculine',en:'friend'},
  {w:'Baum',g:'masculine',en:'tree'},{w:'Garten',g:'masculine',en:'garden'},
  {w:'Stuhl',g:'masculine',en:'chair'},{w:'Bruder',g:'masculine',en:'brother'},
  {w:'Lehrer',g:'masculine',en:'teacher'},{w:'Vater',g:'masculine',en:'father'},
  {w:'Frau',g:'feminine',en:'woman'},{w:'Katze',g:'feminine',en:'cat'},
  {w:'Schule',g:'feminine',en:'school'},{w:'Stadt',g:'feminine',en:'city'},
  {w:'Straße',g:'feminine',en:'street'},{w:'Tür',g:'feminine',en:'door'},
  {w:'Schwester',g:'feminine',en:'sister'},{w:'Mutter',g:'feminine',en:'mother'},
  {w:'Tochter',g:'feminine',en:'daughter'},{w:'Musik',g:'feminine',en:'music'},
  {w:'Nacht',g:'feminine',en:'night'},{w:'Woche',g:'feminine',en:'week'},
  {w:'Kind',g:'neuter',en:'child'},{w:'Haus',g:'neuter',en:'house'},
  {w:'Buch',g:'neuter',en:'book'},{w:'Auto',g:'neuter',en:'car'},
  {w:'Mädchen',g:'neuter',en:'girl'},{w:'Fenster',g:'neuter',en:'window'},
  {w:'Hotel',g:'neuter',en:'hotel'},{w:'Museum',g:'neuter',en:'museum'},
  {w:'Büro',g:'neuter',en:'office'},{w:'Restaurant',g:'neuter',en:'restaurant'},
];

const PRONOUNS = [
  {key:'ich',    nom:'ich', acc:'mich', dat:'mir',   en:'I',            enObj:'me',           tag:'1st person',     num:'sg'},
  {key:'du',     nom:'du',  acc:'dich', dat:'dir',   en:'you',          enObj:'you',          tag:'2nd informal',   num:'sg'},
  {key:'Sie_sg', nom:'Sie', acc:'Sie',  dat:'Ihnen', en:'you',          enObj:'you',          tag:'2nd formal',     num:'sg'},
  {key:'er',     nom:'er',  acc:'ihn',  dat:'ihm',   en:'he',           enObj:'him',          tag:'3rd masculine',  num:'sg'},
  {key:'sie_sg', nom:'sie', acc:'sie',  dat:'ihr',   en:'she',          enObj:'her',          tag:'3rd feminine',   num:'sg'},
  {key:'es',     nom:'es',  acc:'es',   dat:'ihm',   en:'it',           enObj:'it',           tag:'3rd neuter',     num:'sg'},
  {key:'wir',    nom:'wir', acc:'uns',  dat:'uns',   en:'we',           enObj:'us',           tag:'1st plural',     num:'pl'},
  {key:'ihr',    nom:'ihr', acc:'euch', dat:'euch',  en:"y'all",        enObj:"y'all",        tag:'2nd inf plural', num:'pl'},
  {key:'Sie_pl', nom:'Sie', acc:'Sie',  dat:'Ihnen', en:'you (formal)', enObj:'you (formal)', tag:'2nd formal pl',  num:'pl'},
  {key:'sie_pl', nom:'sie', acc:'sie',  dat:'ihnen', en:'they',         enObj:'them',         tag:'3rd plural',     num:'pl'},
];

const POSSESSIVES = [
  {stem:'mein',  en:'my'},
  {stem:'dein',  en:'your (informal)'},
  {stem:'sein',  en:'his / its'},
  {stem:'ihr',   en:'her / their'},
  {stem:'unser', en:'our'},
  {stem:'euer',  en:"y'all's"},
];
const POSS_ENDINGS = {
  masculine:{nominative:'',  accusative:'en', dative:'em', genitive:'es'},
  feminine: {nominative:'e', accusative:'e',  dative:'er', genitive:'er'},
  neuter:   {nominative:'',  accusative:'',   dative:'em', genitive:'es'},
  plural:   {nominative:'e', accusative:'e',  dative:'en', genitive:'er'},
};
const combinePoss = (stem, ending) => (stem === 'euer' && ending) ? 'eur' + ending : stem + ending;

const CL = {nominative:'Nominativ',accusative:'Akkusativ',dative:'Dativ',genitive:'Genitiv',wechsel:'Wechsel'};
const CASE_C = {nominative:'#2563eb',accusative:'#dc2626',dative:'#ea580c',genitive:'#16a34a',wechsel:'#7c3aed'};
const GENDER_C = {masculine:'#16a34a',feminine:'#db2777',neuter:'#2563eb',plural:'#7c3aed'};

const VERBS = {
  sein:    {de:{Er:'ist',Sie:'ist',Wir:'sind',Ich:'bin'},      en:{Er:'is',Sie:'is',Wir:'are',Ich:'am'}},
  stehen:  {de:{Er:'steht',Sie:'steht',Wir:'stehen',Ich:'stehe'}, en:{Er:'is standing',Sie:'is standing',Wir:'are standing',Ich:'am standing'}},
  sitzen:  {de:{Er:'sitzt',Sie:'sitzt',Wir:'sitzen',Ich:'sitze'}, en:{Er:'is sitting',Sie:'is sitting',Wir:'are sitting',Ich:'am sitting'}},
  warten:  {de:{Er:'wartet',Sie:'wartet',Wir:'warten',Ich:'warte'}, en:{Er:'waits',Sie:'waits',Wir:'wait',Ich:'wait'}},
  wohnen:  {de:{Er:'wohnt',Sie:'wohnt',Wir:'wohnen',Ich:'wohne'}, en:{Er:'lives',Sie:'lives',Wir:'live',Ich:'live'}},
  gehen:   {de:{Er:'geht',Sie:'geht',Wir:'gehen',Ich:'gehe'}, en:{Er:'walks',Sie:'walks',Wir:'walk',Ich:'walk'}},
  laufen:  {de:{Er:'läuft',Sie:'läuft',Wir:'laufen',Ich:'laufe'}, en:{Er:'runs',Sie:'runs',Wir:'run',Ich:'run'}},
  fahren:  {de:{Er:'fährt',Sie:'fährt',Wir:'fahren',Ich:'fahre'}, en:{Er:'drives',Sie:'drives',Wir:'drive',Ich:'drive'}},
  kommen:  {de:{Er:'kommt',Sie:'kommt',Wir:'kommen',Ich:'komme'}, en:{Er:'comes',Sie:'comes',Wir:'come',Ich:'come'}},
  reisen:  {de:{Er:'reist',Sie:'reist',Wir:'reisen',Ich:'reise'}, en:{Er:'travels',Sie:'travels',Wir:'travel',Ich:'travel'}},
};
const SUBJ_EN = {Er:'He',Sie:'She',Wir:'We',Ich:'I'};
const MOTION_VS = ['gehen','laufen','fahren','kommen','reisen'];
const LOCATION_VS = ['sein','stehen','sitzen','warten','wohnen'];

function mkv(stems) {
  return {
    ich: stems.ich, du: stems.du,
    er: stems.er, sie_sg: stems.er, es: stems.er,
    wir: stems.wir, sie_pl: stems.wir, Sie_sg: stems.wir, Sie_pl: stems.wir,
    ihr: stems.ihr,
  };
}
const VC = {
  kommen: {de:mkv({ich:'komme',du:'kommst',er:'kommt',wir:'kommen',ihr:'kommt'}),  en:mkv({ich:'come',du:'come',er:'comes',wir:'come',ihr:'come'})},
  sein:   {de:mkv({ich:'bin',du:'bist',er:'ist',wir:'sind',ihr:'seid'}),           en:mkv({ich:'am',du:'are',er:'is',wir:'are',ihr:'are'})},
  haben:  {de:mkv({ich:'habe',du:'hast',er:'hat',wir:'haben',ihr:'habt'}),         en:mkv({ich:'have',du:'have',er:'has',wir:'have',ihr:'have'})},
  sehen:  {de:mkv({ich:'sehe',du:'siehst',er:'sieht',wir:'sehen',ihr:'seht'}),     en:mkv({ich:'see',du:'see',er:'sees',wir:'see',ihr:'see'})},
  kennen: {de:mkv({ich:'kenne',du:'kennst',er:'kennt',wir:'kennen',ihr:'kennt'}),  en:mkv({ich:'know',du:'know',er:'knows',wir:'know',ihr:'know'})},
  lieben: {de:mkv({ich:'liebe',du:'liebst',er:'liebt',wir:'lieben',ihr:'liebt'}),  en:mkv({ich:'love',du:'love',er:'loves',wir:'love',ihr:'love'})},
  helfen: {de:mkv({ich:'helfe',du:'hilfst',er:'hilft',wir:'helfen',ihr:'helft'}),  en:mkv({ich:'help',du:'help',er:'helps',wir:'help',ihr:'help'})},
  geben:  {de:mkv({ich:'gebe',du:'gibst',er:'gibt',wir:'geben',ihr:'gebt'}),       en:mkv({ich:'give',du:'give',er:'gives',wir:'give',ihr:'give'})},
};

const SG_PRONOUN_KEYS = ['ich','du','er','sie_sg','es'];
const PL_PRONOUN_KEYS = ['wir','ihr','sie_pl'];
const FORMAL_PRONOUN_KEYS = ['Sie_sg','Sie_pl'];
const ALL_PRONOUN_KEYS = PRONOUNS.map(p=>p.key);
const SAFE_SUBJECT_KEYS = ['ich','du','er','wir','ihr'];

const TABS = [
  {id:'prep',    label:'Prepositions'},
  {id:'article', label:'Articles'},
  {id:'poss',    label:'Possessives'},
  {id:'pronoun', label:'Pronouns'},
];

const FREE_LEVEL_IDS = new Set([1, 2, 3, 4, 8, 9, 10, 18]);
const CORE_ARTICLE_CASES = ['nominative', 'accusative', 'dative'];
const STRIPE_PLANS = [
  {
    id:'yearly',
    name:'Yearly',
    price:'$9.99',
    cadence:'per year',
    note:'Best value',
    url: import.meta.env.VITE_STRIPE_YEARLY_URL || '',
    env:'VITE_STRIPE_YEARLY_URL',
  },
  {
    id:'lifetime',
    name:'Lifetime',
    price:'$19.99',
    cadence:'one time',
    note:'Pay once',
    url: import.meta.env.VITE_STRIPE_LIFETIME_URL || '',
    env:'VITE_STRIPE_LIFETIME_URL',
  },
  {
    id:'weekly',
    name:'Weekly',
    price:'$2.99',
    cadence:'per week',
    note:'Try it first',
    url: import.meta.env.VITE_STRIPE_WEEKLY_URL || '',
    env:'VITE_STRIPE_WEEKLY_URL',
  },
];

const LEVELS = [
  {id:1, tab:'article', name:'Nominativ', desc:'Gender basics: der, die, or das?', cases:['nominative'], preps:[], type:'article', artType:'definite'},
  {id:2, tab:'article', name:'Akkusativ', desc:'durch, für, gegen, ohne, um, bis', cases:['accusative'], preps:PREPS_BY_CASE.accusative, type:'article', artType:'definite'},
  {id:3, tab:'article', name:'Dativ', desc:'mit, von, zu, aus, bei, nach, seit...', cases:['dative'], preps:PREPS_BY_CASE.dative, type:'article', artType:'definite'},
  {id:4, tab:'article', name:'Wechselpräpositionen', desc:'Two-way: motion (Akk) vs location (Dat)', cases:['accusative','dative'], preps:PREPS_BY_CASE.wechsel, type:'article', artType:'definite', twoWay:true},
  {id:4.5, tab:'article', name:'Kontraktionen', desc:'Fused forms in context: ins, am, zum…', cases:['accusative','dative'], type:'chunk', artType:'definite'},
  {id:5, tab:'article', name:'Genitiv', desc:'trotz, während, wegen, statt...', cases:['genitive'], preps:PREPS_BY_CASE.genitive, type:'article', artType:'definite'},
  {id:6, tab:'article', name:'Alles gemischt', desc:'All prepositions, definite articles', cases:['nominative','accusative','dative','genitive'], preps:[...PREPS_BY_CASE.accusative,...PREPS_BY_CASE.dative,...PREPS_BY_CASE.genitive,...PREPS_BY_CASE.wechsel], type:'article', artType:'definite', mixed:true},
  {id:7, tab:'article', name:'ein-Wörter', desc:'Everything with ein/eine/einem...', cases:['nominative','accusative','dative','genitive'], preps:[...PREPS_BY_CASE.accusative,...PREPS_BY_CASE.dative,...PREPS_BY_CASE.genitive,...PREPS_BY_CASE.wechsel], type:'article', artType:'indefinite', mixed:true},

  {id:8,  tab:'prep', name:'Häufige Präpositionen', desc:'16 most common prepositions', preps:COMMON_PREPS, type:'prep'},
  {id:9,  tab:'prep', name:'Seltene Präpositionen', desc:'15 less common prepositions', preps:RARE_PREPS,   type:'prep'},
  {id:10, tab:'prep', name:'Alle Präpositionen',    desc:'All 31 prepositions mixed',   preps:ALL_PREPS,    type:'prep'},
  {id:18, tab:'prep', name:'Wechsel im Kontext',    desc:'Real sentences: Akkusativ or Dativ?', preps:PREPS_BY_CASE.wechsel, type:'wechselContext'},
  {id:19, tab:'prep', name:'Kontraktionen',         desc:'Contractions: am, ins, zum, beim…', type:'contraction'},

  {id:11, tab:'poss', name:'Häufige Possessive',   desc:'mein · dein · sein — all cases', possessives:['mein','dein','sein'], cases:['nominative','accusative','dative'], type:'poss'},
  {id:12, tab:'poss', name:'Schwierige Possessive', desc:'ihr · unser · euer — all cases', possessives:['ihr','unser','euer'], cases:['nominative','accusative','dative'], type:'poss'},
  {id:13, tab:'poss', name:'Alle gemischt',         desc:'mein, dein, sein, ihr, unser, euer — incl. Genitiv', possessives:['mein','dein','sein','ihr','unser','euer'], cases:['nominative','accusative','dative','genitive'], type:'poss'},

  {id:14, tab:'pronoun', name:'Singular Pronomen', desc:'ich, du, er, sie, es — all cases', pronouns:SG_PRONOUN_KEYS,      cases:['nominative','accusative','dative'], type:'pronoun'},
  {id:15, tab:'pronoun', name:'Plural Pronomen',   desc:'wir, ihr, sie — all cases',         pronouns:PL_PRONOUN_KEYS,      cases:['nominative','accusative','dative'], type:'pronoun'},
  {id:16, tab:'pronoun', name:'Sie (formal)',      desc:'Formal Sie — singular and plural',  pronouns:FORMAL_PRONOUN_KEYS,  cases:['nominative','accusative','dative'], type:'pronoun'},
  {id:17, tab:'pronoun', name:'Alle gemischt',     desc:'All 10 pronouns, all cases',        pronouns:ALL_PRONOUN_KEYS,     cases:['nominative','accusative','dative'], type:'pronoun'},
];

// Audio served from /public/audio
const AUDIO = {
  dativSung:  '/audio/dativ-blue-danube-sung.m4a',
  blueDanube: '/audio/blue-danube.mp3',
  odeToJoy:   '/audio/ode-to-joy.mp3',
};

// Chunked study cards for the Prepositions "Lernen" section.
// Mnemonics reproduced from the U-Michigan German Resources page.
const LEARN_CARDS = [
  {
    case:'accusative', label:'Akkusativ', desc:'These prepositions always take the Accusative.',
    words:[
      {de:'durch', en:'through'}, {de:'für', en:'for'}, {de:'gegen', en:'against'},
      {de:'ohne', en:'without'}, {de:'um', en:'around / at'}, {de:'bis', en:'until / up to'},
    ],
    mnemonics:[
      {label:'Acronym', text:'“O Fudge” — ohne, für, um, durch, gegen.'},
      {label:'Rhyme', text:'“Durch-für-gegen-ohne-um, Deutsch zu lernen ist nicht dumm.”'},
    ],
    audio:[],
  },
  {
    case:'dative', label:'Dativ', desc:'These prepositions always take the Dative.',
    words:[
      {de:'aus', en:'out of'}, {de:'außer', en:'except for'}, {de:'bei', en:'at / near'},
      {de:'mit', en:'with'}, {de:'nach', en:'to / after'}, {de:'seit', en:'since'},
      {de:'von', en:'from'}, {de:'zu', en:'to'}, {de:'gegenüber', en:'opposite'},
    ],
    mnemonics:[
      {label:'Sing it', text:'Sing “Aus-außer-bei-mit, nach-seit, von-zu” to the tune of the “Blue Danube” waltz.'},
      {label:'Rhyme', text:'“Roses are red, violets are blue, aus-außer-bei-mit, nach-seit, von-zu.”'},
    ],
    audio:[
      {label:'🎤 Sung to the Blue Danube', src:AUDIO.dativSung},
      {label:'🎻 The melody — The Blue Danube', src:AUDIO.blueDanube},
    ],
  },
  {
    case:'wechsel', label:'Wechselpräpositionen', desc:'Two-way: motion → Akkusativ (wohin?), staying put → Dativ (wo?).',
    words:[
      {de:'an', en:'at / on'}, {de:'auf', en:'on / onto'}, {de:'hinter', en:'behind'},
      {de:'neben', en:'next to'}, {de:'unter', en:'under'}, {de:'über', en:'over / above'},
      {de:'in', en:'in / into'}, {de:'vor', en:'in front of'}, {de:'zwischen', en:'between'},
    ],
    mnemonics:[
      {label:'Sing it', text:'Sing “An, auf, hin-ter, ne-ben, un-ter/ü-ber, in, vor, zwi-i-schen” to the tune of “An die Freude” (“Ode to Joy”) from Beethoven’s 9th.'},
    ],
    audio:[
      {label:'🎼 The melody — Ode to Joy', src:AUDIO.odeToJoy},
    ],
  },
  {
    case:'genitive', label:'Genitiv', desc:'These take the Genitive — only a handful, so just learn the list.',
    words:[
      {de:'trotz', en:'despite'}, {de:'während', en:'during'},
      {de:'wegen', en:'because of'}, {de:'(an)statt', en:'instead of'},
    ],
    mnemonics:[],
    note:'No song for these — there are only a few. Formal writing uses the genitive; casual speech increasingly uses the dative (e.g. „wegen dem Wetter"). Michigan also lists außerhalb / innerhalb / diesseits / jenseits, which must stay genitive.',
    audio:[],
  },
];

// Contractions (preposition + article). pref:true = preferred even in writing.
const CONTRACTIONS = [
  {prep:'an', art:'das', form:'ans', pref:true,  c:'wechsel'},
  {prep:'an', art:'dem', form:'am',  pref:true,  c:'wechsel'},
  {prep:'in', art:'das', form:'ins', pref:true,  c:'wechsel'},
  {prep:'in', art:'dem', form:'im',  pref:true,  c:'wechsel'},
  {prep:'auf',art:'das', form:'aufs',pref:true,  c:'wechsel'},
  {prep:'bei',art:'dem', form:'beim',pref:true,  c:'dative'},
  {prep:'von',art:'dem', form:'vom', pref:true,  c:'dative'},
  {prep:'zu', art:'dem', form:'zum', pref:true,  c:'dative'},
  {prep:'zu', art:'der', form:'zur', pref:true,  c:'dative'},
  {prep:'durch',art:'das',form:'durchs',pref:false,c:'accusative'},
  {prep:'für',art:'das', form:'fürs',pref:false, c:'accusative'},
  {prep:'um', art:'das', form:'ums', pref:false, c:'accusative'},
  {prep:'über',art:'das',form:'übers',pref:false,c:'wechsel'},
  {prep:'unter',art:'das',form:'unters',pref:false,c:'wechsel'},
  {prep:'vor',art:'das', form:'vors',pref:false, c:'wechsel'},
  {prep:'hinter',art:'das',form:'hinters',pref:false,c:'wechsel'},
];

// Only PREFERRED contractions drive the in-context tests, so the contracted
// form is unambiguously the best answer (the two-word form is the distractor).
const CONTRACTABLE = {};
CONTRACTIONS.filter(c=>c.pref).forEach(c => { (CONTRACTABLE[c.prep] = CONTRACTABLE[c.prep] || {})[c.art] = c.form; });
// Prepositions that get the fused "chunk" question in mixed/dedicated lessons.
const CHUNK_PREPS = new Set(Object.keys(CONTRACTABLE)); // an, in, auf, bei, von, zu
const WECHSEL_SET = new Set(PREPS_BY_CASE.wechsel);

const pick = a => a[Math.floor(Math.random()*a.length)];
const cap = s => s.charAt(0).toUpperCase() + s.slice(1);

// ═══════════════════════════════════════════
// QUESTION GENERATORS
// ═══════════════════════════════════════════

function genArticleQ(level) {
  const lv = LEVELS.find(l=>l.id===level);
  const noun = pick(NOUNS);
  const tbl = lv.artType === 'indefinite' ? INDEF : DEF;
  let cas, prepKey, tw = null, verb;
  if (lv.id === 1) { cas='nominative'; prepKey=null; }
  else if (lv.twoWay) { prepKey=pick(lv.preps); tw=Math.random()>0.5; cas=tw?'accusative':'dative'; }
  else if (lv.mixed) {
    if (Math.random()<0.12) { cas='nominative'; prepKey=null; }
    else { prepKey=pick(lv.preps); const pc=PREPS[prepKey].c;
      if (pc==='wechsel') { tw=Math.random()>0.5; cas=tw?'accusative':'dative'; } else cas=pc; }
  } else { prepKey=pick(lv.preps); cas=lv.cases[0]; }

  // Mixed definite lessons: contraction-capable prep → fused "chunk" question.
  if (lv.mixed && lv.artType === 'definite' && prepKey && CHUNK_PREPS.has(prepKey)) {
    return makeChunkQ(prepKey, noun, cas, tw);
  }

  const ans = tbl[noun.g][cas];
  const _pool = lv.artType === 'indefinite' ? ['ein','eine','einen','einem','eines','einer','keine','keinen','keiner'] : ['der','die','das','den','dem','des'];
  const _rem = _pool.filter(a => a !== ans);
  const _picked = [];
  while (_picked.length < 3 && _rem.length) _picked.push(_rem.splice(Math.floor(Math.random()*_rem.length), 1)[0]);
  const choices = [ans, ..._picked].sort(() => Math.random() - 0.5);
  let sentence, translation, rule;
  if (!prepKey) {
    sentence = `_____ ${noun.w} ist hier.`;
    translation = `The ${noun.en} is here.`;
    rule = 'Subject of the sentence → Nominativ';
  } else {
    const subj = pick(['Er','Sie','Wir']);
    if (tw===true) verb=pick(MOTION_VS);
    else if (tw===false) verb=pick(LOCATION_VS);
    else if (cas==='genitive') verb=pick(['warten','wohnen']);
    else verb=pick(MOTION_VS);
    sentence = `${subj} ${VERBS[verb].de[subj]} ${prepKey} _____ ${noun.w}.`;
    let pEn = PREPS[prepKey].en;
    if (PREPS[prepKey].c==='wechsel') pEn = tw ? PREPS[prepKey].m : PREPS[prepKey].l;
    translation = `${SUBJ_EN[subj]} ${VERBS[verb].en[subj]} ${pEn} the ${noun.en}.`;
    rule = PREPS[prepKey].c==='wechsel'
      ? (tw ? `„${prepKey}" + motion (wohin?) → Akkusativ` : `„${prepKey}" + location (wo?) → Dativ`)
      : `„${prepKey}" → always ${CL[cas]}`;
  }
  return {type:'article', noun, cas, prepKey, ans, sentence, translation, rule, tw, artType:lv.artType, choices};
}

function genPrepQ(level) {
  const lv = LEVELS.find(l=>l.id===level);
  const prepKey = pick(lv.preps);
  const p = PREPS[prepKey];
  const exampleNoun = pick(NOUNS);
  const exCase = p.c==='wechsel' ? 'dative' : p.c;
  return {type:'prep', prepKey, prepEn:p.en, ans:p.c,
    example:`${prepKey} ${DEF[exampleNoun.g][exCase]} ${exampleNoun.w}`, exampleEn:exampleNoun.en};
}

function genWechselContextQ(level) {
  const lv = LEVELS.find(l=>l.id===level);
  const prepKey = pick(lv.preps);
  const isMotion = Math.random() > 0.5;
  const cas = isMotion ? 'accusative' : 'dative';
  const subj = pick(['Er','Sie','Wir','Ich']);
  const verb = isMotion ? pick(MOTION_VS) : pick(LOCATION_VS);
  const ex = WECHSEL_EX[prepKey];
  const art = DEF[ex.g][cas];
  let nounStr = ex.noun;
  if (ex.g === 'plural' && !isMotion && ex.plDat) nounStr += ex.plDat;
  const sentence = `${subj} ${VERBS[verb].de[subj]} ${prepKey} ${art} ${nounStr}.`;
  const pEn = isMotion ? ex.mot : ex.loc;
  const translation = `${SUBJ_EN[subj]} ${VERBS[verb].en[subj]} ${pEn} the ${ex.en}.`;
  return {type:'wechselContext', prepKey, ans:cas, isMotion, verb, sentence, translation, nounEn: ex.en};
}

function genPossQ(level) {
  const lv = LEVELS.find(l=>l.id===level);
  const noun = pick(NOUNS);
  const stem = pick(lv.possessives);
  const possMeaning = POSSESSIVES.find(p=>p.stem===stem).en;
  let cas, prepKey=null, tw=null, verb;
  const wantPrep = Math.random() > 0.5;
  if (wantPrep) {
    const allPreps = lv.cases.includes('genitive')
      ? [...PREPS_BY_CASE.accusative,...PREPS_BY_CASE.dative,...PREPS_BY_CASE.wechsel,...PREPS_BY_CASE.genitive]
      : [...PREPS_BY_CASE.accusative,...PREPS_BY_CASE.dative,...PREPS_BY_CASE.wechsel];
    prepKey = pick(allPreps);
    const pc = PREPS[prepKey].c;
    if (pc==='wechsel') { tw=Math.random()>0.5; cas=tw?'accusative':'dative'; } else cas=pc;
    if (!lv.cases.includes(cas)) { cas=pick(lv.cases); prepKey=null; tw=null; }
  } else {
    cas = pick(lv.cases.filter(c => c !== 'genitive')) || lv.cases[0];
  }
  const ans = combinePoss(stem, POSS_ENDINGS[noun.g][cas]);
  let sentence, translation, rule;
  if (!prepKey) {
    if (cas==='nominative') {
      sentence = `_____ ${noun.w} ist hier.`;
      translation = `${cap(possMeaning)} ${noun.en} is here.`;
    } else if (cas==='accusative') {
      const v = pick([['liebe','love'],['sehe','see'],['kenne','know'],['höre','hear']]);
      sentence = `Ich ${v[0]} _____ ${noun.w}.`;
      translation = `I ${v[1]} ${possMeaning} ${noun.en}.`;
    } else if (cas==='dative') {
      sentence = `Ich helfe _____ ${noun.w}.`;
      translation = `I help ${possMeaning} ${noun.en}.`;
    }
  } else {
    const subj='Ich';
    if (tw===true) verb='gehen';
    else if (tw===false) verb='sein';
    else if (PREPS[prepKey].c==='genitive') verb='warten';
    else verb='gehen';
    sentence = `${subj} ${VERBS[verb].de[subj]} ${prepKey} _____ ${noun.w}.`;
    let pEn = PREPS[prepKey].en;
    if (PREPS[prepKey].c==='wechsel') pEn = tw ? PREPS[prepKey].m : PREPS[prepKey].l;
    translation = `${SUBJ_EN[subj]} ${VERBS[verb].en[subj]} ${pEn} ${possMeaning} ${noun.en}.`;
  }
  rule = `${stem}- + ${CL[cas]} + ${noun.g} → ${ans}`;
  return {type:'poss', noun, stem, possMeaning, cas, ans, sentence, translation, rule, prepKey, tw};
}

function genPronounQ(level) {
  const lv = LEVELS.find(l=>l.id===level);
  const pronKey = pick(lv.pronouns);
  const pron = PRONOUNS.find(p=>p.key===pronKey);
  const cas = pick(lv.cases);
  const rawAns = pron[{nominative:'nom',accusative:'acc',dative:'dat'}[cas]];

  let sentence, translation, ans;
  if (cas === 'nominative') {
    const tpl = pick([
      {v:'kommen', dePart:' aus Berlin', enPart:' from Berlin'},
      {v:'sein',   dePart:' müde',       enPart:' tired'},
      {v:'haben',  dePart:' einen Hund', enPart:' a dog'},
    ]);
    const vDe = VC[tpl.v].de[pronKey];
    const vEn = VC[tpl.v].en[pronKey];
    sentence = `_____ ${vDe}${tpl.dePart}.`;
    translation = `${cap(pron.en)} ${vEn}${tpl.enPart}.`;
    ans = cap(rawAns);
  } else {
    const subjCandidates = SAFE_SUBJECT_KEYS.filter(k => k !== pronKey);
    const subjKey = pick(subjCandidates);
    const subj = PRONOUNS.find(p=>p.key===subjKey);
    let tpl;
    if (cas === 'accusative') {
      tpl = pick([{v:'sehen'}, {v:'kennen'}, {v:'lieben'}]);
    } else {
      tpl = pick([{v:'helfen'}, {v:'geben', dePost:' das Buch', enPost:' the book'}]);
    }
    const sDe = VC[tpl.v].de[subjKey];
    const sEn = VC[tpl.v].en[subjKey];
    const dePost = tpl.dePost || '';
    const enPost = tpl.enPost || '';
    sentence = `${cap(subj.nom)} ${sDe} _____${dePost}.`;
    translation = `${cap(subj.en)} ${sEn} ${pron.enObj}${enPost}.`;
    ans = rawAns;
  }
  return {type:'pronoun', pronKey, pron, cas, ans, sentence, translation};
}

function genContractionQ() {
  const c = pick(CONTRACTIONS);
  return {type:'contraction', prep:c.prep, art:c.art, ans:c.form, pref:c.pref, cas:c.c};
}

// Build the 4 choices for a fused prep+article question: always a BLEND of
// contracted and two-word forms, so the shape never reveals whether it contracts.
function buildChunkChoices(prepKey, article) {
  const map = CONTRACTABLE[prepKey] || {};
  const contracts = !!map[article];
  const correct = contracts ? map[article] : `${prepKey} ${article}`;
  const cand = new Set();
  Object.values(map).forEach(f => cand.add(f));                 // contraction shapes
  Object.keys(map).forEach(a => cand.add(`${prepKey} ${a}`));   // their two-word forms
  cand.add(`${prepKey} ${article}`);                            // two-word of the real article
  cand.delete(correct);
  const pool = [...cand].sort(() => Math.random() - 0.5);
  const choices = [correct, ...pool.slice(0, 3)];
  const filler = ['das','dem','der','die','den'].map(a => `${prepKey} ${a}`);
  for (let i = 0; i < filler.length && choices.length < 4; i++) {
    if (!choices.includes(filler[i])) choices.push(filler[i]);
  }
  return { correct, contracts, choices: choices.slice(0, 4).sort(() => Math.random() - 0.5) };
}

function pickChunkVerb(prepKey, tw) {
  if (PREPS[prepKey].c === 'wechsel') return tw ? pick(MOTION_VS) : pick(LOCATION_VS);
  if (prepKey === 'bei') return pick(['sein','wohnen','warten']);
  if (prepKey === 'von') return 'kommen';
  if (prepKey === 'zu')  return pick(['gehen','fahren','kommen']);
  return pick(['gehen','fahren']);
}

function makeChunkQ(prepKey, noun, cas, tw) {
  const article = DEF[noun.g][cas];
  const { correct, contracts, choices } = buildChunkChoices(prepKey, article);
  const subj = pick(['Er','Sie','Wir','Ich']);
  const verb = pickChunkVerb(prepKey, tw);
  const sentence = `${subj} ${VERBS[verb].de[subj]} _____ ${noun.w}.`;
  let pEn = PREPS[prepKey].en;
  if (PREPS[prepKey].c === 'wechsel') pEn = tw ? PREPS[prepKey].m : PREPS[prepKey].l;
  const translation = `${SUBJ_EN[subj]} ${VERBS[verb].en[subj]} ${pEn} the ${noun.en}.`;
  const pref = contracts ? (CONTRACTIONS.find(x => x.prep === prepKey && x.art === article)?.pref ?? true) : null;
  return { type:'chunk', prepKey, noun, cas, ans:correct, choices, sentence, translation, tw, contracts, pref };
}

// Dedicated "Kontraktionen" level: answer ALWAYS contracts (preferred forms only).
function genContractionContextQ() {
  const c = pick(CONTRACTIONS.filter(x => x.pref && CHUNK_PREPS.has(x.prep)));
  const wechsel = WECHSEL_SET.has(c.prep);
  let cas, gender, tw = null;
  if (c.art === 'das')      { cas='accusative'; gender='neuter';                      tw = wechsel ? true  : null; }
  else if (c.art === 'dem') { cas='dative';     gender=pick(['masculine','neuter']);  tw = wechsel ? false : null; }
  else                      { cas='dative';     gender='feminine';                    tw = wechsel ? false : null; }
  const noun = pick(NOUNS.filter(n => n.g === gender));
  return makeChunkQ(c.prep, noun, cas, tw);
}

function gen(level) {
  const lv = LEVELS.find(l=>l.id===level);
  if (lv.type==='chunk')           return genContractionContextQ();
  if (lv.type==='contraction')     return genContractionQ();
  if (lv.type==='prep')            return genPrepQ(level);
  if (lv.type==='wechselContext')  return genWechselContextQ(level);
  if (lv.type==='poss')            return genPossQ(level);
  if (lv.type==='pronoun')         return genPronounQ(level);
  return genArticleQ(level);
}

// ═══════════════════════════════════════════
// MAIN APP
// ═══════════════════════════════════════════

const ROUND = 10;
const GREEN = '#58a700';
const RED = '#dc2626';

export default function App() {
  const [scr, setScr] = useState('home');
  const [tab, setTab] = useState('prep');
  const [lv, setLv] = useState(1);
  const [q, setQ] = useState(null);
  const [inp, setInp] = useState('');
  const [rev, setRev] = useState(false);
  const [ok, setOk] = useState(false);
  const [res, setRes] = useState([]);
  const [chart, setChart] = useState(false);
  const [learnStep, setLearnStep] = useState(0);
  const [st, setSt] = useState({sc:{},tc:0,ta:0,streak:0,ld:null});
  const [access, setAccess] = useState(null);
  const [payMsg, setPayMsg] = useState('');
  const [loaded, setLoaded] = useState(false);
  const [shake, setShake] = useState(false);
  const iref = useRef(null);

  useEffect(()=>{(async()=>{
    const data = await Storage.get();
    if (data) setSt(data);
    let nextAccess = Access.get();
    const params = new URLSearchParams(window.location.search);
    if (params.get('checkout') === 'success' || params.get('beug_unlock') === '1') {
      nextAccess = Access.set(params.get('plan') || 'paid');
      window.history.replaceState({}, '', window.location.pathname);
      setScr('menu');
    }
    if (nextAccess?.paid) setAccess(nextAccess);
    setLoaded(true);
  })();},[]);

  const save = useCallback(async s => {
    setSt(s);
    await Storage.set(s);
  },[]);

  const hasAccess = !!access?.paid;
  const gated = id => !hasAccess && !FREE_LEVEL_IDS.has(id);

  const unlocked = (id) => {
    return !gated(id);
  };

  const nextLevelInTab = (id) => {
    const lvObj = LEVELS.find(l => l.id === id);
    if (!lvObj) return null;
    const tabLevels = LEVELS.filter(l => l.tab === lvObj.tab).sort((a,b) => a.id - b.id);
    const idx = tabLevels.findIndex(l => l.id === id);
    if (idx === -1 || idx === tabLevels.length - 1) return null;
    return tabLevels[idx + 1];
  };

  const startLv = id => {
    if (!unlocked(id)) { setPayMsg(''); setScr('paywall'); return; }
    setLv(id); setRes([]); setInp(''); setRev(false); setQ(gen(id)); setScr('practice');
  };

  const openCheckout = plan => {
    if (!plan.url) {
      setPayMsg(`Stripe link missing: add ${plan.env} in the deployment environment.`);
      return;
    }
    window.location.href = plan.url;
  };

  const submitRef = useRef(null);
  const nextRef = useRef(null);

  const doSubmit = useCallback((overrideInput) => {
    const userAns = (overrideInput ?? inp).trim();
    if (!userAns) { setShake(true); setTimeout(()=>setShake(false),400); return; }
    const correct = userAns.toLowerCase() === q.ans.toLowerCase();
    if (overrideInput !== undefined) setInp(overrideInput);
    // Dismiss mobile keyboard so feedback is visible
    try { iref.current?.blur(); } catch(e){}
    setOk(correct); setRev(true);
    const nr = [...res, correct]; setRes(nr);
    const today = new Date().toISOString().slice(0,10);
    const ns = {...st};
    ns.ta = (ns.ta||0) + 1;
    if (correct) ns.tc = (ns.tc||0) + 1;
    if (!ns.sc[lv]) ns.sc[lv] = {c:0,t:0};
    ns.sc[lv] = {c: ns.sc[lv].c + (correct?1:0), t: ns.sc[lv].t + 1};
    if (ns.ld !== today) {
      const ld = ns.ld ? new Date(ns.ld) : null;
      ns.streak = ld && Math.round((new Date(today)-ld)/864e5)===1 ? (ns.streak||0)+1 : 1;
      ns.ld = today;
    }
    save(ns);
  }, [inp, q, res, st, lv, save]);

  const doNext = useCallback(() => {
    if (res.length >= ROUND) { setScr('summary'); return; }
    setInp(''); setRev(false); setOk(false); setQ(gen(lv));
  }, [res, lv]);

  submitRef.current = doSubmit;
  nextRef.current = doNext;

  useEffect(() => {
    const h = e => {
      if (chart) { if (e.key === 'Escape') setChart(false); return; }
      if (scr === 'summary' && e.key === 'Enter') { e.preventDefault(); startLv(lv); return; }
      if (scr !== 'practice') return;
      if (!rev && (q?.type === 'article' || q?.type === 'chunk') && q.choices) {
        const idx = ['1','2','3','4'].indexOf(e.key);
        if (idx >= 0 && q.choices[idx]) { e.preventDefault(); submitRef.current(q.choices[idx]); return; }
      }
      if (!rev && q?.type === 'prep') {
        if (e.key === '1') { e.preventDefault(); submitRef.current('accusative'); return; }
        if (e.key === '2') { e.preventDefault(); submitRef.current('dative');     return; }
        if (e.key === '3') { e.preventDefault(); submitRef.current('genitive');   return; }
        if (e.key === '4') { e.preventDefault(); submitRef.current('wechsel');    return; }
      }
      if (!rev && q?.type === 'wechselContext') {
        if (e.key === '1') { e.preventDefault(); submitRef.current('accusative'); return; }
        if (e.key === '2') { e.preventDefault(); submitRef.current('dative');     return; }
      }
      if (e.key === 'Enter') { e.preventDefault(); if (rev) nextRef.current(); else if (q?.type !== 'article' && q?.type !== 'chunk') submitRef.current(); }
      if (rev && (e.key === 'ArrowRight' || e.key === ' ')) { e.preventDefault(); nextRef.current(); }
    };
    window.addEventListener('keydown', h);
    return () => window.removeEventListener('keydown', h);
  });

  if (!loaded) return <div style={S.wrap}><p style={{color:'#999',textAlign:'center',paddingTop:120}}>Loading...</p></div>;

  const rc = res.filter(Boolean).length;
  const cur = LEVELS.find(l=>l.id===lv);
  const chartSection = scr === 'menu' ? tab : (q?.type === 'wechselContext' ? 'prep' : q?.type === 'chunk' ? 'article' : (q?.type || tab));
  const nextInTab = nextLevelInTab(lv);

  return (
    <div style={S.wrap}>
      <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap" rel="stylesheet"/>

      <div style={S.inner}>
        <div style={S.hdr}>
          {scr !== 'home' && scr !== 'menu'
            ? <button style={S.linkBtn} onClick={()=>setScr('menu')}>← back</button>
            : <div style={S.logo}>Beug</div>}
          <div style={{display:'flex',gap:14,alignItems:'center'}}>
            {scr === 'practice' && <button style={S.linkBtn} onClick={()=>setChart(true)}>charts</button>}
            {hasAccess && <span style={S.stat}>Unlocked</span>}
            <span style={S.stat}>🔥 {st.streak||0}</span>
          </div>
        </div>

        {/* HOME */}
        {scr === 'home' && (
          <HomeScreen
            hasAccess={hasAccess}
            onStart={()=>setScr('menu')}
          />
        )}

        {/* MENU */}
        {scr === 'menu' && (
          <div>
            <div style={S.tabs}>
	              {TABS.map(t => (
	                <button key={t.id}
	                  style={{...S.tab, ...(tab===t.id ? S.tabActive : {})}}
	                  onClick={()=>setTab(t.id)}>
	                  {t.label}
	                </button>
	              ))}
            </div>

            <div style={{marginTop:10}}>
              {tab === 'prep' && (
                <div style={{...S.lvRow, cursor:'pointer'}}
	                  onClick={()=>{ setLearnStep(0); setScr('learn'); }}>
                  <div style={{flex:1, minWidth:0}}>
                    <div style={S.lvName}>📚 Lernen</div>
                    <div style={S.lvDesc}>Chunk the lists first — Akkusativ, Dativ, Wechsel, Genitiv</div>
                  </div>
                </div>
              )}
              {LEVELS.filter(l => l.tab === tab).sort((a,b)=>a.id-b.id).map(l => {
	                const u = unlocked(l.id);
	                const s = st.sc[l.id];
	                const p = s ? Math.round(s.c/s.t*100) : null;
	                return (
	                  <div key={l.id}
	                    style={{...S.lvRow, ...(u ? {} : S.lvRowBlurred), cursor:'pointer'}}
	                    onClick={()=>startLv(l.id)}>
	                    <div style={{flex:1, minWidth:0}}>
	                      <div style={S.lvName}>{l.name}</div>
	                      <div style={S.lvDesc}>{l.desc}</div>
                      {s && <div style={{fontSize:11,color:'#bbb',marginTop:3}}>{s.c}/{s.t}</div>}
                    </div>
                    {p !== null && <div style={{...S.lvPct, color:p>=70?GREEN:RED}}>{p}%</div>}
                  </div>
                );
              })}
            </div>

            <button style={S.chartLinkBtn} onClick={()=>setChart(true)}>
              View {TABS.find(t=>t.id===tab).label.toLowerCase()} charts
            </button>
          </div>
        )}

        {/* PAYWALL */}
        {scr === 'paywall' && (
          <PaywallScreen
            message={payMsg}
            onCheckout={openCheckout}
            onBack={()=>setScr('menu')}
          />
        )}

        {/* LEARN: chunked study cards for prepositions */}
        {scr === 'learn' && (() => {
          const card = LEARN_CARDS[learnStep];
          const isLast = learnStep === LEARN_CARDS.length - 1;
          return (
            <div style={{paddingTop:8}}>
              <div style={{display:'flex', gap:6, justifyContent:'center', marginBottom:18}}>
                {LEARN_CARDS.map((c,i)=>(
                  <div key={c.case} style={{height:4, flex:1, maxWidth:64, borderRadius:2,
                    background: i<=learnStep ? CASE_C[c.case] : '#e5e5e5'}}/>
                ))}
              </div>

              <div style={{background:'#fff', border:'1px solid #eee', borderRadius:12, padding:'20px 18px'}}>
                <div style={{fontSize:12, fontWeight:700, textTransform:'uppercase', letterSpacing:1, color:CASE_C[card.case]}}>{card.label}</div>
                <div style={{fontSize:13, color:'#999', marginTop:4, marginBottom:16, lineHeight:1.5}}>{card.desc}</div>

                <div style={{display:'flex', flexWrap:'wrap', gap:8, marginBottom:16}}>
                  {card.words.map(w=>(
                    <div key={w.de} style={{background:'#f7f7f7', borderRadius:8, padding:'8px 12px'}}>
                      <span style={{fontSize:16, fontWeight:700, color:'#1a1a1a'}}>{w.de}</span>
                      <span style={{fontSize:12, color:'#aaa', marginLeft:6}}>{w.en}</span>
                    </div>
                  ))}
                </div>

                {card.mnemonics.length > 0 && (
                  <div style={{borderTop:'1px solid #f0f0f0', paddingTop:14}}>
                    <div style={{fontSize:11, fontWeight:700, textTransform:'uppercase', letterSpacing:0.5, color:'#bbb', marginBottom:8}}>Merkhilfe</div>
                    {card.mnemonics.map((m,i)=>(
                      <div key={i} style={{fontSize:13, color:'#444', lineHeight:1.55, marginBottom:8}}>
                        <span style={{fontSize:10, fontWeight:700, color:CASE_C[card.case], textTransform:'uppercase', letterSpacing:0.5, marginRight:6}}>{m.label}</span>
                        {m.text}
                      </div>
                    ))}
                  </div>
                )}

                {card.note && (
                  <div style={{borderTop:'1px solid #f0f0f0', paddingTop:14, fontSize:12, color:'#888', lineHeight:1.55}}>
                    {card.note}
                  </div>
                )}

                {card.audio.length > 0 && (
                  <div style={{marginTop:8, display:'flex', flexDirection:'column', gap:10}}>
                    {card.audio.map(a=>(
                      <div key={a.src}>
                        <div style={{fontSize:11, color:'#999', marginBottom:4}}>{a.label}</div>
                        <audio controls preload="none" src={a.src} style={{width:'100%', height:36}}/>
                      </div>
                    ))}
                  </div>
                )}
              </div>

              <div style={{display:'flex', gap:10, justifyContent:'center', marginTop:18}}>
                {learnStep > 0 && (
                  <button style={{...S.nextBtn, background:'#fff', color:'#666', border:'1px solid #ddd'}}
                    onClick={()=>setLearnStep(s=>s-1)}>← Zurück</button>
                )}
                {!isLast
                  ? <button style={S.nextBtn} onClick={()=>setLearnStep(s=>s+1)}>Weiter →</button>
                  : <button style={S.nextBtn} onClick={()=>setScr('menu')}>Fertig ✓</button>}
              </div>
            </div>
          );
        })()}

        {/* PRACTICE: sentence-based (article, poss, pronoun) */}
        {scr === 'practice' && (q?.type === 'article' || q?.type === 'chunk' || q?.type === 'poss' || q?.type === 'pronoun') && (
          <div style={{textAlign:'center', paddingTop:10}}>
            <div style={{...S.sentence, animation:shake?'shake 0.3s':'none'}}>
              {q.sentence.split('_____').map((part,i,a)=>(
                <span key={i}>
                  {part}
                  {i < a.length-1 && (
                    <span style={{...S.blank,
                      borderBottomColor: rev?(ok?GREEN:RED):'#bbb',
                      color: rev?(ok?GREEN:RED):'#1a1a1a',
                      minWidth: q.type==='poss' ? 110 : 80,
                    }}>
                      {rev ? q.ans : (inp || '\u00A0\u00A0\u00A0\u00A0\u00A0')}
                    </span>
                  )}
                </span>
              ))}
            </div>

            <div style={S.translation}>{q.translation}</div>

            <div style={S.keyInfo}>
              {(q.type === 'article' || q.type === 'chunk') && <>
                <span style={{color: GENDER_C[q.noun.g], fontWeight:700}}>{DEF[q.noun.g].nominative}</span>
                {' '}{q.noun.w}
                <span style={S.keyInfoMute}> ({q.noun.en})</span>
              </>}
              {q.type === 'poss' && <>
                <span style={{fontWeight:700}}>{q.stem}-</span>
                <span style={S.keyInfoMute}> ({q.possMeaning})</span>
              </>}
              {q.type === 'pronoun' && <>
                <span style={{fontWeight:700}}>{q.pron.nom}</span>
                <span style={S.keyInfoMute}> ({q.pron.en})</span>
                <span style={S.keyInfoMute}> · {q.pron.tag}</span>
              </>}
            </div>

            <div style={S.secondaryContext}>
              <span style={{color: CASE_C[q.cas], fontWeight:600}}>{CL[q.cas]}</span>
              {q.prepKey && <> · {q.prepKey} ({PREPS[q.prepKey].en})</>}
              {q.tw !== null && q.tw !== undefined && <> · {q.tw ? '→ motion' : '● location'}</>}
              {q.type === 'poss' && <> · <span style={{color:GENDER_C[q.noun.g]}}>{DEF[q.noun.g].nominative}</span> {q.noun.w}</>}
            </div>

            {!rev ? (
              (q.type === 'article' || q.type === 'chunk') ? (
                <div style={S.articleGrid}>
                  {q.choices.map((choice, i) => (
                    <button key={choice} style={S.articleBtn} onClick={()=>doSubmit(choice)}>
                      <div style={{fontSize: choice.includes(' ') ? 17 : 22, fontWeight:700}}>{choice}</div>
                      <div style={{fontSize:11,color:'#999',marginTop:4}}><kbd style={S.kbdSmall}>{i+1}</kbd></div>
                    </button>
                  ))}
                </div>
              ) : (
                <div style={S.inputWrap}>
                  <div style={S.inputRow}>
                    <input ref={iref} value={inp} onChange={e=>setInp(e.target.value)}
                      style={S.input}
                      placeholder={q.type==='poss' ? 'form…' : q.type==='pronoun' ? 'pronoun…' : 'article…'}
                      autoComplete="off" autoCapitalize="off" spellCheck={false}
                      enterKeyHint="go"
                      inputMode="text"/>
                    <button style={S.checkBtn} onClick={()=>doSubmit()}>Check</button>
                  </div>
                  <div style={S.hintText}>or press <kbd style={S.kbd}>Enter</kbd></div>
                </div>
              )
            ) : (
              q.type === 'pronoun'
                ? <PronounFeedback q={q} inp={inp} ok={ok}/>
                : q.type === 'chunk'
                ? <ChunkFeedback q={q} inp={inp} ok={ok}/>
                : <FillFeedback q={q} inp={inp} ok={ok}/>
            )}
          </div>
        )}

        {/* PRACTICE: preposition (which case?) */}
        {scr === 'practice' && q?.type === 'prep' && (
          <div style={{textAlign:'center', paddingTop:36}}>
            <div style={{fontSize:13,color:'#999',marginBottom:8}}>Which case does this preposition take?</div>
            <div style={{fontSize:48, fontWeight:700, color:'#1a1a1a', margin:'8px 0 4px'}}>{q.prepKey}</div>
            <div style={{fontSize:15, color:'#999', marginBottom:28}}>{q.prepEn}</div>
            {!rev ? (
              <div style={{display:'flex',gap:8,flexWrap:'wrap',justifyContent:'center',maxWidth:420,margin:'0 auto'}}>
                {[
                  ['accusative','Akkusativ','1'],['dative','Dativ','2'],
                  ['genitive','Genitiv','3'],['wechsel','Wechsel','4'],
                ].map(([val,lab,key]) => (
                  <button key={val} style={{...S.choiceBtn, borderColor: CASE_C[val]+'40'}}
                    onClick={()=>doSubmit(val)}>
                    <div style={{fontWeight:700, color: CASE_C[val]}}>{lab}</div>
                    <div style={{fontSize:11,color:'#999',marginTop:2}}><kbd style={S.kbdSmall}>{key}</kbd></div>
                  </button>
                ))}
              </div>
            ) : (
              <PrepFeedback q={q} inp={inp} ok={ok}/>
            )}
          </div>
        )}

        {/* PRACTICE: wechsel in context */}
        {scr === 'practice' && q?.type === 'wechselContext' && (
          <div style={{textAlign:'center', paddingTop:18}}>
            <div style={{...S.sentence, animation:shake?'shake 0.3s':'none'}}>{q.sentence}</div>
            <div style={S.translation}>{q.translation}</div>

            <div style={S.keyInfo}>
              <span style={{fontWeight:700}}>{q.prepKey}</span>
              <span style={S.keyInfoMute}> ({PREPS[q.prepKey].en})</span>
            </div>
            <div style={S.secondaryContext}>Wechselpräposition · motion or location?</div>

            {!rev ? (
              <div style={{display:'flex',gap:10,justifyContent:'center',maxWidth:340,margin:'8px auto 0'}}>
                {[['accusative','Akkusativ','1','motion'],['dative','Dativ','2','location']].map(([val,lab,key,hint]) => (
                  <button key={val} style={{...S.choiceBtn, borderColor: CASE_C[val]+'40', minWidth:130}}
                    onClick={()=>doSubmit(val)}>
                    <div style={{fontWeight:700, color: CASE_C[val]}}>{lab}</div>
                    <div style={{fontSize:11,color:'#999',marginTop:2}}>{hint} · <kbd style={S.kbdSmall}>{key}</kbd></div>
                  </button>
                ))}
              </div>
            ) : (
              <WechselContextFeedback q={q} inp={inp} ok={ok}/>
            )}
          </div>
        )}

        {/* PRACTICE: contractions */}
        {scr === 'practice' && q?.type === 'contraction' && (
          <div style={{textAlign:'center', paddingTop:30}}>
            <div style={{fontSize:13,color:'#999',marginBottom:12}}>Contract the preposition and article</div>
            <div style={{fontSize:34, fontWeight:700, color:'#1a1a1a', margin:'4px 0 6px'}}>
              {q.prep} <span style={{color:'#ccc'}}>+</span> {q.art} <span style={{color:'#ccc'}}>=</span> <span style={{color:'#ccc'}}>?</span>
            </div>
            <div style={{fontSize:12, color:'#aaa', marginBottom:22}}>
              <span style={{color:CASE_C[q.cas], fontWeight:600}}>{CL[q.cas]}</span>
            </div>
            {!rev ? (
              <div style={S.inputWrap}>
                <div style={S.inputRow}>
                  <input ref={iref} value={inp} onChange={e=>setInp(e.target.value)}
                    style={S.input} placeholder="contraction…"
                    autoComplete="off" autoCapitalize="off" spellCheck={false}
                    enterKeyHint="go" inputMode="text"/>
                  <button style={S.checkBtn} onClick={()=>doSubmit()}>Check</button>
                </div>
                <div style={S.hintText}>or press <kbd style={S.kbd}>Enter</kbd></div>
              </div>
            ) : (
              <ContractionFeedback q={q} inp={inp} ok={ok}/>
            )}
          </div>
        )}

        {scr === 'practice' && (
          <div style={S.dotsRow}>
            {Array.from({length:ROUND},(_,i)=>(
              <div key={i} style={{...S.dot,
                background: i<res.length?(res[i]?GREEN:RED):i===res.length?'#333':'#ddd',
              }}/>
            ))}
            <span style={S.dotsLabel}>{Math.min(res.length+(rev?0:1), ROUND)}/{ROUND} · {cur?.name}</span>
          </div>
        )}

        {scr === 'practice' && rev && (
          <div style={{textAlign:'center', marginTop:14}}>
            <button style={S.nextBtn} onClick={doNext}>
              {res.length >= ROUND ? 'See results' : 'Next'} <span style={{opacity:0.5,fontSize:12,marginLeft:4}}>↵</span>
            </button>
          </div>
        )}

        {/* SUMMARY */}
        {scr === 'summary' && (
          <div style={{textAlign:'center', paddingTop:60}}>
            <div style={{fontSize:12,color:'#999',textTransform:'uppercase',letterSpacing:2,fontWeight:600}}>Complete</div>
            <div style={{fontSize:56, fontWeight:700, color:'#1a1a1a', margin:'10px 0'}}>
              {rc}<span style={{fontSize:24, color:'#bbb'}}>/{ROUND}</span>
            </div>
            <div style={{...S.dotsRow, position:'static', marginTop:14}}>
              {res.map((r,i)=><div key={i} style={{...S.dot,width:11,height:11,borderRadius:6,background:r?GREEN:RED}}/>)}
            </div>
            <div style={{color:rc>=7?GREEN:'#d97706', fontSize:15, fontWeight:600, margin:'24px 0'}}>
              {rc>=9?'Ausgezeichnet!':rc>=7?'Gut gemacht!':rc>=5?'Keep practicing.':'Review the charts.'}
            </div>
            {rc>=7 && nextInTab && unlocked(nextInTab.id) && (
              <div style={{color:GREEN, fontSize:13, marginBottom:16}}>{nextInTab.name} unlocked.</div>
            )}
            <button style={S.nextBtn} onClick={()=>startLv(lv)}>Play again</button>
            {rc>=7 && nextInTab && unlocked(nextInTab.id) && (
              <button style={{...S.nextBtn, background:'#fff', color:GREEN, border:`1.5px solid ${GREEN}`, marginTop:10, marginLeft:8}}
                onClick={()=>startLv(nextInTab.id)}>
                Next: {nextInTab.name} →
              </button>
            )}
            <div style={{marginTop:16}}>
              <button style={S.linkBtn} onClick={()=>setScr('menu')}>back to menu</button>
            </div>
          </div>
        )}
      </div>

      {chart && <ChartModal section={chartSection} q={q} onClose={()=>setChart(false)}/>}

      <style>{`
        @keyframes shake{0%,100%{transform:translateX(0)}25%{transform:translateX(-6px)}75%{transform:translateX(6px)}}
        @keyframes fadeUp{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}
        input::placeholder{color:#bbb}
        *{box-sizing:border-box}
        html,body{margin:0;background:#f7f7f7}
        #root{min-height:100%}
        input{scroll-margin-top:60px;scroll-margin-bottom:80px}
      `}</style>
    </div>
  );
}

function HomeScreen({ onStart }) {
  return (
    <div style={{paddingTop:18}}>
      <section style={S.hero}>
        <div style={S.heroMark}>der · den · dem</div>
        <h1 style={S.heroTitle}>Beat German cases into muscle memory.</h1>
        <p style={S.heroCopy}>
          No mascot. No scenic route. Drill the prepositions, articles, possessives, and pronouns that keep slowing you down, then get back to actual German.
        </p>
        <div style={S.claimStack}>
          <div>Learn this stuff in days, not months.</div>
          <div>Built by fast German learners for other fast German learners.</div>
          <div>Have a feature request? Get in touch. Always looking to improve the tool.</div>
        </div>
        <div style={S.heroActions}>
          <button style={S.nextBtn} onClick={onStart}>Get started</button>
          <a
            style={S.mailBtn}
            href={`mailto:?subject=${encodeURIComponent('Beug feature request')}&body=${encodeURIComponent('Hey, I have a feature request for Beug:\n\n')}`}
          >
            Write an email
          </a>
        </div>
      </section>

      <CoreReference />
    </div>
  );
}

function CoreReference() {
  const prepositionGroups = [
    ['accusative', 'Akkusativ', PREPS_BY_CASE.accusative],
    ['dative', 'Dativ', PREPS_BY_CASE.dative],
    ['wechsel', 'Two-way', PREPS_BY_CASE.wechsel],
    ['genitive', 'Genitiv', PREPS_BY_CASE.genitive],
  ];
  const genders = ['masculine','feminine','neuter','plural'];

  return (
    <div style={{marginTop:20}}>
      <div style={S.sectionTitle}>Case map</div>
      <div style={S.coreGrid}>
        <div style={S.corePanel}>
          <div style={S.chartH}>Prepositions</div>
          {prepositionGroups.map(([key, label, words]) => (
            <div key={key} style={{fontSize:12, color:'#555', lineHeight:1.65, marginTop:8}}>
              <strong style={{color:CASE_C[key]}}>{label}:</strong> {words.join(', ')}
            </div>
          ))}
        </div>
        <div style={S.corePanel}>
          <div style={S.chartH}>Definite Articles</div>
          <table style={S.miniChart}>
            <thead>
              <tr>
                <th style={S.miniTh}></th>
                {genders.map(g => <th key={g} style={{...S.miniTh, color:GENDER_C[g]}}>{g.slice(0,4)}</th>)}
              </tr>
            </thead>
            <tbody>
              {CORE_ARTICLE_CASES.map(c => (
                <tr key={c}>
                  <td style={{...S.miniTd, textAlign:'left', color:CASE_C[c], fontWeight:700}}>{CL[c]}</td>
                  {genders.map(g => <td key={g} style={S.miniTd}>{DEF[g][c]}</td>)}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

function PaywallScreen({ message, onCheckout, onBack }) {
  return (
    <div style={{paddingTop:18}}>
      <div style={S.paywallCard}>
        <div style={S.heroMark}>Beug Premium</div>
        <h2 style={S.payTitle}>Take the whole thing.</h2>
        <p style={S.payCopy}>
          Possessives, pronouns, contractions, genitive articles, and mixed rounds. Built for people who want the case system handled, fast.
        </p>
        <div style={S.benefits}>
          {['Possessives and pronouns', 'Mixed article rounds', 'Contractions and genitive', 'Saved progress on this device'].map(item => (
            <div key={item} style={S.benefit}>✓ {item}</div>
          ))}
        </div>
        <div style={S.planGrid}>
          {STRIPE_PLANS.map(plan => (
            <button key={plan.id} style={{...S.planCard, ...(plan.id === 'yearly' ? S.planFeatured : {})}} onClick={()=>onCheckout(plan)}>
              <span style={S.planNote}>{plan.note}</span>
              <span style={S.planName}>{plan.name}</span>
              <span style={S.planPrice}>{plan.price}</span>
              <span style={S.planCadence}>{plan.cadence}</span>
            </button>
          ))}
        </div>
        {message && <div style={S.payMsg}>{message}</div>}
        <div style={S.payFine}>No account required for v1. Stripe redirects back here and unlocks this browser.</div>
        <button style={{...S.linkBtn, marginTop:16}} onClick={onBack}>Maybe later</button>
      </div>
    </div>
  );
}

// ═══════════════════════════════════════════
// FEEDBACK COMPONENTS
// ═══════════════════════════════════════════

function FillFeedback({ q, inp, ok }) {
  const genders = ['masculine','feminine','neuter','plural'];
  const stripCells = genders.map(g => {
    if (q.type === 'article') {
      const tbl = q.artType === 'indefinite' ? INDEF : DEF;
      return tbl[g][q.cas];
    }
    return combinePoss(q.stem, POSS_ENDINGS[g][q.cas]);
  });
  const stripLabel = q.type === 'article'
    ? (q.artType === 'indefinite' ? 'ein…' : 'der/die')
    : `${q.stem}-`;

  return (
    <div style={{...S.feedback, animation:'fadeUp 0.2s ease-out'}}>
      <div style={{fontSize:15, fontWeight:600, marginBottom:6, color:ok?GREEN:'#1a1a1a'}}>
        {ok ? '✓ Correct' : <>✗ <span style={{textDecoration:'line-through', color:'#999', fontWeight:400}}>{inp}</span> <span style={{color:'#999', fontWeight:400}}>→</span> <span style={{color:GREEN}}>{q.ans}</span></>}
      </div>
      <div style={{fontSize:13, color:'#666', marginBottom:!ok?4:10, lineHeight:1.5}}>{q.rule}</div>
      {!ok && (
        <div style={{fontSize:12, color:'#999', marginBottom:10, lineHeight:1.5}}>
          {q.noun.w} is <span style={{color:GENDER_C[q.noun.g]}}>{q.noun.g}</span>. <span style={{color:CASE_C[q.cas]}}>{CL[q.cas]}</span> + {q.noun.g} = <strong style={{color:'#1a1a1a'}}>{q.ans}</strong>
        </div>
      )}
      <table style={S.stripTbl}>
        <thead><tr>
          <th style={{...S.stripH, textAlign:'left', fontWeight:600, color: CASE_C[q.cas]}}>{CL[q.cas]}</th>
          {genders.map(g => <th key={g} style={{...S.stripH, color:GENDER_C[g]}}>{g.slice(0,4)}.</th>)}
        </tr></thead>
        <tbody><tr>
          <td style={{...S.stripD, textAlign:'left', color:'#aaa', fontSize:11}}>{stripLabel}</td>
          {genders.map((g, i) => {
            const hl = g === q.noun.g;
            return <td key={g} style={{
              ...S.stripD, fontWeight: hl ? 700 : 400,
              color: hl ? '#1a1a1a' : '#bbb',
              background: hl ? '#f3f3f3' : 'transparent',
              borderRadius: 4, fontSize: hl ? 15 : 13,
            }}>{stripCells[i]}</td>;
          })}
        </tr></tbody>
      </table>
    </div>
  );
}

function PrepFeedback({ q, inp, ok }) {
  const motEx = q.ans === 'wechsel' ? wechselExample(q.prepKey, true) : null;
  const locEx = q.ans === 'wechsel' ? wechselExample(q.prepKey, false) : null;

  return (
    <div style={{...S.feedback, animation:'fadeUp 0.2s ease-out', maxWidth:420}}>
      <div style={{fontSize:15, fontWeight:600, marginBottom:6, color:ok?GREEN:'#1a1a1a'}}>
        {ok ? <>✓ <span style={{color:CASE_C[q.ans]}}>{CL[q.ans]}</span></> : <>✗ <span style={{textDecoration:'line-through',color:'#999',fontWeight:400}}>{CL[inp]||inp}</span> <span style={{color:'#999',fontWeight:400}}>→</span> <span style={{color:CASE_C[q.ans]}}>{CL[q.ans]}</span></>}
      </div>
      <div style={{fontSize:13, color:'#666', marginBottom:8, lineHeight:1.5}}>
        „{q.prepKey}" → {q.ans === 'wechsel' ? 'Wechselpräposition (Akkusativ for motion, Dativ for location)' : `always ${CL[q.ans]}`}
      </div>
      {q.ans !== 'wechsel' && (
        <div style={{fontSize:13, color:'#999', fontStyle:'italic', marginTop:4}}>
          Example: {q.example} <span style={{color:'#bbb'}}>({q.prepEn} the {q.exampleEn})</span>
        </div>
      )}
      {q.ans === 'wechsel' && (
        <div style={{fontSize:12, color:'#999', marginTop:4, lineHeight:1.7}}>
          <div>Motion: <em>Er geht <strong>{motEx.de}</strong></em> (<span style={{color:CASE_C.accusative}}>Akk</span>) — "{motEx.en}"</div>
          <div>Location: <em>Er ist <strong>{locEx.de}</strong></em> (<span style={{color:CASE_C.dative}}>Dat</span>) — "{locEx.en}"</div>
        </div>
      )}
    </div>
  );
}

function WechselContextFeedback({ q, inp, ok }) {
  return (
    <div style={{...S.feedback, animation:'fadeUp 0.2s ease-out', maxWidth:420}}>
      <div style={{fontSize:15, fontWeight:600, marginBottom:6, color:ok?GREEN:'#1a1a1a'}}>
        {ok ? <>✓ <span style={{color:CASE_C[q.ans]}}>{CL[q.ans]}</span></> : <>✗ <span style={{textDecoration:'line-through',color:'#999',fontWeight:400}}>{CL[inp]||inp}</span> <span style={{color:'#999',fontWeight:400}}>→</span> <span style={{color:CASE_C[q.ans]}}>{CL[q.ans]}</span></>}
      </div>
      <div style={{fontSize:13, color:'#666', marginBottom:6, lineHeight:1.5}}>
        „{q.verb}" {q.isMotion ? 'implies motion (wohin?)' : 'implies location (wo?)'} → <span style={{color:CASE_C[q.ans], fontWeight:600}}>{CL[q.ans]}</span>
      </div>
      <div style={{fontSize:12, color:'#999', marginTop:4, lineHeight:1.6}}>
        Wechselpräpositionen take <span style={{color:CASE_C.accusative}}>Akkusativ</span> with motion verbs (gehen, fahren, laufen, kommen) and <span style={{color:CASE_C.dative}}>Dativ</span> with location verbs (sein, stehen, sitzen, warten).
      </div>
    </div>
  );
}

function ChunkFeedback({ q, inp, ok }) {
  const article = DEF[q.noun.g][q.cas];
  return (
    <div style={{...S.feedback, animation:'fadeUp 0.2s ease-out', maxWidth:420}}>
      <div style={{fontSize:15, fontWeight:600, marginBottom:6, color:ok?GREEN:'#1a1a1a'}}>
        {ok
          ? <>✓ <span style={{color:GREEN}}>{q.ans}</span></>
          : <>✗ <span style={{textDecoration:'line-through', color:'#999', fontWeight:400}}>{inp}</span> <span style={{color:'#999', fontWeight:400}}>→</span> <span style={{color:GREEN}}>{q.ans}</span></>}
      </div>
      <div style={{fontSize:13, color:'#666', marginBottom:6, lineHeight:1.5}}>
        {q.contracts
          ? <><strong>{q.prepKey}</strong> + <strong>{article}</strong> = <strong style={{color:'#1a1a1a'}}>{q.ans}</strong></>
          : <>„{q.prepKey}" + <span style={{color:CASE_C[q.cas]}}>{CL[q.cas]}</span> → <strong style={{color:'#1a1a1a'}}>{q.ans}</strong></>}
      </div>
      <div style={{fontSize:12, lineHeight:1.5, color: q.contracts ? GREEN : '#999'}}>
        {q.contracts
          ? 'Preferred — these fuse, so use the contraction in speech and writing.'
          : <><strong>{article}</strong> doesn’t fuse with „{q.prepKey}" — keep them as two words.</>}
      </div>
    </div>
  );
}

function ContractionFeedback({ q, inp, ok }) {
  return (
    <div style={{...S.feedback, animation:'fadeUp 0.2s ease-out', maxWidth:420}}>
      <div style={{fontSize:15, fontWeight:600, marginBottom:8, color:ok?GREEN:'#1a1a1a'}}>
        {ok
          ? <>✓ {q.prep} + {q.art} = <span style={{color:GREEN}}>{q.ans}</span></>
          : <>✗ <span style={{textDecoration:'line-through', color:'#999', fontWeight:400}}>{inp}</span> <span style={{color:'#999', fontWeight:400}}>→</span> <span style={{color:GREEN}}>{q.ans}</span></>}
      </div>
      <div style={{fontSize:13, color:'#666', marginBottom:6, lineHeight:1.5}}>
        {q.prep} + {q.art} = <strong style={{color:'#1a1a1a'}}>{q.ans}</strong>
      </div>
      <div style={{fontSize:12, color: q.pref ? GREEN : '#d97706', lineHeight:1.5}}>
        {q.pref
          ? 'Preferred — use this contraction in both speech and writing.'
          : 'Informal — common when speaking, but the two-word form is more usual in writing.'}
      </div>
    </div>
  );
}

function PronounFeedback({ q, inp, ok }) {
  const cases = ['nominative','accusative','dative'];
  const labels = { nominative:'Nom', accusative:'Acc', dative:'Dat' };
  const keys = { nominative:'nom', accusative:'acc', dative:'dat' };
  return (
    <div style={{...S.feedback, animation:'fadeUp 0.2s ease-out', maxWidth:420}}>
      <div style={{fontSize:15, fontWeight:600, marginBottom:8, color:ok?GREEN:'#1a1a1a'}}>
        {ok ? '✓ Correct' : <>✗ <span style={{textDecoration:'line-through', color:'#999', fontWeight:400}}>{inp}</span> <span style={{color:'#999', fontWeight:400}}>→</span> <span style={{color:GREEN}}>{q.ans}</span></>}
      </div>
      <div style={{fontSize:12, color:'#999', marginBottom:10, lineHeight:1.5}}>
        {q.pron.en} ({q.pron.tag}) — <span style={{color: CASE_C[q.cas]}}>{CL[q.cas]}</span> form
      </div>
      <table style={S.stripTbl}>
        <thead><tr>
          <th style={{...S.stripH, textAlign:'left', fontWeight:600}}>{q.pron.nom}</th>
          {cases.map(c => <th key={c} style={{...S.stripH, color: CASE_C[c]}}>{labels[c]}</th>)}
        </tr></thead>
        <tbody><tr>
          <td style={{...S.stripD, textAlign:'left', color:'#aaa', fontSize:11}}>{q.pron.en}</td>
          {cases.map(c => {
            const hl = c === q.cas;
            return <td key={c} style={{
              ...S.stripD, fontWeight: hl ? 700 : 400,
              color: hl ? '#1a1a1a' : '#bbb',
              background: hl ? '#f3f3f3' : 'transparent',
              borderRadius: 4, fontSize: hl ? 15 : 13,
            }}>{q.pron[keys[c]]}</td>;
          })}
        </tr></tbody>
      </table>
    </div>
  );
}

// ═══════════════════════════════════════════
// CHART MODAL
// ═══════════════════════════════════════════

function ChartModal({ section, q, onClose }) {
  const genders = ['masculine','feminine','neuter','plural'];
  const cases = ['nominative','accusative','dative','genitive'];

  return (
    <div style={S.overlay} onClick={onClose}>
      <div style={S.modal} onClick={e=>e.stopPropagation()}>
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:14}}>
          <div style={{fontSize:17,fontWeight:700}}>
            {section==='article'?'Articles':section==='prep'?'Prepositions':section==='poss'?'Possessives':section==='pronoun'?'Personal Pronouns':'Charts'}
          </div>
          <button onClick={onClose} style={{background:'none',border:'none',fontSize:18,color:'#999',cursor:'pointer'}}>✕</button>
        </div>

        {section === 'article' && <>
          <div style={{marginBottom:22}}>
            <div style={S.chartH}>All-in-One Declension Chart</div>
            <div style={{fontSize:11, color:'#999', marginBottom:8}}>Strong (S): no article carries the case. Weak (W): after a definite article.</div>
            <table style={S.fullChart}>
              <thead>
                <tr>
                  <th rowSpan={2} style={{...S.fcTh, textAlign:'left'}}></th>
                  {genders.map(g => <th key={g} colSpan={2} style={{...S.fcTh, color: GENDER_C[g], borderBottom:'1px solid #eee'}}>{g}</th>)}
                </tr>
                <tr>
                  {genders.map(g => (
                    <Frag2 key={g}>
                      <th style={{...S.fcTh, fontSize:10, color:'#aaa', fontWeight:600}}>S</th>
                      <th style={{...S.fcTh, fontSize:10, color:'#aaa', fontWeight:600}}>W</th>
                    </Frag2>
                  ))}
                </tr>
              </thead>
              <tbody>
                {cases.map(c => (
                  <tr key={c}>
                    <td style={{...S.fcTd, textAlign:'left', fontWeight:700, fontSize:11, color:CASE_C[c], textTransform:'uppercase', letterSpacing:0.3}}>{CL[c]}</td>
                    {genders.map(g => {
                      const hl = q && (q.type==='article'||q.type==='poss') && c === q.cas && g === q.noun?.g;
                      return (
                        <Frag2 key={g}>
                          <td style={{...S.fcTd, fontWeight: hl?700:400, color: hl?GREEN:'#444', background: hl?GREEN+'15':'transparent', borderRadius: 3}}>{STRONG[g][c]}</td>
                          <td style={{...S.fcTd, fontWeight: hl?700:400, color: hl?GREEN:'#999', background: hl?GREEN+'15':'transparent', borderRadius: 3}}>{WEAK[g][c]}</td>
                        </Frag2>
                      );
                    })}
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
          <div style={{marginBottom:22}}>
            <div style={S.chartH}>Unbestimmter Artikel (ein/eine/kein)</div>
            <SimpleChart table={INDEF} highlightCase={q?.cas} highlightGender={q?.noun?.g} active={q?.type==='article' && q?.artType==='indefinite'}/>
          </div>
          <div style={{marginBottom:18}}>
            <div style={S.chartH}>Bestimmter Artikel (der/die/das)</div>
            <SimpleChart table={DEF} highlightCase={q?.cas} highlightGender={q?.noun?.g} active={q?.type==='article' && q?.artType==='definite'}/>
          </div>
        </>}

        {section === 'prep' && (
          <div style={{marginBottom:18}}>
            <div style={S.chartH}>Prepositions by Case</div>
            <div style={{fontSize:13, lineHeight:2.2, color:'#444'}}>
              <div><strong style={{color:CASE_C.accusative}}>Akkusativ:</strong> durch, für, gegen, ohne, um, bis</div>
              <div><strong style={{color:CASE_C.dative}}>Dativ:</strong> aus, außer, bei, gegenüber, mit, nach, seit, von, zu</div>
              <div><strong style={{color:CASE_C.genitive}}>Genitiv:</strong> trotz, während, wegen, statt, anstatt, außerhalb, innerhalb</div>
              <div><strong style={{color:CASE_C.wechsel}}>Wechsel:</strong> {PREPS_BY_CASE.wechsel.join(', ')}</div>
            </div>
            <div style={{fontSize:12, color:'#999', marginTop:14, lineHeight:1.6}}>
              <strong>Wechsel</strong>: accusative for motion (wohin?), dative for location (wo?).
            </div>
          </div>
        )}

        {section === 'poss' && (
          <div style={{marginBottom:18}}>
            <div style={S.chartH}>Possessive Determiners</div>
            <div style={{fontSize:12, color:'#888', marginBottom:10}}>
              {POSSESSIVES.map(p => <div key={p.stem} style={{marginBottom:2}}><strong style={{color:'#444'}}>{p.stem}-</strong> <span style={{color:'#999'}}>{p.en}</span></div>)}
            </div>
            <div style={{fontSize:11, color:'#999', marginBottom:10}}>Endings follow the ein/kein pattern. <em>euer</em> drops to <em>eur-</em> when an ending is added.</div>
            <PossessiveChart highlightCase={q?.cas} highlightGender={q?.noun?.g} active={q?.type==='poss'} highlightStem={q?.stem}/>
          </div>
        )}

        {section === 'pronoun' && (
          <div style={{marginBottom:18}}>
            <div style={S.chartH}>Personal Pronouns</div>
            <PronounsChartCombined highlightCase={q?.cas} highlightKey={q?.pronKey} active={q?.type==='pronoun'}/>
          </div>
        )}

        <button style={{...S.nextBtn, width:'100%', marginTop:10}} onClick={onClose}>
          Close <span style={{opacity:0.5,fontSize:12,marginLeft:4}}>esc</span>
        </button>
      </div>
    </div>
  );
}

function Frag2({ children }) { return <>{children}</>; }

function SimpleChart({ table, highlightCase, highlightGender, active }) {
  const genders = ['masculine','feminine','neuter','plural'];
  const cases = ['nominative','accusative','dative','genitive'];
  return (
    <table style={S.fullChart}>
      <thead><tr><th style={S.fcTh}></th>{genders.map(g => <th key={g} style={{...S.fcTh, color:GENDER_C[g]}}>{g}</th>)}</tr></thead>
      <tbody>
        {cases.map(c => (
          <tr key={c}>
            <td style={{...S.fcTd, textAlign:'left', fontWeight:700, fontSize:11, color:CASE_C[c], textTransform:'uppercase', letterSpacing:0.3}}>{CL[c]}</td>
            {genders.map(g => {
              const hl = active && c === highlightCase && g === highlightGender;
              return <td key={g} style={{...S.fcTd, fontSize:14, fontWeight: hl?700:400, color: hl?GREEN:'#555', background: hl?GREEN+'15':'transparent', borderRadius: 3}}>{table[g][c]}</td>;
            })}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

function PossessiveChart({ highlightCase, highlightGender, active, highlightStem }) {
  const genders = ['masculine','feminine','neuter','plural'];
  const cases = ['nominative','accusative','dative','genitive'];
  const stem = highlightStem || 'mein';
  return (
    <div>
      <table style={S.fullChart}>
        <thead><tr>
          <th style={S.fcTh}></th>
          {genders.map(g => <th key={g} style={{...S.fcTh, color:GENDER_C[g]}}>{g}</th>)}
        </tr></thead>
        <tbody>
          {cases.map(c => (
            <tr key={c}>
              <td style={{...S.fcTd, textAlign:'left', fontWeight:700, fontSize:11, color:CASE_C[c], textTransform:'uppercase', letterSpacing:0.3}}>{CL[c]}</td>
              {genders.map(g => {
                const hl = active && c === highlightCase && g === highlightGender;
                const display = combinePoss(stem, POSS_ENDINGS[g][c]);
                return <td key={g} style={{...S.fcTd, fontSize:14, fontWeight: hl?700:400, color: hl?GREEN:'#555', background: hl?GREEN+'15':'transparent', borderRadius: 3}}>{display}</td>;
              })}
            </tr>
          ))}
        </tbody>
      </table>
      <div style={{fontSize:11, color:'#aaa', marginTop:6}}>Showing forms for <strong style={{color:'#666'}}>{stem}-</strong>. Same endings apply to all possessive bases.</div>
    </div>
  );
}

function PronounsChartCombined({ highlightCase, highlightKey, active }) {
  const cases = ['nominative','accusative','dative'];
  const labels = { nominative:'Nom', accusative:'Acc', dative:'Dat' };
  const keys = { nominative:'nom', accusative:'acc', dative:'dat' };
  return (
    <table style={S.fullChart}>
      <thead><tr>
        <th style={{...S.fcTh, textAlign:'left'}}>english</th>
        <th style={{...S.fcTh, textAlign:'left', color:'#aaa'}}>person</th>
        {cases.map(c => <th key={c} style={{...S.fcTh, color: CASE_C[c]}}>{labels[c]}</th>)}
      </tr></thead>
      <tbody>
        {PRONOUNS.map((p, idx) => {
          const sep = idx === 6;
          return (
            <tr key={p.key} style={{borderTop: sep ? '2px solid #ddd' : 'none'}}>
              <td style={{...S.fcTd, textAlign:'left', fontSize:12, color:'#888'}}>{p.en}</td>
              <td style={{...S.fcTd, textAlign:'left', fontSize:11, color:'#bbb'}}>{p.tag}</td>
              {cases.map(c => {
                const hl = active && c === highlightCase && p.key === highlightKey;
                return <td key={c} style={{...S.fcTd, fontSize:14, fontWeight: hl?700:400, color: hl?GREEN:'#555', background: hl?GREEN+'15':'transparent', borderRadius: 3}}>{p[keys[c]]}</td>;
              })}
            </tr>
          );
        })}
      </tbody>
    </table>
  );
}

// ═══════════════════════════════════════════
// STYLES
// ═══════════════════════════════════════════

const S = {
  wrap: { background:'#f7f7f7', minHeight:'100vh', color:'#1a1a1a', fontFamily:'"DM Sans",system-ui,sans-serif' },
  inner: { maxWidth:520, margin:'0 auto', padding:'16px 20px 100px' },

  hdr: { display:'flex', justifyContent:'space-between', alignItems:'center', padding:'10px 0 8px', position:'sticky', top:0, background:'#f7f7f7', zIndex:10 },
  logo: { fontSize:17, fontWeight:700, color:'#1a1a1a' },
  linkBtn: { background:'none', border:'none', color:'#999', fontSize:13, cursor:'pointer', padding:0, fontWeight:500, fontFamily:'inherit' },
  stat: { fontSize:13, color:'#999', fontWeight:500 },

  tabs: { display:'flex', gap:0, borderBottom:'1px solid #eee', marginTop:6, marginBottom:12 },
  tab: { flex:1, background:'none', border:'none', padding:'12px 8px', cursor:'pointer', color:'#999', fontSize:13, fontWeight:600, fontFamily:'inherit', borderBottomWidth:2, borderBottomStyle:'solid', borderBottomColor:'transparent', marginBottom:-1, transition:'all 0.15s' },
  tabActive: { color:'#1a1a1a', borderBottomColor:GREEN },

  hero: { background:'#fff', border:'1px solid #eee', borderRadius:12, padding:'24px 22px', textAlign:'left' },
  heroMark: { fontSize:12, color:GREEN, fontWeight:800, textTransform:'uppercase', letterSpacing:0.8, marginBottom:10 },
  heroTitle: { fontSize:38, lineHeight:1.05, margin:'0 0 12px', color:'#1a1a1a', letterSpacing:0, fontWeight:800 },
  heroCopy: { fontSize:15, lineHeight:1.55, color:'#666', margin:'0 0 20px' },
  claimStack: { display:'grid', gap:8, margin:'0 0 20px', fontSize:13, lineHeight:1.35, color:'#222', fontWeight:800 },
  heroActions: { display:'flex', gap:10, flexWrap:'wrap' },
  secondaryBtn: { padding:'11px 18px', borderRadius:8, border:'1.5px solid #ddd', background:'#fff', color:'#444', fontSize:14, fontWeight:700, cursor:'pointer', fontFamily:'inherit' },
  mailBtn: { display:'inline-flex', alignItems:'center', justifyContent:'center', padding:'11px 18px', borderRadius:8, border:'1.5px solid #ddd', background:'#fff', color:'#444', fontSize:14, fontWeight:700, cursor:'pointer', fontFamily:'inherit', textDecoration:'none' },
  sectionTitle: { fontSize:12, color:'#999', textTransform:'uppercase', letterSpacing:1, fontWeight:800, margin:'0 0 8px' },
  coreGrid: { display:'grid', gridTemplateColumns:'1fr', gap:10 },
  corePanel: { background:'#fff', border:'1px solid #eee', borderRadius:10, padding:'16px 16px' },
  miniChart: { width:'100%', borderCollapse:'collapse', marginTop:10 },
  miniTh: { padding:'5px 4px', fontSize:10, color:'#aaa', textAlign:'center', textTransform:'uppercase', fontWeight:700 },
  miniTd: { padding:'7px 4px', fontSize:13, color:'#444', borderBottom:'1px solid #f3f3f3', textAlign:'center' },

  lvRow: { display:'flex', alignItems:'center', gap:14, padding:'13px 16px', marginBottom:6, background:'#fff', borderRadius:10, border:'1px solid #eee' },
  lvRowBlurred: { filter:'blur(2px)', opacity:0.62 },
  lvName: { fontSize:14, fontWeight:700, color:'#1a1a1a' },
  lvDesc: { fontSize:12, color:'#999', marginTop:1 },
  lvPct: { fontSize:14, fontWeight:700 },
  chartLinkBtn: { width:'100%', padding:12, marginTop:20, background:'#fff', border:'1px solid #eee', borderRadius:10, color:'#888', fontSize:13, cursor:'pointer', fontFamily:'inherit' },

  sentence: { fontSize:26, fontWeight:600, color:'#1a1a1a', lineHeight:1.5, margin:'0 0 10px', letterSpacing:'-0.3px' },
  blank: { display:'inline-block', minWidth:80, borderBottom:'2px solid', textAlign:'center', padding:'0 6px', margin:'0 2px', fontWeight:700, transition:'all 0.15s' },
  translation: { fontSize:14, color:'#999', marginBottom:10 },

  keyInfo: { fontSize:16, color:'#333', fontWeight:600, marginBottom:4 },
  keyInfoMute: { color:'#aaa', fontWeight:400 },
  secondaryContext: { fontSize:12, color:'#aaa', marginBottom:16, lineHeight:1.5 },

  inputWrap: { display:'flex', flexDirection:'column', alignItems:'center', gap:6 },
  inputRow: { display:'flex', gap:8, alignItems:'center' },
  input: { width:170, padding:'12px 14px', borderRadius:8, border:'1.5px solid #ddd', background:'#fff', color:'#1a1a1a', fontSize:18, fontWeight:600, outline:'none', textAlign:'center', fontFamily:'inherit' },
  checkBtn: { padding:'12px 20px', borderRadius:8, background:GREEN, color:'#fff', border:'none', fontSize:14, fontWeight:700, cursor:'pointer', fontFamily:'inherit' },
  hintText: { fontSize:11, color:'#bbb' },
  kbd: { background:'#f0f0f0', padding:'1px 5px', borderRadius:3, fontSize:10, color:'#888', border:'1px solid #e0e0e0' },
  kbdSmall: { background:'#f0f0f0', padding:'1px 4px', borderRadius:3, fontSize:9, color:'#888', border:'1px solid #e0e0e0' },

  articleGrid: { display:'grid', gridTemplateColumns:'1fr 1fr', gap:10, maxWidth:320, margin:'12px auto 0' },
  articleBtn: { padding:'18px 16px', borderRadius:10, border:'1.5px solid #ddd', background:'#fff', color:'#1a1a1a', cursor:'pointer', fontFamily:'inherit', textAlign:'center', transition:'all 0.12s' },

  choiceBtn: { flex:'1 1 auto', minWidth:90, padding:'14px 16px', borderRadius:10, border:'1.5px solid', background:'#fff', color:'#1a1a1a', cursor:'pointer', fontSize:14, fontFamily:'inherit', textAlign:'center', transition:'all 0.12s' },

  feedback: { textAlign:'left', background:'#fff', borderRadius:10, padding:'14px 18px', margin:'0 auto', maxWidth:400, border:'1px solid #eee' },

  stripTbl: { width:'100%', borderCollapse:'collapse', marginTop:10 },
  stripH: { padding:'4px 6px', fontSize:10, fontWeight:600, textTransform:'uppercase', letterSpacing:0.3, color:'#bbb', textAlign:'center' },
  stripD: { padding:'6px', textAlign:'center', fontSize:13 },

  dotsRow: { display:'flex', gap:5, justifyContent:'center', alignItems:'center', position:'fixed', bottom:20, left:0, right:0 },
  dotsLabel: { fontSize:11, color:'#aaa', marginLeft:10 },
  dot: { width:7, height:7, borderRadius:4, transition:'background 0.2s' },

  nextBtn: { padding:'11px 22px', borderRadius:8, border:'none', background:GREEN, color:'#fff', fontSize:14, fontWeight:700, cursor:'pointer', fontFamily:'inherit' },

  paywallCard: { background:'#fff', border:'1px solid #eee', borderRadius:12, padding:'24px 20px', textAlign:'left' },
  payTitle: { fontSize:30, lineHeight:1.1, margin:'0 0 10px', color:'#1a1a1a', letterSpacing:0, fontWeight:800 },
  payCopy: { fontSize:14, lineHeight:1.55, color:'#666', margin:'0 0 16px' },
  benefits: { display:'grid', gap:7, margin:'0 0 18px' },
  benefit: { fontSize:13, color:'#444', fontWeight:600 },
  planGrid: { display:'grid', gridTemplateColumns:'1fr', gap:10 },
  planCard: { position:'relative', display:'grid', gridTemplateColumns:'1fr auto', alignItems:'end', gap:'2px 12px', width:'100%', padding:'15px 14px', borderRadius:10, border:'1.5px solid #e6e6e6', background:'#fff', color:'#1a1a1a', cursor:'pointer', fontFamily:'inherit', textAlign:'left' },
  planFeatured: { borderColor:GREEN, boxShadow:'0 0 0 3px rgba(88,167,0,0.10)' },
  planNote: { gridColumn:'1 / -1', fontSize:10, fontWeight:800, letterSpacing:0.6, textTransform:'uppercase', color:GREEN, marginBottom:2 },
  planName: { fontSize:14, fontWeight:800 },
  planPrice: { fontSize:20, fontWeight:800, textAlign:'right' },
  planCadence: { fontSize:11, color:'#999' },
  payMsg: { marginTop:12, border:'1px solid #f5d0d0', background:'#fff7f7', color:'#b91c1c', borderRadius:8, padding:'10px 12px', fontSize:12, lineHeight:1.4 },
  payFine: { marginTop:12, fontSize:11, color:'#aaa', lineHeight:1.4 },

  overlay: { position:'fixed', inset:0, background:'rgba(0,0,0,0.35)', display:'flex', alignItems:'center', justifyContent:'center', zIndex:100, padding:16 },
  modal: { background:'#fff', borderRadius:14, padding:'22px 22px', maxWidth:520, width:'100%', maxHeight:'88vh', overflow:'auto', boxShadow:'0 8px 30px rgba(0,0,0,0.12)' },

  chartH: { fontSize:13, fontWeight:700, color:'#1a1a1a', marginBottom:6 },
  fullChart: { width:'100%', borderCollapse:'collapse', fontSize:14 },
  fcTh: { padding:'6px 4px', fontSize:11, fontWeight:600, textAlign:'center', color:'#888', textTransform:'capitalize' },
  fcTd: { padding:'7px 4px', textAlign:'center', fontSize:14, borderBottom:'1px solid #f3f3f3' },
};
