// ── CharacterSheetTab — D&D 5.5e (2024 PHB) sheet ──────────────────────────
// Wraps the new Character columns into a single sheet view. Layout sections:
//   Identity, Abilities, Combat, Saves, Skills, Conditions / Death saves
// Languages, Features, Backstory will land in a follow-up iteration.
//
// Editing model: `editable` is true for the character's owning user or any
// DM/Admin of their campaign. Each field has an inline edit affordance —
// numbers are <input type=number>, role-style pickers use <select>, etc.
// Changes auto-save via api.characters.update on blur (or on toggle for
// checkboxes/selects), so there's no global "Save" button — feels more like
// a live sheet, less like a form.

// PHB 2024 skill list with associated ability key.
const SKILLS = [
  { key:'acrobatics',     label:'Acrobatics',     ability:'dex' },
  { key:'animalHandling', label:'Animal Handling',ability:'wis' },
  { key:'arcana',         label:'Arcana',         ability:'int' },
  { key:'athletics',      label:'Athletics',      ability:'str' },
  { key:'deception',      label:'Deception',      ability:'cha' },
  { key:'history',        label:'History',        ability:'int' },
  { key:'insight',        label:'Insight',        ability:'wis' },
  { key:'intimidation',   label:'Intimidation',   ability:'cha' },
  { key:'investigation',  label:'Investigation',  ability:'int' },
  { key:'medicine',       label:'Medicine',       ability:'wis' },
  { key:'nature',         label:'Nature',         ability:'int' },
  { key:'perception',     label:'Perception',     ability:'wis' },
  { key:'performance',    label:'Performance',    ability:'cha' },
  { key:'persuasion',     label:'Persuasion',     ability:'cha' },
  { key:'religion',       label:'Religion',       ability:'int' },
  { key:'sleightOfHand',  label:'Sleight of Hand',ability:'dex' },
  { key:'stealth',        label:'Stealth',        ability:'dex' },
  { key:'survival',       label:'Survival',       ability:'wis' },
];

const ABILITIES = [
  { key:'str', long:'Strength',     col:'strength'     },
  { key:'dex', long:'Dexterity',    col:'dexterity'    },
  { key:'con', long:'Constitution', col:'constitution' },
  { key:'int', long:'Intelligence', col:'intelligence' },
  { key:'wis', long:'Wisdom',       col:'wisdom'       },
  { key:'cha', long:'Charisma',     col:'charisma'     },
];

// 5e modifier formula: floor((score - 10) / 2).
const abilityMod = (score) => Math.floor(((score|0) - 10) / 2);
// Proficiency bonus by level: 1-4→+2, 5-8→+3, 9-12→+4, 13-16→+5, 17-20→+6.
const profBonusFor = (level) => Math.ceil(1 + Math.max(1, Math.min(20, level|0)) / 4);
const fmtMod = (n) => (n >= 0 ? `+${n}` : `${n}`);
const safeJson = (s, fb) => { try { return JSON.parse(s || ''); } catch { return fb; } };

const CharacterSheetTab = ({ ch, editable, onPatch, onRefresh }) => {
  // Local optimistic state — backend is the source of truth, but inputs feel
  // jittery if we wait for the round-trip before showing the new value.
  const [draft, setDraft] = React.useState(ch);
  React.useEffect(() => { setDraft(ch); }, [ch.id, ch.level, ch.xp, ch.strength, ch.dexterity, ch.constitution, ch.intelligence, ch.wisdom, ch.charisma, ch.armorClass, ch.speed, ch.hpMax, ch.hpCurrent, ch.hpTemp, ch.hitDieType, ch.hitDiceUsed, ch.deathSaveSuccesses, ch.deathSaveFailures, ch.exhaustionLevel, ch.inspiration, ch.background, ch.alignment, ch.saveProficienciesJson, ch.skillProficienciesJson]);

  // Push a single-field patch through to the parent (which calls the API).
  // We rebuild `draft` immediately for snappy UI; persistence is best-effort.
  const setField = (field, value) => {
    setDraft(d => ({...d, [field]: value}));
    onPatch({ [field]: value });
  };
  const setJsonField = (field, obj) => {
    const json = JSON.stringify(obj);
    setDraft(d => ({...d, [field]: json}));
    onPatch({ [field]: json });
  };

  const saveProfs = safeJson(draft.saveProficienciesJson, []);  // ['str','con',...]
  const skillProfs = safeJson(draft.skillProficienciesJson, {}); // { perception:'expert', ... }
  const languages  = safeJson(draft.languagesJson, []);          // ['Common', 'Elvish', ...]
  const features   = safeJson(draft.featuresJson, []);           // [{ name, source, description }]
  const backstory  = safeJson(draft.backstoryJson, {});          // { appearance, personality, ... }
  const conditions = safeJson(draft.conditionsJson, []);         // ['poisoned','prone', ...]
  const proficiencies = safeJson(draft.proficienciesJson, {});   // { weapons:[], armor:[], tools:[] }

  const profBonus = profBonusFor(draft.level);
  const initiative = abilityMod(draft.dexterity);

  // Saves: ability mod + (proficient ? profBonus : 0). No half-prof (Jack of
  // All Trades) for now — Bard rule lands in a follow-up.
  const saveTotal = (abilKey) => {
    const score = draft[ABILITIES.find(a => a.key === abilKey).col];
    return abilityMod(score) + (saveProfs.includes(abilKey) ? profBonus : 0);
  };
  const skillTotal = (skill) => {
    const score = draft[ABILITIES.find(a => a.key === skill.ability).col];
    const lvl = skillProfs[skill.key];
    const mult = lvl === 'expert' ? 2 : lvl === 'proficient' ? 1 : 0;
    return abilityMod(score) + mult * profBonus;
  };

  const toggleSaveProf = (abilKey) => {
    const next = saveProfs.includes(abilKey)
      ? saveProfs.filter(k => k !== abilKey)
      : [...saveProfs, abilKey];
    setJsonField('saveProficienciesJson', next);
  };
  const cycleSkillProf = (skillKey) => {
    const cur = skillProfs[skillKey];
    const next = cur === 'proficient' ? 'expert' : cur === 'expert' ? undefined : 'proficient';
    const out = { ...skillProfs };
    if (next === undefined) delete out[skillKey]; else out[skillKey] = next;
    setJsonField('skillProficienciesJson', out);
  };

  return (
    <div style={shStyles.page}>
      {/* IDENTITY */}
      <section style={shStyles.card}>
        <SectionTitle>Identity</SectionTitle>
        <div style={shStyles.identityGrid}>
          <Field label="Background" value={draft.background} editable={editable} onChange={v => setField('background', v)} placeholder="e.g. Soldier" />
          <Field label="Alignment"  value={draft.alignment}  editable={editable} onChange={v => setField('alignment', v)}  placeholder="e.g. Chaotic Good" />
          <NumberField label="Level" value={draft.level} min={1} max={20} editable={editable} onChange={v => setField('level', v)} />
          <NumberField label="XP"    value={draft.xp}    min={0}           editable={editable} onChange={v => setField('xp', v)} />
          <ReadOnly label="Proficiency bonus" value={fmtMod(profBonus)} />
        </div>
      </section>

      {/* ABILITY SCORES */}
      <section style={shStyles.card}>
        <SectionTitle>Ability Scores</SectionTitle>
        <div style={shStyles.abilityRow}>
          {ABILITIES.map(a => (
            <div key={a.key} style={shStyles.abilityBox}>
              <div style={shStyles.abilityLabel}>{a.long}</div>
              <div style={shStyles.abilityModBig}>{fmtMod(abilityMod(draft[a.col]))}</div>
              <input type="number" min={1} max={30}
                disabled={!editable}
                value={draft[a.col]}
                onChange={e => setField(a.col, Math.max(1, Math.min(30, parseInt(e.target.value)||0)))}
                style={shStyles.abilityScoreInput} />
            </div>
          ))}
        </div>
      </section>

      {/* COMBAT */}
      <section style={shStyles.card}>
        <SectionTitle>Combat</SectionTitle>
        <div style={shStyles.combatGrid}>
          <NumberField big label="AC"        value={draft.armorClass} editable={editable} onChange={v => setField('armorClass', v)} />
          <ReadOnly    big label="Initiative" value={fmtMod(initiative)} />
          <NumberField big label="Speed (ft)" value={draft.speed}     editable={editable} onChange={v => setField('speed', v)} />
        </div>
        <div style={{...shStyles.combatGrid, marginTop:14}}>
          <NumberField label="HP Max"     value={draft.hpMax}     editable={editable} onChange={v => setField('hpMax', v)} />
          <NumberField label="HP Current" value={draft.hpCurrent} editable={editable} onChange={v => setField('hpCurrent', v)} />
          <NumberField label="HP Temp"    value={draft.hpTemp}    editable={editable} onChange={v => setField('hpTemp', v)} />
        </div>
        <div style={{...shStyles.combatGrid, marginTop:14}}>
          <Field label="Hit die" value={draft.hitDieType}
            editable={editable}
            onChange={v => setField('hitDieType', v)}
            select={['d6','d8','d10','d12']} />
          <NumberField label="Hit dice used" value={draft.hitDiceUsed} min={0} max={draft.level} editable={editable} onChange={v => setField('hitDiceUsed', v)} />
          <NumberField label="Exhaustion (0-10)" value={draft.exhaustionLevel} min={0} max={10} editable={editable} onChange={v => setField('exhaustionLevel', v)} />
        </div>
        <div style={{...shStyles.combatGrid, marginTop:14, gridTemplateColumns:'1fr 1fr 1fr'}}>
          <DeathSaveTrack label="Successes" value={draft.deathSaveSuccesses} editable={editable} onChange={v => setField('deathSaveSuccesses', v)} color="#22c55e" />
          <DeathSaveTrack label="Failures"  value={draft.deathSaveFailures}  editable={editable} onChange={v => setField('deathSaveFailures', v)}  color="#f87171" />
          <label style={{ display:'flex', alignItems:'center', gap:8, cursor: editable ? 'pointer' : 'default' }}>
            <input type="checkbox" checked={!!draft.inspiration} disabled={!editable}
              onChange={e => setField('inspiration', e.target.checked)} />
            <span style={{ fontSize:13, color:'#e8e6e3', fontWeight:700 }}>Inspiration</span>
          </label>
        </div>
      </section>

      {/* WEALTH — full 5e currency set + computed total in gp. Each field is
          editable inline; total recomputes from the local draft. */}
      <section style={shStyles.card}>
        <SectionTitle>Wealth</SectionTitle>
        <div style={shStyles.coinRow}>
          {[
            ['platinumPieces','PP','#e5e4e2','Platinum'],
            ['gold',          'GP','#c9a227','Gold'],
            ['electrumPieces','EP','#cdb88a','Electrum'],
            ['silver',        'SP','#9a9793','Silver'],
            ['copper',        'CP','#b45309','Copper'],
          ].map(([col, abbr, color, long]) => (
            <div key={col} style={{...shStyles.coinTile, borderLeftColor: color}}>
              <div style={{ fontSize:10, color, textTransform:'uppercase', letterSpacing:'0.12em', fontWeight:700 }}>{abbr}</div>
              <input type="number" min={0} disabled={!editable}
                value={draft[col] ?? 0}
                onChange={e => setField(col, Math.max(0, parseInt(e.target.value)||0))}
                style={{ ...shStyles.coinInput, color }} />
              <div style={{ fontSize:10, color:'#6b6966', marginTop:2 }}>{long}</div>
            </div>
          ))}
        </div>
        <div style={{ marginTop:12, padding:'8px 12px', background:'rgba(201,162,39,0.07)', borderRadius:6, fontSize:12, color:'#9a9793', textAlign:'right' }}>
          Total value: <strong style={{color:'#c9a227'}}>
            {/* 5e: 1pp = 10gp, 1ep = 0.5gp, 1sp = 0.1gp, 1cp = 0.01gp */}
            {(
              (draft.platinumPieces||0)*10 +
              (draft.gold||0) +
              (draft.electrumPieces||0)*0.5 +
              (draft.silver||0)*0.1 +
              (draft.copper||0)*0.01
            ).toFixed(2)} gp
          </strong>
        </div>
      </section>

      {/* MATERIALS — carved materials (per-character stockpile). Has its own
          add/delete flow because materials are sub-rows, not flat fields. */}
      <MaterialsPanel ch={ch} editable={editable} onRefresh={onRefresh} />

      {/* SAVES + SKILLS — two columns on wide screens */}
      <div style={shStyles.twoCol}>
        {/* Saving throws */}
        <section style={shStyles.card}>
          <SectionTitle>Saving Throws</SectionTitle>
          <div style={shStyles.list}>
            {ABILITIES.map(a => {
              const prof = saveProfs.includes(a.key);
              return (
                <div key={a.key} style={shStyles.row}>
                  <button onClick={() => editable && toggleSaveProf(a.key)}
                    disabled={!editable}
                    title={prof ? 'Proficient — click to remove' : 'Click to mark proficient'}
                    style={{ ...shStyles.profDot, background: prof ? '#c9a227' : 'transparent', borderColor: prof ? '#c9a227' : '#3a3a42', cursor: editable ? 'pointer' : 'default' }} />
                  <span style={shStyles.rowLabel}>{a.long}</span>
                  <span style={shStyles.rowTotal}>{fmtMod(saveTotal(a.key))}</span>
                </div>
              );
            })}
          </div>
        </section>

        {/* Skills */}
        <section style={shStyles.card}>
          <SectionTitle>Skills</SectionTitle>
          <div style={shStyles.list}>
            {SKILLS.map(s => {
              const lvl = skillProfs[s.key]; // undefined | 'proficient' | 'expert'
              const dot = lvl === 'expert' ? '◉' : lvl === 'proficient' ? '●' : '○';
              const dotColor = lvl === 'expert' ? '#c9a227' : lvl === 'proficient' ? '#c9a227' : '#4a4a52';
              return (
                <div key={s.key} style={shStyles.row}>
                  <button onClick={() => editable && cycleSkillProf(s.key)}
                    disabled={!editable}
                    title="Click to cycle: none → proficient → expert → none"
                    style={{ ...shStyles.profCycle, color: dotColor, cursor: editable ? 'pointer' : 'default' }}>
                    {dot}
                  </button>
                  <span style={shStyles.rowLabel}>{s.label}
                    <span style={{ color:'#6b6966', fontSize:10, marginLeft:6, textTransform:'uppercase', letterSpacing:'0.08em' }}>({s.ability})</span>
                  </span>
                  <span style={shStyles.rowTotal}>{fmtMod(skillTotal(s))}</span>
                </div>
              );
            })}
          </div>
          <div style={{ marginTop:10, paddingTop:10, borderTop:'1px solid #2a2a32', display:'flex', justifyContent:'space-between', fontSize:13 }}>
            <span style={{ color:'#9a9793' }}>Passive Perception</span>
            <span style={{ color:'#e8e6e3', fontWeight:700 }}>{10 + skillTotal(SKILLS.find(s => s.key === 'perception'))}</span>
          </div>
        </section>
      </div>

      {/* CONDITIONS — togglable PHB-2024 status conditions. Exhaustion has its
          own scale (0-10) and lives in the Combat panel above. */}
      <ConditionsPanel
        conditions={conditions}
        editable={editable}
        onChange={next => setJsonField('conditionsJson', next)} />

      {/* LANGUAGES — chip list with add/remove. Pre-fills a known list of 5e
          languages but accepts free-form additions for homebrew / regional. */}
      <LanguagesPanel
        languages={languages}
        editable={editable}
        onChange={next => setJsonField('languagesJson', next)} />

      {/* PROFICIENCIES — weapons / armor / tools, each a free-form chip list.
          5e proficiencies aren't strictly enum-bounded, so we don't try to
          dropdown them — just typeable chips. */}
      <ProficienciesPanel
        proficiencies={proficiencies}
        editable={editable}
        onChange={next => setJsonField('proficienciesJson', next)} />

      {/* FEATURES & TRAITS — card list. Each card has name + source + description,
          all editable inline. New entries open a draft card at the top. */}
      <FeaturesPanel
        features={features}
        editable={editable}
        onChange={next => setJsonField('featuresJson', next)} />

      {/* BACKSTORY — the six standard PHB narrative fields. Each is a free-form
          textarea. Stored as a single JSON object so the schema stays flat. */}
      <BackstoryPanel
        backstory={backstory}
        editable={editable}
        onChange={next => setJsonField('backstoryJson', next)} />
    </div>
  );
};

// ── MaterialsPanel — carved-material stockpile, inlined into the sheet ────
// Lives in its own component because materials have their own add/delete
// lifecycle (separate sub-rows, not flat character fields). After mutating,
// we ask the parent to reload the character so the rest of the sheet sees
// the new state — the alternative would be local state that drifts from the
// parent's ch prop, which led to bugs in the prior tab implementation.
const MaterialsPanel = ({ ch, editable, onRefresh }) => {
  const [adding, setAdding] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const blank = { name:'', quantity:1, unit:'', notes:'' };
  const [form, setForm] = React.useState(blank);

  const addMaterial = async () => {
    if (!form.name.trim() || busy) return;
    setBusy(true);
    try {
      await api.characters.materials.add(ch.id, {
        name: form.name.trim(),
        quantity: Number(form.quantity) || 1,
        unit: form.unit, notes: form.notes,
      });
      setForm(blank);
      setAdding(false);
      await onRefresh?.();
    } catch (err) {
      console.error('[MaterialsPanel] add failed:', err);
      window.dialog.alert('Failed to add material: ' + (err.message || 'unknown error'));
    } finally { setBusy(false); }
  };

  const deleteMaterial = async (matId) => {
    if (!(await window.dialog.confirm({ title:'Remove material?', message:'Remove this material?', danger:true, confirmLabel:'Remove' }))) return;
    try {
      await api.characters.materials.delete(ch.id, matId);
      await onRefresh?.();
    } catch (err) {
      console.error('[MaterialsPanel] delete failed:', err);
      window.dialog.alert('Failed to remove material: ' + (err.message || 'unknown error'));
    }
  };

  const list = ch.carvedMaterials || [];
  return (
    <section style={shStyles.card}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:12 }}>
        <SectionTitle>Carved Materials ({list.length})</SectionTitle>
        {editable && !adding && (
          <button style={shStyles.addBtn} onClick={() => setAdding(true)}>+ Add Material</button>
        )}
      </div>

      {adding && (
        <div style={{ background:'#0e0e12', border:'1px solid #c9a22744', borderRadius:8, padding:14, marginBottom:12 }}>
          <div style={{ display:'grid', gridTemplateColumns:'2fr 80px 1fr', gap:10, marginBottom:8 }}>
            <div>
              <div style={shStyles.fieldLabel}>Name<Req/></div>
              <input style={shStyles.fieldInput} value={form.name}
                onChange={e => setForm(p => ({...p, name:e.target.value}))}
                placeholder="e.g. Moonstone Dust" autoFocus />
            </div>
            <div>
              <div style={shStyles.fieldLabel}>Qty</div>
              <input type="number" min={1} style={shStyles.fieldInput}
                value={form.quantity}
                onChange={e => setForm(p => ({...p, quantity:e.target.value}))} />
            </div>
            <div>
              <div style={shStyles.fieldLabel}>Unit</div>
              <input style={shStyles.fieldInput} value={form.unit}
                onChange={e => setForm(p => ({...p, unit:e.target.value}))}
                placeholder="vials, shards, …" />
            </div>
          </div>
          <div>
            <div style={shStyles.fieldLabel}>Notes</div>
            <input style={shStyles.fieldInput} value={form.notes}
              onChange={e => setForm(p => ({...p, notes:e.target.value}))}
              placeholder="Where it came from, how it was carved…" />
          </div>
          <div style={{ display:'flex', gap:8, marginTop:10 }}>
            <button style={shStyles.addBtn} onClick={addMaterial} disabled={!form.name.trim() || busy}>{busy ? 'Saving…' : 'Add'}</button>
            <button style={shStyles.addBtnGhost} onClick={() => { setAdding(false); setForm(blank); }} disabled={busy}>Cancel</button>
          </div>
        </div>
      )}

      {list.length === 0 ? (
        <div style={{ color:'#6b6966', fontSize:13, textAlign:'center', padding:'20px 0' }}>No carved materials recorded.</div>
      ) : (
        <div style={{ display:'flex', flexDirection:'column', gap:8 }}>
          {list.map(m => (
            <div key={m.id} style={shStyles.matRow}>
              <div style={shStyles.matIcon}>◆</div>
              <div style={{ flex:1, minWidth:0 }}>
                <div style={{ fontSize:14, fontWeight:700, color:'#e8e6e3' }}>{m.name}</div>
                {m.notes && <div style={{ fontSize:12, color:'#9a9793', marginTop:2, fontStyle:'italic' }}>{m.notes}</div>}
              </div>
              <div style={{ textAlign:'right', flexShrink:0, marginRight:editable ? 8 : 0 }}>
                <div style={{ fontSize:14, fontWeight:700, color:'#c9a227' }}>{m.quantity}</div>
                <div style={{ fontSize:11, color:'#6b6966' }}>{m.unit}</div>
              </div>
              {editable && (
                <button title="Remove" onClick={() => deleteMaterial(m.id)}
                  style={{ background:'transparent', border:'1px solid rgba(248,113,113,0.2)', color:'#f87171', borderRadius:5, padding:'4px 8px', cursor:'pointer', fontSize:11, fontFamily:"'Nunito',sans-serif" }}>✕</button>
              )}
            </div>
          ))}
        </div>
      )}
    </section>
  );
};

// ── ConditionsPanel — the 14 PHB 2024 status conditions. Active conditions
// are highlighted; clicking toggles on/off. Hover shows the rules summary so
// players don't have to flip a book mid-fight. Exhaustion is its own scale
// (level 0-10) and lives in the Combat panel — we exclude it here.
const PHB_CONDITIONS = [
  { key:'blinded',      desc:"Can't see; attack rolls have disadvantage and attacks against you have advantage." },
  { key:'charmed',      desc:"Can't attack or harm the charmer; they have advantage on social checks against you." },
  { key:'deafened',     desc:"Can't hear; auto-fail any check that requires hearing." },
  { key:'frightened',   desc:"Disadvantage on checks & attacks while the source is in sight; can't move closer to it." },
  { key:'grappled',     desc:"Speed becomes 0; ends if the grappler is incapacitated or you’re moved out of range." },
  { key:'incapacitated',desc:"Can't take actions, bonus actions, or reactions. Concentration breaks." },
  { key:'invisible',    desc:"Can't be seen without magic or special sense; attacks against you have disadvantage, your attacks have advantage." },
  { key:'paralyzed',    desc:"Incapacitated, can't move/speak; auto-fail STR and DEX saves; melee crits within 5 ft." },
  { key:'petrified',    desc:"Turned to stone — incapacitated, weight x10, resist all damage, immune to poison/disease." },
  { key:'poisoned',     desc:"Disadvantage on attack rolls and ability checks." },
  { key:'prone',        desc:"Movement halved (must crawl); disadvantage on attacks; melee attacks against you have advantage." },
  { key:'restrained',   desc:"Speed 0; disadvantage on attacks & DEX saves; attacks against you have advantage." },
  { key:'stunned',      desc:"Incapacitated, can't move, can speak falteringly; auto-fail STR and DEX saves." },
  { key:'unconscious',  desc:"Incapacitated, prone, drops what they're holding; melee crits within 5 ft; auto-fail STR/DEX saves." },
];
const ConditionsPanel = ({ conditions, editable, onChange }) => {
  // Stored lowercase so toggling stays canonical regardless of case the DM
  // typed elsewhere. We compare lowercased keys.
  const active = new Set((conditions || []).map(c => (c || '').toLowerCase()));
  const toggle = (key) => {
    const next = active.has(key)
      ? conditions.filter(c => (c || '').toLowerCase() !== key)
      : [...conditions, key];
    onChange(next);
  };
  return (
    <section style={shStyles.card}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:12 }}>
        <SectionTitle>Conditions</SectionTitle>
        {active.size > 0 && (
          <span style={{ fontSize:11, color:'#f87171', fontWeight:700 }}>
            {active.size} active
          </span>
        )}
      </div>
      <div style={{ display:'flex', flexWrap:'wrap', gap:6 }}>
        {PHB_CONDITIONS.map(c => {
          const on = active.has(c.key);
          return (
            <button key={c.key} type="button"
              disabled={!editable}
              onClick={() => editable && toggle(c.key)}
              title={c.desc}
              style={{
                background: on ? 'rgba(248,113,113,0.18)' : 'transparent',
                border: '1px solid ' + (on ? '#f87171' : '#3a3a42'),
                color: on ? '#f87171' : '#9a9793',
                borderRadius: 20, padding: '4px 12px',
                fontSize: 12, fontWeight: on ? 700 : 500,
                textTransform: 'capitalize',
                cursor: editable ? 'pointer' : 'default',
                fontFamily: "'Nunito',sans-serif",
              }}>
              {c.key}
            </button>
          );
        })}
      </div>
      <div style={{ fontSize:11, color:'#6b6966', marginTop:8 }}>
        Hover a chip for the rules summary. Exhaustion is tracked separately in the Combat panel.
      </div>
    </section>
  );
};

// ── ProficienciesPanel — three free-form chip strips (weapons / armor / tools).
// No fixed dropdown set because 5e proficiencies are open-ended (a class can
// grant "all simple weapons", a feat grants a specific weapon, etc.). Type a
// label, press Enter, get a chip.
const ProficienciesPanel = ({ proficiencies, editable, onChange }) => {
  const get = (key) => Array.isArray(proficiencies[key]) ? proficiencies[key] : [];
  const set = (key, next) => onChange({ ...proficiencies, [key]: next });

  return (
    <section style={shStyles.card}>
      <SectionTitle>Proficiencies</SectionTitle>
      <div style={{ display:'flex', flexDirection:'column', gap:14 }}>
        <ProficiencyStrip
          label="Weapons" placeholder="e.g. Simple weapons, Martial weapons, Longsword"
          items={get('weapons')}
          editable={editable}
          onChange={next => set('weapons', next)} />
        <ProficiencyStrip
          label="Armor" placeholder="e.g. Light, Medium, Shields"
          items={get('armor')}
          editable={editable}
          onChange={next => set('armor', next)} />
        <ProficiencyStrip
          label="Tools" placeholder="e.g. Thieves’ tools, Smith’s tools, Vehicles (land)"
          items={get('tools')}
          editable={editable}
          onChange={next => set('tools', next)} />
      </div>
    </section>
  );
};
const ProficiencyStrip = ({ label, items, editable, onChange, placeholder }) => {
  const [draft, setDraft] = React.useState('');
  const add = () => {
    const v = draft.trim();
    if (!v) return;
    if (items.some(x => x.toLowerCase() === v.toLowerCase())) { setDraft(''); return; }
    onChange([...items, v]);
    setDraft('');
  };
  const remove = (x) => onChange(items.filter(i => i !== x));
  return (
    <div>
      <div style={shStyles.fieldLabel}>{label}</div>
      <div style={{ display:'flex', flexWrap:'wrap', gap:6, alignItems:'center' }}>
        {items.length === 0 && !editable && (
          <span style={{ fontSize:12, color:'#6b6966', fontStyle:'italic' }}>None</span>
        )}
        {items.map(x => (
          <div key={x} style={shStyles.langChip}>
            <span>{x}</span>
            {editable && (
              <button onClick={() => remove(x)} title="Remove"
                style={{ background:'transparent', border:'none', color:'#6b6966', fontSize:12, cursor:'pointer', padding:'0 2px 0 6px' }}>✕</button>
            )}
          </div>
        ))}
        {editable && (
          <div style={{ display:'inline-flex', gap:4 }}>
            <input value={draft} onChange={e => setDraft(e.target.value)}
              onKeyDown={e => e.key === 'Enter' && (e.preventDefault(), add())}
              placeholder={placeholder}
              style={{ ...shStyles.fieldInput, width:280, padding:'4px 8px', fontSize:12 }} />
            <button style={shStyles.addBtnGhost} onClick={add} disabled={!draft.trim()}>+ Add</button>
          </div>
        )}
      </div>
    </div>
  );
};

// ── LanguagesPanel — chip list. PHB-known languages in the picker plus an
// "Other…" option that prompts for a custom string (homebrew dialects etc).
const PHB_LANGUAGES = [
  'Common', 'Common Sign Language',
  'Draconic', 'Dwarvish', 'Elvish', 'Giant', 'Gnomish', 'Goblin', 'Halfling', 'Orc',
  'Abyssal', 'Celestial', 'Deep Speech', 'Infernal', 'Primordial', 'Sylvan', 'Undercommon',
  'Thieves’ Cant', 'Druidic',
];
const LanguagesPanel = ({ languages, editable, onChange }) => {
  const [adding, setAdding] = React.useState(false);
  const [pick, setPick] = React.useState('');
  const [custom, setCustom] = React.useState('');

  const remove = (lang) => onChange(languages.filter(l => l !== lang));
  const add = (val) => {
    const trimmed = (val || '').trim();
    if (!trimmed) return;
    if (languages.some(l => l.toLowerCase() === trimmed.toLowerCase())) {
      setAdding(false); setPick(''); setCustom('');
      return;
    }
    onChange([...languages, trimmed]);
    setAdding(false); setPick(''); setCustom('');
  };
  const onPickerChange = (val) => {
    if (val === '__custom') { setPick('__custom'); return; }
    if (val) add(val);
  };

  // Pre-filter known languages so the picker doesn't suggest ones already taken.
  const available = PHB_LANGUAGES.filter(l => !languages.includes(l));

  return (
    <section style={shStyles.card}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:12 }}>
        <SectionTitle>Languages</SectionTitle>
        {editable && !adding && (
          <button style={shStyles.addBtn} onClick={() => setAdding(true)}>+ Add Language</button>
        )}
      </div>

      {languages.length === 0 && !adding && (
        <div style={{ color:'#6b6966', fontSize:13, padding:'4px 0' }}>No languages recorded.</div>
      )}

      <div style={{ display:'flex', flexWrap:'wrap', gap:6 }}>
        {languages.map(l => (
          <div key={l} style={shStyles.langChip}>
            <span>{l}</span>
            {editable && (
              <button onClick={() => remove(l)} title="Remove"
                style={{ background:'transparent', border:'none', color:'#6b6966', fontSize:12, cursor:'pointer', padding:'0 2px 0 6px' }}>✕</button>
            )}
          </div>
        ))}
      </div>

      {adding && (
        <div style={{ marginTop:10, display:'flex', gap:6, alignItems:'center', flexWrap:'wrap' }}>
          {pick !== '__custom' ? (
            <>
              <select autoFocus value="" onChange={e => onPickerChange(e.target.value)} style={shStyles.fieldInput}>
                <option value="">— Pick a language —</option>
                {available.map(l => <option key={l} value={l}>{l}</option>)}
                <option value="__custom">Other…</option>
              </select>
              <button style={shStyles.addBtnGhost} onClick={() => { setAdding(false); setPick(''); }}>Cancel</button>
            </>
          ) : (
            <>
              <input autoFocus value={custom} onChange={e => setCustom(e.target.value)}
                onKeyDown={e => e.key === 'Enter' && add(custom)}
                placeholder="Custom language name" style={shStyles.fieldInput} />
              <button style={shStyles.addBtn} onClick={() => add(custom)} disabled={!custom.trim()}>Add</button>
              <button style={shStyles.addBtnGhost} onClick={() => { setAdding(false); setPick(''); setCustom(''); }}>Cancel</button>
            </>
          )}
        </div>
      )}
    </section>
  );
};

// ── FeaturesPanel — card list of class features, racial traits, background
// boons, magic-item attunements, anything the player wants to remember on
// their sheet. Each row is { name, source, description }; description is
// long-form (multi-line textarea).
const FeaturesPanel = ({ features, editable, onChange }) => {
  // Adding a feature opens an inline draft at the top. Existing rows toggle
  // their own edit mode on click; we don't open a modal.
  const [adding, setAdding] = React.useState(false);
  const blank = { name:'', source:'', description:'' };
  const [draft, setDraft] = React.useState(blank);
  const [editIndex, setEditIndex] = React.useState(null);
  const [editBuf, setEditBuf] = React.useState(blank);

  const commitNew = () => {
    if (!draft.name.trim()) return;
    onChange([...features, { ...draft, name: draft.name.trim() }]);
    setDraft(blank);
    setAdding(false);
  };
  const startEdit = (i) => { setEditIndex(i); setEditBuf({ ...features[i] }); };
  const commitEdit = () => {
    if (editIndex == null) return;
    const next = features.map((f, i) => i === editIndex ? { ...editBuf, name: (editBuf.name || '').trim() || f.name } : f);
    onChange(next);
    setEditIndex(null);
  };
  const remove = async (i) => {
    const f = features[i];
    if (!(await window.dialog.confirm({ title:'Remove feature?', message:`Remove "${f.name}" from this sheet?`, danger:true, confirmLabel:'Remove' }))) return;
    onChange(features.filter((_, j) => j !== i));
    if (editIndex === i) setEditIndex(null);
  };

  return (
    <section style={shStyles.card}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:12 }}>
        <SectionTitle>Features &amp; Traits ({features.length})</SectionTitle>
        {editable && !adding && (
          <button style={shStyles.addBtn} onClick={() => setAdding(true)}>+ Add Feature</button>
        )}
      </div>

      {adding && (
        <div style={shStyles.featureDraft}>
          <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:10, marginBottom:8 }}>
            <div>
              <div style={shStyles.fieldLabel}>Name<Req/></div>
              <input autoFocus style={shStyles.fieldInput} value={draft.name}
                onChange={e => setDraft(d => ({...d, name: e.target.value}))}
                placeholder="e.g. Second Wind" />
            </div>
            <div>
              <div style={shStyles.fieldLabel}>Source</div>
              <input style={shStyles.fieldInput} value={draft.source}
                onChange={e => setDraft(d => ({...d, source: e.target.value}))}
                placeholder="e.g. Fighter 1, Folk Hero, Belt of Hill Giant Strength" />
            </div>
          </div>
          <div>
            <div style={shStyles.fieldLabel}>Description</div>
            <textarea rows={3} style={{...shStyles.fieldInput, minHeight:72, resize:'vertical'}} value={draft.description}
              onChange={e => setDraft(d => ({...d, description: e.target.value}))}
              placeholder="Mechanical effect and any narrative notes." />
          </div>
          <div style={{ display:'flex', gap:8, marginTop:10 }}>
            <button style={shStyles.addBtn} onClick={commitNew} disabled={!draft.name.trim()}>Add</button>
            <button style={shStyles.addBtnGhost} onClick={() => { setAdding(false); setDraft(blank); }}>Cancel</button>
          </div>
        </div>
      )}

      {features.length === 0 && !adding ? (
        <div style={{ color:'#6b6966', fontSize:13, textAlign:'center', padding:'20px 0' }}>
          No features yet. Add class features, racial traits, background boons, attunements, etc.
        </div>
      ) : (
        <div style={{ display:'flex', flexDirection:'column', gap:8 }}>
          {features.map((f, i) => editIndex === i ? (
            <div key={i} style={shStyles.featureDraft}>
              <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:10, marginBottom:8 }}>
                <div>
                  <div style={shStyles.fieldLabel}>Name<Req/></div>
                  <input style={shStyles.fieldInput} value={editBuf.name}
                    onChange={e => setEditBuf(d => ({...d, name: e.target.value}))} />
                </div>
                <div>
                  <div style={shStyles.fieldLabel}>Source</div>
                  <input style={shStyles.fieldInput} value={editBuf.source}
                    onChange={e => setEditBuf(d => ({...d, source: e.target.value}))} />
                </div>
              </div>
              <div>
                <div style={shStyles.fieldLabel}>Description</div>
                <textarea rows={3} style={{...shStyles.fieldInput, minHeight:72, resize:'vertical'}} value={editBuf.description}
                  onChange={e => setEditBuf(d => ({...d, description: e.target.value}))} />
              </div>
              <div style={{ display:'flex', gap:8, marginTop:10, justifyContent:'space-between' }}>
                <button style={{ ...shStyles.addBtnGhost, color:'#f87171', borderColor:'rgba(248,113,113,0.25)' }} onClick={() => remove(i)}>Remove</button>
                <div style={{ display:'flex', gap:8 }}>
                  <button style={shStyles.addBtnGhost} onClick={() => setEditIndex(null)}>Cancel</button>
                  <button style={shStyles.addBtn} onClick={commitEdit}>Save</button>
                </div>
              </div>
            </div>
          ) : (
            <div key={i} style={shStyles.featureCard}>
              <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', gap:8 }}>
                <div style={{ flex:1, minWidth:0 }}>
                  <div style={{ fontSize:14, fontWeight:800, color:'#e8e6e3', fontFamily:"'Cinzel',serif" }}>{f.name}</div>
                  {f.source && <div style={{ fontSize:11, color:'#c9a227', marginTop:2 }}>{f.source}</div>}
                </div>
                {editable && (
                  <button style={shStyles.editLink} onClick={() => startEdit(i)}>✎ Edit</button>
                )}
              </div>
              {f.description && (
                <div style={{ fontSize:13, color:'#c5c3c0', lineHeight:1.6, marginTop:6, whiteSpace:'pre-wrap' }}>{f.description}</div>
              )}
            </div>
          ))}
        </div>
      )}
    </section>
  );
};

// ── BackstoryPanel — the six narrative fields. Each is a free-form textarea
// stored as a string key on the backstory JSON blob. We render them in a
// fixed order matching the PHB.
const BACKSTORY_FIELDS = [
  { key:'appearance',  label:'Appearance',          placeholder:'Height, build, distinguishing features, typical attire.' },
  { key:'personality', label:'Personality Traits',  placeholder:'Two traits that summarise how the character behaves.' },
  { key:'ideals',      label:'Ideals',              placeholder:'What drives them — honor, freedom, knowledge, power, faith…' },
  { key:'bonds',       label:'Bonds',               placeholder:'People, places, or things tying them to the world.' },
  { key:'flaws',       label:'Flaws',               placeholder:'The vice, fear, or weakness that gets them in trouble.' },
  { key:'backstory',   label:'Backstory',           placeholder:'The longer narrative — where they’re from, what they’ve done.' },
];
const BackstoryPanel = ({ backstory, editable, onChange }) => {
  const set = (key, val) => onChange({ ...backstory, [key]: val });
  return (
    <section style={shStyles.card}>
      <SectionTitle>Backstory</SectionTitle>
      <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:14 }}>
        {BACKSTORY_FIELDS.map(f => {
          const isWide = f.key === 'backstory'; // long-form spans both columns
          return (
            <div key={f.key} style={isWide ? { gridColumn:'1/-1' } : {}}>
              <div style={shStyles.fieldLabel}>{f.label}</div>
              <textarea
                rows={isWide ? 6 : 3}
                disabled={!editable}
                value={backstory[f.key] || ''}
                onChange={e => set(f.key, e.target.value)}
                placeholder={f.placeholder}
                style={{ ...shStyles.fieldInput, minHeight: isWide ? 120 : 64, resize:'vertical', fontFamily:"'Nunito',sans-serif" }} />
            </div>
          );
        })}
      </div>
    </section>
  );
};

// ── Tiny presentational helpers ────────────────────────────────────────────
const SectionTitle = ({ children }) => (
  <div style={{ fontSize:11, fontWeight:800, color:'#6b6966', textTransform:'uppercase', letterSpacing:'0.12em', marginBottom:12 }}>{children}</div>
);
const Field = ({ label, value, onChange, editable, placeholder, select }) => (
  <div>
    <div style={shStyles.fieldLabel}>{label}</div>
    {select
      ? <select disabled={!editable} value={value || ''} onChange={e => onChange(e.target.value)} style={shStyles.fieldInput}>
          {select.map(opt => <option key={opt} value={opt}>{opt}</option>)}
        </select>
      : <input disabled={!editable} value={value || ''} onChange={e => onChange(e.target.value)} placeholder={placeholder} style={shStyles.fieldInput} />}
  </div>
);
const NumberField = ({ label, value, onChange, editable, min, max, big }) => (
  <div>
    <div style={shStyles.fieldLabel}>{label}</div>
    <input type="number" disabled={!editable}
      value={Number.isFinite(value) ? value : 0}
      min={min} max={max}
      onChange={e => {
        const v = parseInt(e.target.value) || 0;
        const clamped = (min !== undefined ? Math.max(min, v) : v);
        onChange(max !== undefined ? Math.min(max, clamped) : clamped);
      }}
      style={big ? shStyles.fieldInputBig : shStyles.fieldInput} />
  </div>
);
const ReadOnly = ({ label, value, big }) => (
  <div>
    <div style={shStyles.fieldLabel}>{label}</div>
    <div style={big ? shStyles.readOnlyBig : shStyles.readOnly}>{value}</div>
  </div>
);
// Three-dot tracker (clickable to set count). Used for death save tracks.
const DeathSaveTrack = ({ label, value, onChange, editable, color }) => (
  <div>
    <div style={shStyles.fieldLabel}>{label}</div>
    <div style={{ display:'flex', gap:6, marginTop:4 }}>
      {[1,2,3].map(n => (
        <button key={n} disabled={!editable}
          onClick={() => onChange(value === n ? n - 1 : n)}
          style={{ width:22, height:22, borderRadius:'50%',
            background: value >= n ? color : 'transparent',
            border: '2px solid ' + (value >= n ? color : '#3a3a42'),
            cursor: editable ? 'pointer' : 'default' }} />
      ))}
    </div>
  </div>
);

const shStyles = {
  page: { display:'flex', flexDirection:'column', gap:18 },
  card: { background:'#1c1c22', border:'1px solid #2a2a32', borderRadius:10, padding:'18px 20px' },
  identityGrid: { display:'grid', gridTemplateColumns:'repeat(auto-fit, minmax(160px, 1fr))', gap:14 },
  abilityRow: { display:'grid', gridTemplateColumns:'repeat(6, 1fr)', gap:10 },
  abilityBox: { background:'#16161b', border:'1px solid #2a2a32', borderRadius:10, padding:'12px 6px', textAlign:'center' },
  abilityLabel: { fontSize:10, color:'#6b6966', textTransform:'uppercase', letterSpacing:'0.12em', fontWeight:700 },
  abilityModBig: { fontFamily:"'Cinzel',serif", fontSize:28, fontWeight:900, color:'#c9a227', margin:'6px 0' },
  abilityScoreInput: { width:60, padding:'5px 6px', background:'#0e0e12', border:'1px solid #2a2a32', borderRadius:6, color:'#e8e6e3', fontSize:14, textAlign:'center', fontFamily:"'Nunito',sans-serif" },
  combatGrid: { display:'grid', gridTemplateColumns:'repeat(auto-fit, minmax(140px, 1fr))', gap:14 },
  twoCol: { display:'grid', gridTemplateColumns:'1fr 1fr', gap:18 },
  list: { display:'flex', flexDirection:'column', gap:4 },
  row: { display:'flex', alignItems:'center', gap:10, padding:'5px 0', borderBottom:'1px solid #1a1a20', fontSize:13 },
  rowLabel: { flex:1, color:'#c5c3c0' },
  rowTotal: { color:'#e8e6e3', fontWeight:700, fontFamily:"'Cinzel',serif", minWidth:36, textAlign:'right' },
  profDot: { width:14, height:14, borderRadius:'50%', border:'2px solid', flexShrink:0 },
  profCycle: { width:22, height:22, fontSize:14, lineHeight:1, background:'transparent', border:'none', padding:0 },
  fieldLabel: { fontSize:10, color:'#6b6966', textTransform:'uppercase', letterSpacing:'0.1em', fontWeight:700, marginBottom:4 },
  fieldInput: { width:'100%', padding:'7px 10px', background:'#16161b', border:'1px solid #2a2a32', borderRadius:6, color:'#e8e6e3', fontSize:13, fontFamily:"'Nunito',sans-serif", boxSizing:'border-box' },
  fieldInputBig: { width:'100%', padding:'10px 12px', background:'#16161b', border:'1px solid #2a2a32', borderRadius:6, color:'#e8e6e3', fontSize:22, fontFamily:"'Cinzel',serif", fontWeight:800, textAlign:'center', boxSizing:'border-box' },
  readOnly: { padding:'7px 10px', background:'#0e0e12', border:'1px dashed #2a2a32', borderRadius:6, color:'#c9a227', fontSize:13, fontFamily:"'Cinzel',serif", fontWeight:700 },
  readOnlyBig: { padding:'10px 12px', background:'#0e0e12', border:'1px dashed #2a2a32', borderRadius:6, color:'#c9a227', fontSize:22, fontFamily:"'Cinzel',serif", fontWeight:800, textAlign:'center' },
  // Wealth panel
  coinRow: { display:'grid', gridTemplateColumns:'repeat(5, 1fr)', gap:10 },
  coinTile: { background:'#16161b', border:'1px solid #2a2a32', borderLeft:'3px solid', borderRadius:8, padding:'10px 12px', display:'flex', flexDirection:'column', alignItems:'flex-start' },
  coinInput: { width:'100%', padding:'4px 8px', background:'#0e0e12', border:'1px solid #2a2a32', borderRadius:6, fontSize:18, fontFamily:"'Cinzel',serif", fontWeight:800, marginTop:6, boxSizing:'border-box' },
  // Materials panel
  matRow: { display:'flex', alignItems:'center', gap:10, background:'#16161b', border:'1px solid #2a2a32', borderRadius:8, padding:'9px 12px' },
  matIcon: { color:'#c9a227', fontSize:14, width:24, textAlign:'center', flexShrink:0 },
  addBtn: { background:'rgba(201,162,39,0.15)', border:'1px solid #c9a227', color:'#c9a227', borderRadius:6, padding:'5px 12px', fontSize:12, fontWeight:700, cursor:'pointer', fontFamily:"'Nunito',sans-serif" },
  addBtnGhost: { background:'transparent', border:'1px solid #3a3a42', color:'#9a9793', borderRadius:6, padding:'5px 12px', fontSize:12, fontWeight:700, cursor:'pointer', fontFamily:"'Nunito',sans-serif" },
  // Languages
  langChip: { display:'inline-flex', alignItems:'center', gap:4, padding:'4px 4px 4px 10px', background:'#16161b', border:'1px solid #2a2a32', borderRadius:20, fontSize:12, color:'#e8e6e3' },
  // Features
  featureCard: { background:'#16161b', border:'1px solid #2a2a32', borderRadius:8, padding:'10px 14px' },
  featureDraft: { background:'#0e0e12', border:'1px solid #c9a22744', borderRadius:8, padding:14, marginBottom:8 },
  editLink: { background:'transparent', border:'none', color:'#6b6966', fontSize:11, cursor:'pointer', padding:'2px 6px', fontFamily:"'Nunito',sans-serif" },
};

window.CharacterSheetTab = CharacterSheetTab;
