/* ============================================================
   Character Sheet — play view (Phase 11)

   One-screen reference for a finalized character during play.
   Clickable condition monitors write back to career damage state.
   Everything else is a read-only readout of computed values.
   ============================================================ */
const { useState, useEffect, useRef, useMemo, useCallback, Fragment } = React;

window.CharacterSheetView = function CharacterSheetView({ character, onChange }) {
    const C = SR5_CALC;
    const D = SR5_DATA;
    const inCareer = C.isCareerMode(character);
    const priorityMagic = C.priorityRow(character, 'magic');
    const magicType = priorityMagic?.type || 'mundane';
    const isAwakened = magicType !== 'mundane' && magicType !== 'technomancer';
    const isTechnomancer = magicType === 'technomancer';
    const mtype = D.METATYPES[character?.identity?.metatype];

    function onPrint() {
        /* Build a standalone, self-contained printable HTML doc.
           We deliberately do NOT clone the app's stylesheets — too many
           cascade rules ended up clipping the print output. Instead, we
           extract the character data directly and format it with tight
           inline print CSS. */
        const C = SR5_CALC;
        const D = SR5_DATA;
        const M = SR5_MAGIC;
        const Q = SR5_QUALITIES;

        const esc = s => String(s == null ? '' : s)
            .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');

        const mt = D.METATYPES[character?.identity?.metatype];
        const name = character?.identity?.name || 'Unnamed Runner';
        const alias = character?.identity?.streetName || character?.identity?.alias || '';
        const metatypeLabel = mt?.name || '—';
        const inCareerMode = C.isCareerMode(character);

        /* Attributes */
        const attrRows = (D.ATTRIBUTES || []).map(a => {
            const v = C.attributeValue(character, a.key);
            return `<td><strong>${esc(a.abbr || a.key.toUpperCase())}</strong> ${v}</td>`;
        }).join('');

        const specialAttrRows = ['edg', 'mag', 'res'].map(k => {
            const isAwakenedAttr = k === 'mag' || k === 'res';
            const magType = C.priorityRow(character, 'magic')?.type;
            const hide = (k === 'mag' && !['magician','adept','mystic_adept','aspected'].includes(magType))
                      || (k === 'res' && magType !== 'technomancer');
            if (hide) return '';
            const v = C.specialAttributeValue(character, k);
            const sa = (D.SPECIAL_ATTRIBUTES || []).find(s => s.key === k);
            return `<td><strong>${esc(sa?.abbr || k.toUpperCase())}</strong> ${v}</td>`;
        }).filter(Boolean).join('');

        /* Skills */
        const skillsList = Object.entries(character?.skills || {})
            .map(([k, r]) => ({ key: k, rating: r, tpl: D.ACTIVE_SKILLS.find(s => s.key === k) }))
            .filter(s => s.tpl)
            .sort((a, b) => a.tpl.label.localeCompare(b.tpl.label));
        const groupsList = Object.entries(character?.skillGroups || {})
            .map(([k, r]) => ({ key: k, rating: r, tpl: D.SKILL_GROUPS.find(g => g.key === k) }))
            .filter(s => s.tpl);
        const specs = character?.specializations || {};
        const skillsBody = [
            ...groupsList.map(g => `<tr><td>${esc(g.tpl.label)} <em>(group)</em></td><td class="num">${g.rating}</td><td></td></tr>`),
            ...skillsList.map(s => {
                const dice = C.skillDicePool(character, s.tpl);
                const spec = specs[s.key];
                return `<tr><td>${esc(s.tpl.label)}${spec ? ` <em>(${esc(spec)})</em>` : ''}</td><td class="num">${s.rating}</td><td class="num">${dice}</td></tr>`;
            })
        ].join('');

        /* Knowledge */
        const knowledge = C.knowledgeSkills ? C.knowledgeSkills(character) : (character?.knowledge || []);
        const knowledgeRows = knowledge.map(k => `
            <tr><td>${esc(k.name || 'Unnamed')}</td><td>${esc(k.category || '')}</td><td class="num">${k.native ? 'N' : k.rating}</td></tr>
        `).join('');

        /* Qualities */
        const qualities = C.characterQualities(character);
        const posQuals = qualities.filter(q => q.type === 'positive');
        const negQuals = qualities.filter(q => q.type === 'negative');
        const qualRow = q => {
            const tpl = q.key ? (Q.POSITIVE.find(t => t.key === q.key) || Q.NEGATIVE.find(t => t.key === q.key)) : null;
            let selLabel = '';
            if (q.selection && tpl?.needsSelection) {
                if (tpl.needsSelection.kind === 'skill') selLabel = D.ACTIVE_SKILLS.find(x => x.key === q.selection)?.label || q.selection;
                else if (tpl.needsSelection.kind === 'attribute') selLabel = D.ATTRIBUTES.find(x => x.key === q.selection)?.label || q.selection;
                else if (tpl.needsSelection.kind === 'mentorSpirit') selLabel = M.findMentorSpirit(q.selection)?.name || q.selection;
            }
            return `<tr><td>${esc(q.name)}${q.ranks > 1 ? ` ×${q.ranks}` : ''}${selLabel ? ` — ${esc(selLabel)}` : ''}</td><td class="num">${q.karma || ''}</td></tr>`;
        };

        /* Contacts */
        const contacts = C.characterContacts(character);
        const contactsRows = contacts.map(c => `
            <tr><td>${esc(c.name || 'Unnamed')}</td><td>${esc(c.archetype || '')}</td><td class="num">${c.connection}</td><td class="num">${c.loyalty}</td></tr>
        `).join('');

        /* Gear by category */
        const gear = character?.gear || {};
        const gearCats = [
            ['weapons', 'Weapons'],
            ['armor', 'Armor'],
            ['cyberware', 'Cyberware'],
            ['bioware', 'Bioware'],
            ['electronics', 'Electronics'],
            ['security', 'Security'],
            ['medical', 'Medical'],
            ['misc', 'Misc Gear'],
            ['vehicles', 'Vehicles'],
            ['drones', 'Drones'],
            ['sins', 'SINs'],
            ['licenses', 'Licenses'],
        ];
        const gearSections = gearCats
            .filter(([k]) => (gear[k] || []).length > 0)
            .map(([k, label]) => {
                const rows = gear[k].map(item => `
                    <tr>
                        <td>${esc(item.name)}${item.rating ? ` (R${item.rating})` : ''}</td>
                        <td class="num">${item.cost ? item.cost.toLocaleString() + '¥' : ''}</td>
                    </tr>
                `).join('');
                return `<h3>${esc(label)}</h3><table class="list">${rows}</table>`;
            })
            .join('');

        /* Magic section */
        const magType = C.priorityRow(character, 'magic')?.type;
        const isAwakened = ['magician','mystic_adept','adept','aspected'].includes(magType);
        const isTechno = magType === 'technomancer';
        let magicSection = '';
        if (isAwakened) {
            const spells = character?.magic?.spells || [];
            const adeptPowers = character?.magic?.adeptPowers || [];
            const boundSpirits = character?.magic?.boundSpirits || [];
            const tradition = M.findTradition(C.characterTradition(character));
            let content = `<p><strong>Tradition:</strong> ${esc(tradition?.name || '—')}</p>`;
            if (spells.length) {
                content += '<h3>Spells</h3><table class="list">' +
                    spells.map(s => `<tr><td>${esc(s.name)}</td><td>${esc(s.category || '')}</td></tr>`).join('') +
                    '</table>';
            }
            if (adeptPowers.length) {
                content += '<h3>Adept Powers</h3><table class="list">' +
                    adeptPowers.map(p => `<tr><td>${esc(p.name)}${p.rating ? ` R${p.rating}` : ''}</td></tr>`).join('') +
                    '</table>';
            }
            if (boundSpirits.length) {
                content += '<h3>Bound Spirits</h3><table class="list">' +
                    boundSpirits.map(s => `<tr><td>${esc(s.type)}</td><td class="num">F${s.force}</td><td class="num">${s.services} svc</td></tr>`).join('') +
                    '</table>';
            }
            magicSection = content;
        } else if (isTechno) {
            const forms = character?.magic?.complexForms || [];
            const sprites = character?.matrix?.registeredSprites || [];
            let content = '';
            if (forms.length) {
                content += '<h3>Complex Forms</h3><table class="list">' +
                    forms.map(f => `<tr><td>${esc(f.name)}</td><td>${esc(f.target || '')}</td></tr>`).join('') +
                    '</table>';
            }
            if (sprites.length) {
                content += '<h3>Registered Sprites</h3><table class="list">' +
                    sprites.map(s => `<tr><td>${esc(s.type)}</td><td class="num">L${s.level}</td><td class="num">${s.tasks} task</td></tr>`).join('') +
                    '</table>';
            }
            magicSection = content;
        }

        /* Lifestyle */
        const tierKey = character?.lifestyle?.tier;
        const tier = tierKey ? C.findLifestyleTier(tierKey) : null;
        const months = character?.lifestyle?.months || 1;
        const monthly = C.lifestyleMonthlyRent(character);
        const lifestyleSection = tier
            ? `<p><strong>${esc(tier.label)}</strong> · ${monthly.toLocaleString()}¥/mo · paid ${months} month${months === 1 ? '' : 's'} (${(monthly * months).toLocaleString()}¥)${character.lifestyle.notes ? '<br><em>' + esc(character.lifestyle.notes) + '</em>' : ''}</p>`
            : '<p><em>No lifestyle set.</em></p>';

        /* Condition monitor */
        const cm = C.conditionMonitor ? C.conditionMonitor(character) : { physical: 10, stun: 10 };

        const html = `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>${esc(name)} — Shadowrun 5e</title>
<style>
@page { size: letter; margin: 0.5in; }
* { box-sizing: border-box; }
html, body {
    margin: 0; padding: 0;
    font-family: Georgia, 'Times New Roman', serif;
    font-size: 10pt;
    color: #000;
    background: #fff;
    line-height: 1.35;
}
body { padding: 0; }
h1, h2, h3 { font-family: 'Helvetica Neue', Arial, sans-serif; color: #000; }
h1 { font-size: 18pt; margin: 0 0 4px 0; border-bottom: 2px solid #000; padding-bottom: 4px; }
h2 { font-size: 12pt; margin: 14px 0 6px 0; border-bottom: 1px solid #888; padding-bottom: 2px; letter-spacing: 0.05em; text-transform: uppercase; }
h3 { font-size: 10pt; margin: 10px 0 4px 0; text-transform: uppercase; letter-spacing: 0.05em; color: #444; }
.meta { font-family: 'Helvetica Neue', Arial, sans-serif; font-size: 10pt; color: #333; margin-bottom: 12px; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 14px; }
table { border-collapse: collapse; width: 100%; font-size: 9.5pt; }
table.list td { padding: 2px 4px; border-bottom: 1px solid #ddd; }
table.list tr:last-child td { border-bottom: none; }
table.attrs { margin: 6px 0; }
table.attrs td { padding: 4px 8px; border: 1px solid #888; text-align: center; background: #f6f6f6; font-family: 'Helvetica Neue', Arial, sans-serif; font-size: 10pt; }
table.attrs td strong { display: block; font-size: 8pt; letter-spacing: 0.1em; color: #555; margin-bottom: 2px; }
td.num { text-align: right; font-family: 'Helvetica Neue', Arial, sans-serif; font-weight: 600; color: #8a3a10; white-space: nowrap; width: 40px; }
.section { break-inside: avoid; page-break-inside: avoid; margin-bottom: 8px; }
.cm-row { display: flex; align-items: center; gap: 4px; margin-bottom: 6px; }
.cm-label { font-family: 'Helvetica Neue', Arial, sans-serif; font-size: 9pt; font-weight: 600; width: 60px; }
.cm-box { display: inline-block; width: 12px; height: 12px; border: 1px solid #000; margin-right: 1px; vertical-align: middle; }
.cm-box.filled { background: #000; }
p { margin: 4px 0; }
em { color: #555; }
</style>
</head>
<body>
<h1>${esc(name)}${alias ? ` <span style="font-weight: normal; color: #555;">"${esc(alias)}"</span>` : ''}</h1>
<div class="meta">${esc(metatypeLabel)}${inCareerMode ? ' · Career' : ' · Creation'}</div>

<div class="section">
<h2>Attributes</h2>
<table class="attrs"><tr>${attrRows}</tr></table>
${specialAttrRows ? `<table class="attrs"><tr>${specialAttrRows}</tr></table>` : ''}
</div>

<div class="section">
<h2>Condition Monitor</h2>
<div class="cm-row"><span class="cm-label">Physical</span>${
    Array.from({ length: cm.physical }, (_, i) => `<span class="cm-box"></span>`).join('')
}</div>
<div class="cm-row"><span class="cm-label">Stun</span>${
    Array.from({ length: cm.stun }, (_, i) => `<span class="cm-box"></span>`).join('')
}</div>
</div>

<div class="grid-2">
<div class="section">
<h2>Active Skills</h2>
<table class="list"><thead><tr><th style="text-align:left">Skill</th><th class="num">Rank</th><th class="num">Pool</th></tr></thead>
<tbody>${skillsBody || '<tr><td colspan="3"><em>None</em></td></tr>'}</tbody>
</table>
</div>

<div class="section">
<h2>Knowledge / Languages</h2>
<table class="list"><thead><tr><th style="text-align:left">Name</th><th style="text-align:left">Category</th><th class="num">R</th></tr></thead>
<tbody>${knowledgeRows || '<tr><td colspan="3"><em>None</em></td></tr>'}</tbody>
</table>
</div>
</div>

<div class="grid-2">
<div class="section">
<h2>Positive Qualities</h2>
<table class="list">${posQuals.map(qualRow).join('') || '<tr><td><em>None</em></td></tr>'}</table>
</div>
<div class="section">
<h2>Negative Qualities</h2>
<table class="list">${negQuals.map(qualRow).join('') || '<tr><td><em>None</em></td></tr>'}</table>
</div>
</div>

${magicSection ? `<div class="section"><h2>${isTechno ? 'Resonance' : 'Magic'}</h2>${magicSection}</div>` : ''}

${contacts.length ? `<div class="section">
<h2>Contacts</h2>
<table class="list"><thead><tr><th style="text-align:left">Name</th><th style="text-align:left">Archetype</th><th class="num">Conn</th><th class="num">Loy</th></tr></thead>
<tbody>${contactsRows}</tbody></table>
</div>` : ''}

<div class="section">
<h2>Lifestyle</h2>
${lifestyleSection}
</div>

${gearSections ? `<div class="section"><h2>Gear</h2>${gearSections}</div>` : ''}

<script>
(function() {
    var go = function() { setTimeout(function() { window.print(); }, 300); };
    if (document.fonts && document.fonts.ready) document.fonts.ready.then(go);
    else window.addEventListener('load', go);
})();
</script>
</body>
</html>`;

        const w = window.open('', '_blank', 'width=816,height=1056');
        if (!w) { alert('Please allow popups for this site to print character sheets.'); return; }
        w.document.open();
        w.document.write(html);
        w.document.close();
    }

    return (
        <>
            <div className="content-header sheet-content-header">
                <div style={{ flex: 1 }}>
                    <div className="content-title">✦ / Character Sheet</div>
                    <h2 className="content-heading">
                        {character?.identity?.name || 'Unnamed Runner'}
                        <span className="muted mono" style={{ fontSize: 13, marginLeft: 12 }}>
                            {mtype?.name || '—'}
                            {character?.identity?.streetName && ` · "${character.identity.streetName}"`}
                        </span>
                    </h2>
                </div>
                <button className="btn btn-primary print-sheet-btn"
                        onClick={onPrint}
                        title="Print-friendly version of this sheet">
                    🖨 Print Sheet
                </button>
            </div>

            {/* Print-only header — duplicates the identity info in a format
                tuned for paper. Hidden on screen via CSS. */}
            <div className="print-sheet-header">
                <h2>{character?.identity?.name || 'Unnamed Runner'}</h2>
                <div className="print-meta">
                    {mtype?.name || '—'}
                    {character?.identity?.streetName && ` · "${character.identity.streetName}"`}
                </div>
            </div>

            <div className="sheet-grid">
                <SheetConditionMonitors character={character} onChange={onChange} inCareer={inCareer} />
                <SheetAttributes character={character} />
                <SheetInitiativeLimits character={character} isAwakened={isAwakened} isTechnomancer={isTechnomancer} />
                <SheetSkills character={character} />
                <SheetCombat character={character} />
                <SheetDefenses character={character} />
                <SheetAugmentations character={character} />
                {isAwakened && <SheetMagic character={character} />}
                {isTechnomancer && <SheetMatrix character={character} />}
                {!isTechnomancer && <SheetDecker character={character} />}
                <SheetIdentity character={character} />
                <SheetLifestyle character={character} />
                <SheetContacts character={character} />
            </div>
        </>
    );
};

/* =========================================================
   Condition monitors — interactive in career mode
   ========================================================= */
function SheetConditionMonitors({ character, onChange, inCareer }) {
    const C = SR5_CALC;
    const cm = C.conditionMonitor
        ? C.conditionMonitor(character)
        : { physical: 10, stun: 10 };  /* fallback */
    const careerData = C.career(character) || {};
    const physDamage = careerData.currentPhysicalDamage || 0;
    const stunDamage = careerData.currentStunDamage || 0;
    const overflow = Math.max(0, physDamage - cm.physical);

    /* Click box N to set damage level = N (or unset if already at N) */
    function onBoxClick(kind, boxIndex) {
        if (!inCareer) return;
        const current = kind === 'physical' ? physDamage : stunDamage;
        /* If clicking the last-filled box, reduce by one; otherwise set to that level */
        const newAmount = (boxIndex === current) ? current - 1 : boxIndex;
        onChange(C.setDamage(character, kind, newAmount));
    }

    return (
        <div className="sheet-tile sheet-cm">
            <div className="sheet-tile-head">Condition Monitor</div>
            <div className="cm-track">
                <div className="cm-label">
                    Physical <span className="muted mono">{physDamage}/{cm.physical}</span>
                </div>
                <div className="cm-boxes">
                    {Array.from({ length: cm.physical }, (_, i) => {
                        const n = i + 1;
                        const filled = n <= physDamage;
                        const wound = Math.floor((n - 1) / 3);  /* every 3rd box = -1 dice mod */
                        return (
                            <button key={n}
                                    className={`cm-box ${filled ? 'filled' : ''} ${wound > 0 ? 'wound' : ''}`}
                                    onClick={() => onBoxClick('physical', n)}
                                    disabled={!inCareer}
                                    title={inCareer ? `Set damage to ${n}` : 'Career mode only'}>
                                {n % 3 === 0 && <span className="cm-mod">−{wound + 1}</span>}
                            </button>
                        );
                    })}
                </div>
            </div>
            <div className="cm-track">
                <div className="cm-label">
                    Stun <span className="muted mono">{stunDamage}/{cm.stun}</span>
                </div>
                <div className="cm-boxes">
                    {Array.from({ length: cm.stun }, (_, i) => {
                        const n = i + 1;
                        const filled = n <= stunDamage;
                        const wound = Math.floor((n - 1) / 3);
                        return (
                            <button key={n}
                                    className={`cm-box ${filled ? 'filled' : ''} ${wound > 0 ? 'wound' : ''}`}
                                    onClick={() => onBoxClick('stun', n)}
                                    disabled={!inCareer}
                                    title={inCareer ? `Set damage to ${n}` : 'Career mode only'}>
                                {n % 3 === 0 && <span className="cm-mod">−{wound + 1}</span>}
                            </button>
                        );
                    })}
                </div>
            </div>
            {overflow > 0 && (
                <div className="cm-overflow">
                    ⚠ Physical overflow: {overflow}. Character is dying.
                </div>
            )}
            <div className="cm-edge">
                <span className="cm-label">Edge</span>
                <span className="cm-edge-value">{C.specialAttributeValue(character, 'edg')}</span>
                {inCareer && (
                    <span className="muted mono" style={{ fontSize: 10, marginLeft: 8 }}>
                        track edge burn manually in the Career Log
                    </span>
                )}
            </div>
        </div>
    );
}

/* =========================================================
   Attributes — compact grid
   ========================================================= */
function SheetAttributes({ character }) {
    const C = SR5_CALC;
    const D = SR5_DATA;
    const attrRow = (a) => {
        const isSpecial = !!D.SPECIAL_ATTRIBUTES.find(s => s.key === a.key);
        const v = isSpecial ? C.specialAttributeValue(character, a.key) : C.attributeValue(character, a.key);
        return { label: a.label.slice(0, 3).toUpperCase(), value: v, key: a.key };
    };
    const physMental = D.ATTRIBUTES.map(attrRow);
    const special = D.SPECIAL_ATTRIBUTES.map(attrRow);
    const priorityMagic = C.priorityRow(character, 'magic');
    const magicType = priorityMagic?.type || 'mundane';

    const filteredSpecial = special.filter(a => {
        if (a.key === 'mag') return magicType !== 'mundane' && magicType !== 'technomancer';
        if (a.key === 'res') return magicType === 'technomancer';
        return true;
    });

    const essence = C.currentEssence(character);

    return (
        <div className="sheet-tile sheet-attrs">
            <div className="sheet-tile-head">Attributes</div>
            <div className="attr-grid">
                {physMental.map(a => (
                    <div key={a.key} className="attr-cell">
                        <span className="attr-label mono">{a.label}</span>
                        <span className="attr-val mono">{a.value}</span>
                    </div>
                ))}
                {filteredSpecial.map(a => (
                    <div key={a.key} className="attr-cell special">
                        <span className="attr-label mono">{a.label}</span>
                        <span className="attr-val mono">{a.value}</span>
                    </div>
                ))}
                <div className="attr-cell special">
                    <span className="attr-label mono">ESS</span>
                    <span className="attr-val mono" style={{
                        color: essence <= 1 ? 'var(--bad)' : essence <= 3 ? 'var(--warn)' : 'var(--text-primary)',
                    }}>
                        {essence.toFixed(2)}
                    </span>
                </div>
            </div>
        </div>
    );
}

/* =========================================================
   Initiative + Limits
   ========================================================= */
function SheetInitiativeLimits({ character, isAwakened, isTechnomancer }) {
    const C = SR5_CALC;
    const phys = C.physicalInitiative(character);
    const matrix = C.matrixInitiative ? C.matrixInitiative(character, false) : null;
    const matrixHot = C.matrixInitiative ? C.matrixInitiative(character, true) : null;
    const astral = isAwakened && C.astralInitiative ? C.astralInitiative(character) : null;
    const derived = C.derivedStats(character);
    const physLimit = derived.find(d => d.key === 'physicalLimit')?.value;
    const mentLimit = derived.find(d => d.key === 'mentalLimit')?.value;
    const socLimit = derived.find(d => d.key === 'socialLimit')?.value;

    return (
        <div className="sheet-tile sheet-init">
            <div className="sheet-tile-head">Initiative &amp; Limits</div>
            <div className="init-section">
                <div className="init-row">
                    <span className="init-label">Physical</span>
                    <span className="init-value mono">{phys.base} + {phys.dice}D6</span>
                </div>
                {matrix && (matrix.base > 0 || isTechnomancer) && (
                    <>
                        <div className="init-row">
                            <span className="init-label">Matrix (cold)</span>
                            <span className="init-value mono">{matrix.base} + {matrix.dice}D6</span>
                        </div>
                        <div className="init-row">
                            <span className="init-label">Matrix (hot)</span>
                            <span className="init-value mono">{matrixHot.base} + {matrixHot.dice}D6</span>
                        </div>
                    </>
                )}
                {astral && (
                    <div className="init-row">
                        <span className="init-label">Astral</span>
                        <span className="init-value mono">{astral.base} + {astral.dice}D6</span>
                    </div>
                )}
            </div>
            <div className="limit-section">
                <div className="limit-row">
                    <span className="limit-label">Physical Limit</span>
                    <span className="limit-val mono">{physLimit}</span>
                </div>
                <div className="limit-row">
                    <span className="limit-label">Mental Limit</span>
                    <span className="limit-val mono">{mentLimit}</span>
                </div>
                <div className="limit-row">
                    <span className="limit-label">Social Limit</span>
                    <span className="limit-val mono">{socLimit}</span>
                </div>
            </div>
        </div>
    );
}

/* =========================================================
   Skills — by category, with dice pools as the hero number
   ========================================================= */
function SheetSkills({ character }) {
    const C = SR5_CALC;
    const skills = C.characterActiveSkills(character);
    if (skills.length === 0) {
        return (
            <div className="sheet-tile sheet-skills">
                <div className="sheet-tile-head">Skills</div>
                <div className="muted">No skills with a rating yet.</div>
            </div>
        );
    }
    /* Group by category */
    const byCat = {};
    for (const s of skills) {
        byCat[s.skill.category] = byCat[s.skill.category] || [];
        byCat[s.skill.category].push(s);
    }
    const catLabels = {
        combat: 'Combat', physical: 'Physical', social: 'Social',
        magical: 'Magical', resonance: 'Resonance',
        technical: 'Technical', vehicle: 'Vehicle',
    };
    const catKeys = Object.keys(byCat);

    return (
        <div className="sheet-tile sheet-skills">
            <div className="sheet-tile-head">Skills ({skills.length})</div>
            <div className="skills-grid">
                {catKeys.map(cat => (
                    <div key={cat} className="skills-category">
                        <div className="skills-cat-label mono">{catLabels[cat] || cat}</div>
                        {byCat[cat].map(s => (
                            <div key={s.skillKey} className="skill-row">
                                <span className="skill-pool mono">{s.pool}</span>
                                <span className="skill-name">
                                    {s.skill.label}
                                    <span className="muted mono skill-meta">
                                        {' '}· {s.rating} {s.skill.attr.toUpperCase()}
                                    </span>
                                </span>
                                {s.spec && (
                                    <span className="skill-spec mono">
                                        {s.spec} (+2)
                                    </span>
                                )}
                            </div>
                        ))}
                    </div>
                ))}
            </div>
        </div>
    );
}

/* =========================================================
   Combat — weapons
   ========================================================= */
function SheetCombat({ character }) {
    const C = SR5_CALC;
    const G = SR5_GEAR;
    const weapons = C.characterWeapons(character);
    return (
        <div className="sheet-tile sheet-combat">
            <div className="sheet-tile-head">Weapons</div>
            {weapons.length === 0 ? (
                <div className="muted">No weapons equipped.</div>
            ) : (
                <div className="weapon-list">
                    {weapons.map(w => {
                        const tpl = w.key ? G.findWeapon(w.key) : null;
                        const stats = tpl || w;
                        return (
                            <div key={w.id} className="sheet-weapon">
                                <div className="sw-name">{w.name}</div>
                                <div className="sw-stats mono">
                                    <span>DV {stats.damage || '—'}</span>
                                    <span>AP {stats.ap ?? 0}</span>
                                    <span>Acc {stats.acc ?? '—'}</span>
                                    {stats.mode && <span>{stats.mode}</span>}
                                    {stats.rc !== undefined && <span>RC {stats.rc}</span>}
                                    {stats.ammo && <span>{stats.ammo}</span>}
                                    {stats.reach !== undefined && <span>Reach {stats.reach}</span>}
                                </div>
                                {w.notes && <div className="sw-notes muted">{w.notes}</div>}
                            </div>
                        );
                    })}
                </div>
            )}
        </div>
    );
}

/* =========================================================
   Defenses — armor, dodge, soak dice
   ========================================================= */
function SheetDefenses({ character }) {
    const C = SR5_CALC;
    const armor = C.currentArmorRating(character);
    const bod = C.attributeValue(character, 'bod');
    const rea = C.attributeValue(character, 'rea');
    const int = C.attributeValue(character, 'int');
    /* Soak pool: BOD + armor */
    const soak = bod + armor;
    /* Dodge pool: REA + INT (defending against ranged attacks) */
    const dodge = rea + int;
    /* Full Defense: + WIL */
    const wil = C.attributeValue(character, 'wil');
    const fullDef = dodge + wil;
    return (
        <div className="sheet-tile sheet-defenses">
            <div className="sheet-tile-head">Defense &amp; Soak</div>
            <div className="def-grid">
                <div className="def-cell">
                    <div className="def-label mono">Armor</div>
                    <div className="def-val mono">{armor}</div>
                </div>
                <div className="def-cell">
                    <div className="def-label mono">Soak</div>
                    <div className="def-val mono">{soak}</div>
                    <div className="def-hint muted mono">BOD+Armor</div>
                </div>
                <div className="def-cell">
                    <div className="def-label mono">Dodge</div>
                    <div className="def-val mono">{dodge}</div>
                    <div className="def-hint muted mono">REA+INT</div>
                </div>
                <div className="def-cell">
                    <div className="def-label mono">Full Def</div>
                    <div className="def-val mono">{fullDef}</div>
                    <div className="def-hint muted mono">+WIL</div>
                </div>
            </div>
        </div>
    );
}

/* =========================================================
   Augmentations — cyberware + bioware
   ========================================================= */
function SheetAugmentations({ character }) {
    const C = SR5_CALC;
    const augs = C.characterActiveAugmentations(character);
    if (augs.length === 0) return null;
    return (
        <div className="sheet-tile sheet-augs">
            <div className="sheet-tile-head">Augmentations</div>
            <div className="augs-list">
                {augs.map((a, i) => (
                    <div key={i} className={`aug-row aug-${a.type}`}>
                        <span className="aug-name">{a.name}</span>
                        <span className="aug-type mono">{a.grade || 'standard'}</span>
                    </div>
                ))}
            </div>
        </div>
    );
}

/* =========================================================
   Magic — spells, powers, drain, foci (awakened only)
   ========================================================= */
function SheetMagic({ character }) {
    const C = SR5_CALC;
    const magic = character?.magic || {};
    const spells = magic.spells || [];
    const powers = magic.adeptPowers || [];
    const foci = C.characterFoci(character);
    const mag = C.magicRatingCurrent ? C.magicRatingCurrent(character) : C.specialAttributeValue(character, 'mag');
    const priority = C.priorityRow(character, 'magic');
    const tradition = magic.tradition;
    const drainAttr = tradition?.drain || 'wil';
    const drainPool = C.attributeValue(character, 'wil') + C.attributeValue(character, drainAttr === 'wil' ? 'cha' : drainAttr);

    return (
        <div className="sheet-tile sheet-magic">
            <div className="sheet-tile-head">Magic</div>
            <div className="magic-header">
                <div className="mh-item">
                    <span className="muted mono">MAG</span>
                    <span className="mono">{mag}</span>
                </div>
                {tradition && (
                    <div className="mh-item">
                        <span className="muted mono">Tradition</span>
                        <span>{tradition.name || tradition.key}</span>
                    </div>
                )}
                {tradition && (
                    <div className="mh-item">
                        <span className="muted mono">Drain</span>
                        <span className="mono">{drainPool}d</span>
                    </div>
                )}
            </div>
            {spells.length > 0 && (
                <div className="magic-subsection">
                    <div className="skills-cat-label mono">Spells ({spells.length})</div>
                    {spells.map((s, i) => (
                        <div key={i} className="sheet-spell">
                            <span>{s.name}</span>
                            <span className="muted mono">{s.category}</span>
                        </div>
                    ))}
                </div>
            )}
            {powers.length > 0 && (
                <div className="magic-subsection">
                    <div className="skills-cat-label mono">Adept Powers ({powers.length})</div>
                    {powers.map((p, i) => (
                        <div key={i} className="sheet-power">
                            <span>{p.name}{p.level ? ` ${p.level}` : ''}</span>
                            <span className="muted mono">{p.ppCost || 0} PP</span>
                        </div>
                    ))}
                </div>
            )}
            {foci.length > 0 && (
                <div className="magic-subsection">
                    <div className="skills-cat-label mono">Foci ({foci.length})</div>
                    {foci.map((f, i) => (
                        <div key={i} className="sheet-focus">
                            <span>{f.name} F{f.force}</span>
                            <span className="muted mono">{f.bonded !== false ? 'bonded' : 'unbonded'}</span>
                        </div>
                    ))}
                </div>
            )}
        </div>
    );
}

/* =========================================================
   Matrix — technomancer
   ========================================================= */
function SheetMatrix({ character }) {
    const C = SR5_CALC;
    const persona = C.personaStats(character);
    const forms = character?.magic?.complexForms || [];
    const sprites = C.characterSprites ? C.characterSprites(character) : [];
    const echoes = C.characterEchoes ? C.characterEchoes(character) : [];
    const subGrade = C.submersionGrade ? C.submersionGrade(character) : 0;
    const res = C.specialAttributeValue(character, 'res');

    return (
        <div className="sheet-tile sheet-matrix">
            <div className="sheet-tile-head">Matrix — Living Persona</div>
            <div className="persona-stats">
                <div className="ps-cell">
                    <span className="muted mono">RES</span>
                    <span className="mono">{res}</span>
                </div>
                {persona && persona.kind !== 'none' && (
                    <>
                        <div className="ps-cell">
                            <span className="muted mono">ATK</span>
                            <span className="mono">{persona.attack}</span>
                        </div>
                        <div className="ps-cell">
                            <span className="muted mono">SLZ</span>
                            <span className="mono">{persona.sleaze}</span>
                        </div>
                        <div className="ps-cell">
                            <span className="muted mono">DP</span>
                            <span className="mono">{persona.dataProcessing}</span>
                        </div>
                        <div className="ps-cell">
                            <span className="muted mono">FW</span>
                            <span className="mono">{persona.firewall}</span>
                        </div>
                    </>
                )}
                {subGrade > 0 && (
                    <div className="ps-cell">
                        <span className="muted mono">SUB</span>
                        <span className="mono">G{subGrade}</span>
                    </div>
                )}
            </div>
            {forms.length > 0 && (
                <div className="magic-subsection">
                    <div className="skills-cat-label mono">Complex Forms ({forms.length})</div>
                    {forms.map((f, i) => (
                        <div key={i} className="sheet-spell">
                            <span>{f.name}</span>
                            <span className="muted mono">fade {f.fade || '—'}</span>
                        </div>
                    ))}
                </div>
            )}
            {echoes.length > 0 && (
                <div className="magic-subsection">
                    <div className="skills-cat-label mono">Echoes ({echoes.length})</div>
                    {echoes.map((e, i) => {
                        const tpl = SR5_MATRIX.findEcho(e.key);
                        return (
                            <div key={i} className="sheet-spell">
                                <span>{tpl?.name || e.key}{e.selection ? ` (${e.selection})` : ''}</span>
                            </div>
                        );
                    })}
                </div>
            )}
            {sprites.length > 0 && (
                <div className="magic-subsection">
                    <div className="skills-cat-label mono">Sprites ({sprites.length})</div>
                    {sprites.map((s, i) => (
                        <div key={i} className="sheet-spell">
                            <span>{s.name || s.type} F{s.level}</span>
                            <span className="muted mono">{s.state}</span>
                        </div>
                    ))}
                </div>
            )}
        </div>
    );
}

/* =========================================================
   Decker — active deck
   ========================================================= */
function SheetDecker({ character }) {
    const C = SR5_CALC;
    const decks = C.characterCyberdecks ? C.characterCyberdecks(character) : [];
    if (decks.length === 0) return null;
    const activeDeck = decks.find(d => d.id === character?.matrix?.activeDeckId) || decks[0];
    const asdf = character?.matrix?.asdf;
    return (
        <div className="sheet-tile sheet-matrix">
            <div className="sheet-tile-head">Matrix — Cyberdeck</div>
            <div className="mh-item" style={{ marginBottom: 8 }}>
                <span>{activeDeck.name}</span>
            </div>
            {asdf && (
                <div className="persona-stats">
                    <div className="ps-cell">
                        <span className="muted mono">ATK</span>
                        <span className="mono">{asdf.attack}</span>
                    </div>
                    <div className="ps-cell">
                        <span className="muted mono">SLZ</span>
                        <span className="mono">{asdf.sleaze}</span>
                    </div>
                    <div className="ps-cell">
                        <span className="muted mono">DP</span>
                        <span className="mono">{asdf.dataProcessing}</span>
                    </div>
                    <div className="ps-cell">
                        <span className="muted mono">FW</span>
                        <span className="mono">{asdf.firewall}</span>
                    </div>
                </div>
            )}
        </div>
    );
}

/* =========================================================
   Identity — SINs and licenses
   ========================================================= */
function SheetIdentity({ character }) {
    const C = SR5_CALC;
    const sins = C.characterSINs(character);
    const licenses = C.characterLicenses(character);
    if (sins.length === 0 && licenses.length === 0) return null;
    return (
        <div className="sheet-tile sheet-identity">
            <div className="sheet-tile-head">SINs &amp; Licenses</div>
            {sins.map(s => (
                <div key={s.id} className="sheet-sin">
                    <span>{s.name || `Fake SIN R${s.rating}`}</span>
                    <span className="muted mono">R{s.rating}</span>
                </div>
            ))}
            {licenses.map(l => (
                <div key={l.id} className="sheet-sin">
                    <span className="muted">⌐</span>
                    <span>{l.name || `License R${l.rating}`}</span>
                    <span className="muted mono">R{l.rating}</span>
                </div>
            ))}
        </div>
    );
}

/* =========================================================
   Lifestyle tile — monthly rent indicator
   ========================================================= */
function SheetLifestyle({ character }) {
    const C = SR5_CALC;
    const tierKey = character?.lifestyle?.tier;
    if (!tierKey) return null;
    const tier = C.findLifestyleTier(tierKey);
    if (!tier) return null;
    const monthly = C.lifestyleMonthlyRent(character);
    const months = character?.lifestyle?.months || 1;
    const notes = character?.lifestyle?.notes || '';
    return (
        <div className="sheet-tile sheet-lifestyle">
            <div className="sheet-tile-head">Lifestyle</div>
            <div className="sheet-lt-row">
                <span className="sheet-lt-name">{tier.label}</span>
                <span className="sheet-lt-cost">{monthly.toLocaleString()}¥/mo</span>
            </div>
            <div className="sheet-lt-paid">
                <span className="muted">paid at creation:</span>
                <span> {months}mo × {monthly.toLocaleString()}¥ = </span>
                <span className="accent">{(monthly * months).toLocaleString()}¥</span>
            </div>
            {notes && (
                <div className="sheet-lt-notes">{notes}</div>
            )}
        </div>
    );
}

/* =========================================================
   Contacts tile — name / Connection / Loyalty table
   ========================================================= */
function SheetContacts({ character }) {
    const C = SR5_CALC;
    const contacts = C.characterContacts(character);
    if (contacts.length === 0) return null;
    return (
        <div className="sheet-tile sheet-contacts">
            <div className="sheet-tile-head">Contacts ({contacts.length})</div>
            <div className="sheet-contacts-table">
                {contacts.map(c => (
                    <div key={c.id} className="sheet-contact-row">
                        <div className="sheet-contact-name">
                            <strong>{c.name || 'Unnamed'}</strong>
                            {c.archetype && (
                                <span className="muted" style={{ fontSize: 10, marginLeft: 6 }}>
                                    {c.archetype}
                                </span>
                            )}
                        </div>
                        <div className="sheet-contact-ratings">
                            <span className="mono">C{c.connection}</span>
                            <span className="muted">/</span>
                            <span className="mono">L{c.loyalty}</span>
                        </div>
                    </div>
                ))}
            </div>
        </div>
    );
}
