/* global React, MEMBERS, TESTS, CHALLENGES, TODAY,
   Eyebrow, Avatar, Card, Pill, Button, Input, SectionHeader, Progress,
   getActiveSeason, seasonWeek, daysRemaining, getMemberChallenges,
   getTest, latestAttempt, baselineAttempt, challengeProgress, challengeOnTrack,
   addAttempt, removeAttempt, archiveChallenge, unarchiveChallenge,
   updateChallenge, createChallenge, suggestedTarget, createTest, fmtDate */
const { useState, useMemo } = React;

function TesterScreen({ memberId, tweaks }) {
  const [bump, setBump] = useState(0);
  const refresh = () => setBump(b => b + 1);
  const [openChallenge, setOpenChallenge] = useState(null); // challenge id
  const [creating, setCreating] = useState(false);
  const [showArchived, setShowArchived] = useState(false);

  const member = MEMBERS.find(m => m.id === memberId);
  const season = getActiveSeason();
  const wk = seasonWeek(TODAY, season.id);
  const daysLeft = daysRemaining(TODAY, season.id);

  // Re-read on bump so React refreshes after mutations on window.CHALLENGES
  const active = useMemo(
    () => getMemberChallenges(memberId, { includeArchived: false }),
    [memberId, bump]
  );
  const archived = useMemo(
    () => getMemberChallenges(memberId, { includeArchived: true }).filter(c => c.status === 'arkiverad'),
    [memberId, bump]
  );

  const open = openChallenge ? CHALLENGES.find(c => c.id === openChallenge) : null;

  return (
    <div style={{ padding: '32px 32px 80px', maxWidth: 1280, margin: '0 auto' }}>
      {/* Season banner */}
      <SeasonBanner season={season} wk={wk} daysLeft={daysLeft} active={active.length} />

      {/* Page header */}
      <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', marginTop: 28, marginBottom: 24, flexWrap: 'wrap', gap: 12 }}>
        <div>
          <Eyebrow style={{ marginBottom: 10 }}>Tester · utmaningar</Eyebrow>
          <h1 style={{ margin: 0, fontFamily: 'var(--font-sans)', fontSize: 36, fontWeight: 500, letterSpacing: '-0.02em', lineHeight: 1.1 }}>
            <span style={{ color: member.color }}>{member.name}</span>s utmaningar
          </h1>
          <p style={{ margin: '10px 0 0', maxWidth: 620, fontSize: 14, color: 'var(--fg-muted)' }}>
            Bestäm ett mål, mät baseline idag och följ utvecklingen genom säsongen.
            Logga försök när du gör testet. Du kan ha flera utmaningar parallellt.
          </p>
        </div>
        <Button kind="primary" size="lg" onClick={() => setCreating(true)}>
          <span style={{ fontSize: 16, lineHeight: 1 }}>+</span> Skapa utmaning
        </Button>
      </div>

      {/* Active challenges grid */}
      {active.length === 0 ? (
        <EmptyState onCreate={() => setCreating(true)} />
      ) : (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))', gap: 16 }}>
          {active.map(c => (
            <ChallengeCard
              key={c.id}
              challenge={c}
              onOpen={() => setOpenChallenge(c.id)}
            />
          ))}
        </div>
      )}

      {/* Archived section */}
      {archived.length > 0 && (
        <div style={{ marginTop: 36 }}>
          <button
            onClick={() => setShowArchived(s => !s)}
            style={{
              fontFamily: 'var(--font-mono)', fontSize: 11, letterSpacing: '0.08em', textTransform: 'uppercase',
              background: 'transparent', border: 'none', color: 'var(--fg-muted)', cursor: 'pointer',
              padding: 0, display: 'inline-flex', alignItems: 'center', gap: 6,
            }}
          >
            <span style={{ transform: showArchived ? 'rotate(90deg)' : 'rotate(0)', transition: 'transform 120ms', display: 'inline-block' }}>›</span>
            Arkiverade utmaningar ({archived.length})
          </button>
          {showArchived && (
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))', gap: 16, marginTop: 16 }}>
              {archived.map(c => (
                <ChallengeCard
                  key={c.id}
                  challenge={c}
                  onOpen={() => setOpenChallenge(c.id)}
                  archived
                />
              ))}
            </div>
          )}
        </div>
      )}

      {/* Principles row (kept from original) */}
      <div style={{ marginTop: 36, display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 16 }}>
        <PrincipleCard title="Säkerhet" body="Avbryt om smärta, yrsel eller ovanlig trötthet. Kvalitet före maxning. Stoppa om tekniken faller." />
        <PrincipleCard title="Uppvärmning" body="10–15 min lätt jogg/cykel, dynamisk rörlighet och 3–4 stegringslopp före sprint, hopp eller agility." />
        <PrincipleCard title="Jämför rätt" body="Samma underlag, skor, tidtagare och ungefär samma tid på dagen ger bäst jämförelse mellan försök." />
      </div>

      {/* Modals */}
      {open && (
        <ChallengeDetailModal
          challenge={open}
          onClose={() => setOpenChallenge(null)}
          onChange={refresh}
        />
      )}
      {creating && (
        <CreateChallengeModal
          memberId={memberId}
          existingTestIds={active.map(c => c.testId)}
          onClose={() => setCreating(false)}
          onCreated={() => { setCreating(false); refresh(); }}
        />
      )}
    </div>
  );
}

// ============================================================================
// Season banner
// ============================================================================
function SeasonBanner({ season, wk, daysLeft, active }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16, flexWrap: 'wrap',
      padding: '14px 20px',
      background: 'linear-gradient(95deg, var(--ink-25), var(--bg-raised))',
      border: '1px solid var(--border)',
      borderRadius: 'var(--radius-2)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 14, flexWrap: 'wrap' }}>
        <Pill bg="var(--signal)" color="#fff">Säsong</Pill>
        <div style={{ fontSize: 15, fontWeight: 500, letterSpacing: '-0.01em' }}>{season.name}</div>
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--fg-muted)', letterSpacing: '0.04em' }}>
          Vecka {wk} av {season.weeks} · {daysLeft} dagar kvar
        </div>
      </div>
      <div style={{ minWidth: 200, flex: '0 1 280px' }}>
        <Progress value={wk} max={season.weeks} />
      </div>
    </div>
  );
}

// ============================================================================
// Challenge card
// ============================================================================
function ChallengeCard({ challenge, onOpen, archived }) {
  const test = getTest(challenge.testId);
  if (!test) return null;
  const latest = latestAttempt(challenge);
  const latestVal = latest?.value ?? null;
  const baseline = challenge.baseline;
  const target = challenge.target;
  const pct = challengeProgress(challenge);
  const track = challengeOnTrack(challenge, TODAY);
  const trackColor = track === 'före' ? 'var(--success)' : track === 'bakom' ? 'var(--danger)' : 'var(--fg-muted)';
  const trackBg    = track === 'före' ? 'rgba(14,133,67,0.10)' : track === 'bakom' ? 'rgba(176,33,26,0.10)' : 'var(--ink-50)';

  const fmtVal = (v) => v == null ? '—' : (test.unit === 'antal' || test.unit === 'nivå' || test.unit === 'poäng' ? String(v) : (Math.round(v * 100) / 100));

  return (
    <Card hover onClick={onOpen} style={{ opacity: archived ? 0.6 : 1, position: 'relative' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 10 }}>
        <div>
          <Eyebrow>{test.cat}</Eyebrow>
          <div style={{ fontSize: 16, fontWeight: 500, marginTop: 6, letterSpacing: '-0.01em' }}>{test.name}</div>
          <div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--fg-subtle)', letterSpacing: '0.04em', marginTop: 2 }}>
            {test.unit} · {test.dir === 'lower' ? 'lägre = bättre' : 'högre = bättre'}
          </div>
        </div>
        {archived
          ? <Pill bg="var(--ink-50)" color="var(--fg-muted)">Arkiverad</Pill>
          : <Pill bg={trackBg} color={trackColor}>{track}</Pill>}
      </div>

      <div style={{
        marginTop: 16, display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8,
        fontFamily: 'var(--font-mono)', fontVariantNumeric: 'tabular-nums',
      }}>
        <ValueBlock label="Baseline" value={fmtVal(baseline)} unit={test.unit} />
        <ValueBlock label="Senaste"  value={fmtVal(latestVal)} unit={test.unit} highlight />
        <ValueBlock label="Mål"      value={fmtVal(target)}    unit={test.unit} />
      </div>

      <div style={{ marginTop: 14 }}>
        <Progress value={pct} max={100} color={track === 'bakom' ? 'var(--danger)' : 'var(--signal)'} />
        <div style={{ marginTop: 6, display: 'flex', justifyContent: 'space-between', fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--fg-muted)' }}>
          <span>{Math.round(pct)}% mot mål</span>
          <span>{challenge.attempts.length} {challenge.attempts.length === 1 ? 'försök' : 'försök'}</span>
        </div>
      </div>
    </Card>
  );
}

function ValueBlock({ label, value, unit, highlight }) {
  return (
    <div>
      <div style={{ fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '0.10em', textTransform: 'uppercase', color: 'var(--fg-subtle)' }}>{label}</div>
      <div style={{
        marginTop: 4,
        fontSize: highlight ? 22 : 18, fontWeight: 500,
        color: highlight ? 'var(--fg)' : 'var(--fg-muted)',
        letterSpacing: '-0.01em',
      }}>{value}</div>
      <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--fg-subtle)', letterSpacing: '0.04em' }}>{unit}</div>
    </div>
  );
}

// ============================================================================
// Challenge detail modal
// ============================================================================
function ChallengeDetailModal({ challenge, onClose, onChange }) {
  const test = getTest(challenge.testId);
  const member = MEMBERS.find(m => m.id === challenge.memberId);
  const [newValue, setNewValue] = useState('');
  const [newNote, setNewNote] = useState('');
  const [editingTarget, setEditingTarget] = useState(false);
  const [targetDraft, setTargetDraft] = useState(challenge.target ?? '');

  // Sort attempts newest-first. On date ties, the last-appended wins (so a
  // newly-logged attempt on the same day as the seed "today" attempt appears
  // first in the log).
  const indexed = challenge.attempts.map((a, idx) => ({ a, idx }));
  indexed.sort((x, y) => (y.a.date - x.a.date) || (y.idx - x.idx));
  const sortedAttempts = indexed.map(x => x.a);
  const latest = latestAttempt(challenge);
  const pct = challengeProgress(challenge);
  const wk = seasonWeek(TODAY, challenge.seasonId);

  const submitAttempt = () => {
    if (newValue === '' || isNaN(Number(newValue))) return;
    addAttempt(challenge.id, Number(newValue), newNote.trim());
    setNewValue('');
    setNewNote('');
    onChange?.();
  };

  const saveTarget = () => {
    const t = Number(targetDraft);
    if (isNaN(t)) { setEditingTarget(false); return; }
    updateChallenge(challenge.id, { target: t });
    setEditingTarget(false);
    onChange?.();
  };

  const onArchive = () => {
    if (challenge.status === 'arkiverad') {
      unarchiveChallenge(challenge.id);
    } else {
      if (!confirm('Arkivera denna utmaning? Historiken sparas men den göms från aktiva listan.')) return;
      archiveChallenge(challenge.id);
    }
    onChange?.();
    onClose();
  };

  return (
    <Modal onClose={onClose} maxWidth={680}>
      <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 14, marginBottom: 16 }}>
        <div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
            <Avatar member={member} size={24} />
            <Eyebrow>{member.name} · {test.cat}</Eyebrow>
          </div>
          <h2 style={{ margin: 0, fontSize: 24, fontWeight: 500, letterSpacing: '-0.015em' }}>{test.name}</h2>
          <div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--fg-subtle)', letterSpacing: '0.04em', marginTop: 4 }}>
            {test.unit} · {test.dir === 'lower' ? 'lägre = bättre' : 'högre = bättre'} · vecka {wk} av 12
          </div>
        </div>
        <div style={{ display: 'flex', gap: 8 }}>
          <Button kind="danger" size="sm" onClick={onArchive}>
            {challenge.status === 'arkiverad' ? 'Återaktivera' : 'Arkivera'}
          </Button>
          <Button kind="ghost" size="sm" onClick={onClose}>Stäng</Button>
        </div>
      </div>

      {/* Stats row */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16, padding: '16px 0', borderTop: '1px solid var(--border-faint)', borderBottom: '1px solid var(--border-faint)' }}>
        <Stat label="Baseline" value={fmtNumber(challenge.baseline, test)} unit={test.unit} />
        <Stat
          label="Senaste"
          value={fmtNumber(latest?.value, test)}
          unit={test.unit}
          deltaPct={
            latest && challenge.baseline
              ? ((latest.value - challenge.baseline) / challenge.baseline * 100)
              : null
          }
          dir={test.dir}
        />
        <div>
          <Eyebrow>Mål</Eyebrow>
          {editingTarget ? (
            <div style={{ marginTop: 6, display: 'flex', gap: 6, alignItems: 'center' }}>
              <Input value={targetDraft} onChange={setTargetDraft} type="number" size="sm" style={{ width: 120 }} suffix={test.unit} />
              <Button size="sm" onClick={saveTarget}>Spara</Button>
            </div>
          ) : (
            <button
              onClick={() => { setTargetDraft(challenge.target); setEditingTarget(true); }}
              style={{
                marginTop: 6, background: 'none', border: 'none', cursor: 'pointer', padding: 0,
                fontFamily: 'var(--font-sans)', fontSize: 32, fontWeight: 500, color: 'var(--fg)',
                letterSpacing: '-0.02em', fontVariantNumeric: 'tabular-nums', textAlign: 'left',
              }}
              title="Klicka för att ändra"
            >{fmtNumber(challenge.target, test)}
              <span style={{ fontSize: 13, color: 'var(--fg-muted)', fontFamily: 'var(--font-mono)', marginLeft: 6 }}>{test.unit}</span>
            </button>
          )}
        </div>
      </div>

      {/* Progress bar */}
      <div style={{ margin: '16px 0' }}>
        <Progress value={pct} max={100} />
        <div style={{ marginTop: 6, fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--fg-muted)' }}>
          {Math.round(pct)}% mot mål
        </div>
      </div>

      {/* Chart */}
      <div style={{ margin: '16px 0' }}>
        <Eyebrow style={{ marginBottom: 8 }}>Försök över tid</Eyebrow>
        <AttemptChart attempts={challenge.attempts} test={test} baseline={challenge.baseline} target={challenge.target} />
      </div>

      {/* Add attempt */}
      <div style={{ margin: '20px 0 12px', padding: 14, background: 'var(--ink-25)', border: '1px solid var(--border-faint)', borderRadius: 'var(--radius-1)' }}>
        <Eyebrow style={{ marginBottom: 10 }}>Logga nytt försök</Eyebrow>
        <div style={{ display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' }}>
          <Input value={newValue} onChange={setNewValue} type="number" placeholder="Värde" suffix={test.unit} style={{ width: 150 }} />
          <Input value={newNote} onChange={setNewNote} placeholder="Anteckning (frivilligt)" style={{ flex: 1, minWidth: 220 }} />
          <Button onClick={submitAttempt} disabled={newValue === ''}>Lägg till</Button>
        </div>
      </div>

      {/* Attempt log */}
      <div style={{ marginTop: 16 }}>
        <Eyebrow style={{ marginBottom: 8 }}>Logg ({challenge.attempts.length})</Eyebrow>
        <div style={{ border: '1px solid var(--border-faint)', borderRadius: 'var(--radius-1)', overflow: 'hidden' }}>
          {sortedAttempts.map((a, i) => (
            <div key={a.id} style={{
              display: 'grid',
              gridTemplateColumns: '110px 100px 80px 1fr 36px',
              gap: 12,
              padding: '10px 14px',
              alignItems: 'center',
              borderBottom: i < sortedAttempts.length - 1 ? '1px solid var(--border-faint)' : 'none',
              background: a.milestone ? 'var(--ink-25)' : 'transparent',
            }}>
              <div style={{ fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--fg-muted)', letterSpacing: '0.02em' }}>
                {fmtDate(a.date)}
              </div>
              <div style={{ fontFamily: 'var(--font-mono)', fontSize: 14, fontVariantNumeric: 'tabular-nums', fontWeight: 500 }}>
                {fmtNumber(a.value, test)} <span style={{ fontSize: 10, color: 'var(--fg-subtle)' }}>{test.unit}</span>
              </div>
              <div>
                {a.milestone && (
                  <Pill bg="var(--ink-100)" color="var(--fg-muted)">
                    {a.milestone === 'baseline' ? 'baseline' : a.milestone === 'mid' ? 'mid' : 'post'}
                  </Pill>
                )}
              </div>
              <div style={{ fontSize: 12, color: 'var(--fg-muted)' }}>{a.note || ''}</div>
              <button
                onClick={() => {
                  if (a.milestone === 'baseline') { alert('Baseline-försöket går inte att ta bort.'); return; }
                  if (!confirm('Ta bort detta försök?')) return;
                  removeAttempt(challenge.id, a.id);
                  onChange?.();
                }}
                title="Ta bort"
                style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--fg-subtle)', fontSize: 14, padding: 4 }}
              >×</button>
            </div>
          ))}
        </div>
      </div>
    </Modal>
  );
}

// ============================================================================
// Attempt chart — small inline SVG line + markers
// ============================================================================
function AttemptChart({ attempts, test, baseline, target }) {
  if (!attempts.length) return null;
  const W = 600, H = 140, P = 28;
  const sorted = attempts.slice().sort((a, b) => a.date - b.date);
  const dates = sorted.map(a => a.date.getTime());
  const dMin = Math.min(...dates);
  const dMax = Math.max(...dates, dMin + 86400000);
  const vals = [baseline, target, ...sorted.map(a => a.value)].filter(v => v != null);
  const vMin = Math.min(...vals);
  const vMax = Math.max(...vals);
  const range = vMax - vMin || 1;
  const x = (d) => P + ((d - dMin) / (dMax - dMin || 1)) * (W - 2 * P);
  const y = (v) => H - P - ((v - vMin) / range) * (H - 2 * P);

  const path = sorted.map((a, i) => `${i === 0 ? 'M' : 'L'} ${x(a.date.getTime()).toFixed(1)} ${y(a.value).toFixed(1)}`).join(' ');
  const baselineY = y(baseline);
  const targetY   = target != null ? y(target) : null;

  return (
    <div style={{ background: 'var(--ink-25)', border: '1px solid var(--border-faint)', borderRadius: 'var(--radius-1)', padding: 8 }}>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" height={H} style={{ display: 'block' }}>
        {/* Baseline line */}
        <line x1={P} x2={W - P} y1={baselineY} y2={baselineY} stroke="var(--border-strong)" strokeDasharray="3 3" />
        <text x={W - P + 2} y={baselineY + 4} fontFamily="var(--font-mono)" fontSize="9" fill="var(--fg-subtle)" textAnchor="end" dx={-4}>baseline</text>
        {/* Target line */}
        {targetY != null && (
          <>
            <line x1={P} x2={W - P} y1={targetY} y2={targetY} stroke="var(--success)" strokeDasharray="4 2" opacity="0.6" />
            <text x={W - P + 2} y={targetY - 4} fontFamily="var(--font-mono)" fontSize="9" fill="var(--success)" textAnchor="end" dx={-4}>mål</text>
          </>
        )}
        {/* Path */}
        <path d={path} fill="none" stroke="var(--signal)" strokeWidth="2" strokeLinejoin="round" strokeLinecap="round" />
        {/* Points */}
        {sorted.map((a, i) => (
          <g key={a.id}>
            <circle cx={x(a.date.getTime())} cy={y(a.value)} r="4" fill="#fff" stroke="var(--signal)" strokeWidth="2" />
            {i === sorted.length - 1 && (
              <text x={x(a.date.getTime())} y={y(a.value) - 10} fontFamily="var(--font-mono)" fontSize="10" fontWeight="500" fill="var(--fg)" textAnchor="middle">
                {fmtNumber(a.value, test)}
              </text>
            )}
          </g>
        ))}
      </svg>
    </div>
  );
}

// ============================================================================
// Create challenge modal — pick test (or create new) + baseline + target
// ============================================================================
function CreateChallengeModal({ memberId, existingTestIds, onClose, onCreated }) {
  const [step, setStep] = useState('pick');  // 'pick' | 'configure' | 'newTest'
  const [pickedTestId, setPickedTestId] = useState(null);
  const [baseline, setBaseline] = useState('');
  const [target, setTarget] = useState('');

  // New-test form state
  const [tName, setTName] = useState('');
  const [tUnit, setTUnit] = useState('');
  const [tDir, setTDir] = useState('higher');
  const [tCat, setTCat] = useState('Övrigt');

  const test = pickedTestId ? getTest(pickedTestId) : null;
  const availableTests = TESTS.filter(t => !t.archived);

  const onPickTest = (id) => {
    setPickedTestId(id);
    const t = getTest(id);
    setBaseline('');
    setTarget('');
    setStep('configure');
  };

  const onBaselineChange = (v) => {
    setBaseline(v);
    if (v !== '' && !isNaN(Number(v)) && test) {
      const sug = suggestedTarget(test.id, Number(v));
      if (sug != null && target === '') setTarget(sug);
    }
  };

  const submitNewTest = () => {
    if (!tName.trim() || !tUnit.trim()) return;
    const t = createTest({ name: tName.trim(), unit: tUnit.trim(), dir: tDir, cat: tCat.trim() || 'Övrigt' });
    setPickedTestId(t.id);
    setStep('configure');
  };

  const submit = () => {
    if (baseline === '' || target === '' || !pickedTestId) return;
    createChallenge({
      memberId, testId: pickedTestId,
      baseline: Number(baseline),
      target: Number(target),
    });
    onCreated();
  };

  return (
    <Modal onClose={onClose} maxWidth={560}>
      <Eyebrow style={{ marginBottom: 8 }}>Ny utmaning</Eyebrow>
      <h2 style={{ margin: 0, fontSize: 22, fontWeight: 500, letterSpacing: '-0.015em' }}>
        {step === 'newTest' ? 'Nytt test' : 'Skapa utmaning'}
      </h2>

      {step === 'pick' && (
        <div style={{ marginTop: 18 }}>
          <Eyebrow style={{ marginBottom: 10 }}>Välj test</Eyebrow>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, maxHeight: 360, overflow: 'auto' }}>
            {availableTests.map(t => {
              const dup = existingTestIds.includes(t.id);
              return (
                <button
                  key={t.id}
                  disabled={dup}
                  onClick={() => onPickTest(t.id)}
                  title={dup ? 'Du har redan en aktiv utmaning för det här testet' : ''}
                  style={{
                    textAlign: 'left', padding: 12,
                    background: 'var(--bg-raised)', border: '1px solid var(--border)', borderRadius: 6,
                    cursor: dup ? 'not-allowed' : 'pointer', opacity: dup ? 0.4 : 1,
                  }}
                  onMouseEnter={e => { if (!dup) e.currentTarget.style.borderColor = 'var(--signal)'; }}
                  onMouseLeave={e => { e.currentTarget.style.borderColor = 'var(--border)'; }}
                >
                  <div style={{ fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '0.10em', textTransform: 'uppercase', color: 'var(--fg-subtle)' }}>{t.cat}</div>
                  <div style={{ fontSize: 14, fontWeight: 500, marginTop: 4 }}>{t.name}</div>
                  <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--fg-subtle)', marginTop: 2 }}>
                    {t.unit} · {t.dir === 'lower' ? 'lägre = bättre' : 'högre = bättre'}
                  </div>
                  {dup && <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--fg-subtle)', marginTop: 4 }}>redan aktiv</div>}
                </button>
              );
            })}
          </div>
          <div style={{ marginTop: 14, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <Button kind="ghost" onClick={() => setStep('newTest')}>+ Nytt test</Button>
            <Button kind="secondary" onClick={onClose}>Avbryt</Button>
          </div>
        </div>
      )}

      {step === 'configure' && test && (
        <div style={{ marginTop: 18 }}>
          <div style={{ display: 'flex', gap: 10, alignItems: 'center', marginBottom: 18 }}>
            <Pill bg="var(--signal)" color="#fff">{test.cat}</Pill>
            <div style={{ fontSize: 16, fontWeight: 500 }}>{test.name}</div>
            <button onClick={() => setStep('pick')} style={{ marginLeft: 'auto', background: 'none', border: 'none', color: 'var(--fg-muted)', fontFamily: 'var(--font-mono)', fontSize: 11, cursor: 'pointer' }}>byt test</button>
          </div>

          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 16 }}>
            <Field label={`Baseline idag (${test.unit})`}>
              <Input value={baseline} onChange={onBaselineChange} type="number" placeholder={test.dir === 'lower' ? 't.ex. 2.04' : 't.ex. 2'} suffix={test.unit} />
            </Field>
            <Field label={`Mål till säsongens slut (${test.unit})`}>
              <Input value={target} onChange={setTarget} type="number" placeholder="—" suffix={test.unit} />
            </Field>
          </div>
          {baseline !== '' && target === '' && (
            <div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--fg-subtle)', marginBottom: 8 }}>
              Förslag: {suggestedTarget(test.id, Number(baseline)) ?? '—'} {test.unit}
            </div>
          )}
          <div style={{ fontSize: 12, color: 'var(--fg-muted)', marginBottom: 18 }}>
            Utmaningen löper till säsongens slut (Sommar 2026). Du kan logga försök längs vägen och se kurvan mot målet.
          </div>

          <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 10 }}>
            <Button kind="secondary" onClick={onClose}>Avbryt</Button>
            <Button onClick={submit} disabled={baseline === '' || target === ''}>Skapa utmaning</Button>
          </div>
        </div>
      )}

      {step === 'newTest' && (
        <div style={{ marginTop: 18 }}>
          <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 12, marginBottom: 12 }}>
            <Field label="Namn"><Input value={tName} onChange={setTName} placeholder="t.ex. Stänga ögonen-balans" /></Field>
            <Field label="Enhet"><Input value={tUnit} onChange={setTUnit} placeholder="sek, antal, m..." /></Field>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, marginBottom: 16 }}>
            <Field label="Kategori"><Input value={tCat} onChange={setTCat} placeholder="t.ex. Balans" /></Field>
            <Field label="Riktning">
              <select
                value={tDir}
                onChange={e => setTDir(e.target.value)}
                style={{
                  fontFamily: 'var(--font-mono)', fontSize: 13, color: 'var(--fg)',
                  padding: '8px 10px', border: '1px solid var(--border-strong)', borderRadius: 4,
                  background: 'var(--bg-raised)', width: '100%',
                }}
              >
                <option value="higher">högre = bättre</option>
                <option value="lower">lägre = bättre</option>
              </select>
            </Field>
          </div>
          <div style={{ display: 'flex', justifyContent: 'space-between' }}>
            <Button kind="ghost" onClick={() => setStep('pick')}>← Tillbaka</Button>
            <Button onClick={submitNewTest} disabled={!tName.trim() || !tUnit.trim()}>Skapa test + fortsätt</Button>
          </div>
        </div>
      )}
    </Modal>
  );
}

// ============================================================================
// Empty state
// ============================================================================
function EmptyState({ onCreate }) {
  return (
    <Card pad={32} style={{ textAlign: 'center' }}>
      <div style={{ fontSize: 16, fontWeight: 500, marginBottom: 6 }}>Inga aktiva utmaningar än</div>
      <div style={{ fontSize: 13, color: 'var(--fg-muted)', maxWidth: 360, margin: '0 auto 16px' }}>
        En utmaning är ett mål du sätter idag med en deadline vid säsongens slut. Skapa din första.
      </div>
      <Button kind="primary" onClick={onCreate}>+ Skapa utmaning</Button>
    </Card>
  );
}

// ============================================================================
// Field — labeled input wrapper
// ============================================================================
function Field({ label, children }) {
  return (
    <div>
      <Eyebrow style={{ marginBottom: 6 }}>{label}</Eyebrow>
      {children}
    </div>
  );
}

// ============================================================================
// Modal — overlay + centered card
// ============================================================================
function Modal({ children, onClose, maxWidth = 560 }) {
  return (
    <div
      onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}
      style={{
        position: 'fixed', inset: 0, background: 'rgba(20,22,24,0.45)',
        display: 'flex', alignItems: 'flex-start', justifyContent: 'center',
        zIndex: 1000, padding: '6vh 16px', overflowY: 'auto',
      }}
    >
      <div style={{
        width: '100%', maxWidth, background: 'var(--bg-raised)',
        border: '1px solid var(--border)', borderRadius: 'var(--radius-2)',
        padding: 24, boxShadow: '0 24px 60px -20px rgba(0,0,0,0.30)',
      }}>
        {children}
      </div>
    </div>
  );
}

// ============================================================================
// Principle card (kept from original)
// ============================================================================
function PrincipleCard({ title, body }) {
  return (
    <Card pad={20}>
      <Eyebrow>{title}</Eyebrow>
      <p style={{ margin: '8px 0 0', fontSize: 13, color: 'var(--fg-muted)', lineHeight: 1.55 }}>{body}</p>
    </Card>
  );
}

// ============================================================================
// Helpers
// ============================================================================
function fmtNumber(v, test) {
  if (v == null || isNaN(v)) return '—';
  if (!test) return String(v);
  if (test.unit === 'antal' || test.unit === 'nivå' || test.unit === 'poäng') return String(Math.round(v));
  return String(Math.round(v * 100) / 100);
}

window.TesterScreen = TesterScreen;
