// fc-reveal-poster.jsx — three distinct treatments of the "Your camp" reveal,
// shown as separate artboards so they can be compared. They share the same engine
// content but differ deliberately in HIERARCHY, CARD treatment, COLOUR and INFO
// density (best-practice divergent directions):
//
//   1. Editorial (copy-led)   — RevealPosterPlus  · coach read leads, white cards,
//                               fullest info (explorable sharpen cards)
//   2. Dashboard (number-led) — RevealPosterData  · make-weight number + metric grid
//                               lead, filled cards, condensed sharpen rows
//   3. Checklist (plan-led)   — RevealPosterPlan  · clay number hero, the ticked plan
//                               leads, clay cards, minimal sharpen rows
//
// All three carry the same point and PROVE it visually: we read the trend under the
// daily noise. PosterGlide draws the jagged daily line + a water-spike callout that
// shows the trend (and the plan) never moved.
//
// Reuses fc-reveal-lab helpers via window (separate babel scope).

const CLAY = '#e9ddcd';

// ── the proof chart ──────────────────────────────────────────────────────────
// faint jagged DAILY line + dots (the noise) under one bold smoothed TREND line,
// with a callout on the biggest upward spike: "+1.1 kg water — trend held". The
// whole point of the app, shown not claimed.
function PosterGlide({ c, width = 349, height = 196, proof = true }) {
  const d = c.d;
  const W = width, H = height, padL = 16, padR = 16, padT = 46, padB = 30;
  const innerW = W - padL - padR, innerH = H - padT - padB;
  const drop = (d.startW - d.targetW) || 1;
  const x = (t) => padL + t * innerW;
  const y = (v) => padT + ((d.startW - v) / drop) * innerH;
  const sx = x(0), sy = y(d.startW), ex = x(1), ey = y(d.targetW);
  const trendD = `M ${sx} ${sy} L ${ex} ${ey}`;
  const areaD = `${trendD} L ${ex} ${padT + innerH} L ${sx} ${padT + innerH} Z`;
  // deterministic daily jitter around the trend (in trend-fraction units)
  const jit = [0.5, -0.7, 0.4, -1.45, 0.6, -0.4, 0.9, -0.6, 0.5, -0.9, 0.7, -0.5, 0.8, -0.6, 0.4, -0.5, 0.7, -0.45];
  const n = jit.length;
  const amp = innerH * 0.13;
  const pts = jit.map((m, i) => {
    const t = i / (n - 1);
    const ty = sy + (ey - sy) * t;
    return { x: x(t), y: ty + m * amp, ty, m };
  });
  const dailyD = pts.map((p, i) => `${i ? 'L' : 'M'} ${p.x.toFixed(1)} ${p.y.toFixed(1)}`).join(' ');
  // spike = biggest UPWARD deviation (most-negative m → smallest y → highest weight)
  let spikeI = 0; pts.forEach((p, i) => { if (p.m < pts[spikeI].m) spikeI = i; });
  const sp = pts[spikeI];
  const spikeKg = (Math.abs(sp.m) * 0.85).toFixed(1);

  return (
    <svg width={W} height={H} viewBox={`0 0 ${W} ${H}`} style={{ display: 'block' }} role="img" aria-label={`Daily weigh-ins jump around, but the trend descends smoothly from ${d.startW} to ${d.targetW} kg`}>
      <defs><linearGradient id="pgFill" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stopColor="var(--accent)" stopOpacity="0.13" /><stop offset="1" stopColor="var(--accent)" stopOpacity="0" /></linearGradient></defs>
      {/* make-weight zone */}
      <rect x={padL} y={ey} width={innerW} height={padT + innerH - ey} fill="var(--accent)" opacity="0.06" />
      <line x1={padL} x2={W - padR} y1={ey} y2={ey} stroke="var(--accent)" strokeWidth="1" strokeDasharray="3 3" opacity="0.5" />
      <path d={areaD} fill="url(#pgFill)" />
      {/* daily = the noise: jagged line + dots */}
      <path d={dailyD} fill="none" stroke="var(--ink-3)" strokeWidth="1.3" strokeLinejoin="round" strokeLinecap="round" opacity="0.34" />
      {pts.map((p, i) => <circle key={i} cx={p.x} cy={p.y} r={i === spikeI && proof ? 3.4 : 2.2} fill={i === spikeI && proof ? 'var(--ink)' : 'var(--ink-3)'} opacity={i === spikeI && proof ? 0.9 : 0.42} />)}
      {/* the trend we read through it */}
      <path d={trendD} fill="none" stroke="var(--accent)" strokeWidth="2.8" strokeLinecap="round" />
      <circle cx={sx} cy={sy} r="4.5" fill="var(--ink)" />
      <circle cx={ex} cy={ey} r="5.5" fill="var(--accent)" stroke="var(--paper)" strokeWidth="2" />
      <text x={sx} y={sy - 22} textAnchor="start" fontFamily="var(--mono)" fontSize="8" letterSpacing="0.08em" fill="var(--ink-3)" fontWeight="700">TODAY</text>
      <text x={sx} y={sy - 10} textAnchor="start" fontFamily="var(--num)" fontSize="14" fontWeight="500" fill="var(--ink)">{d.startW.toFixed(1)}</text>
      <text x={ex} y={ey + 20} textAnchor="end" fontFamily="var(--mono)" fontSize="8" letterSpacing="0.08em" fill="var(--accent)" fontWeight="700">MAKE-WEIGHT</text>
      <text x={ex} y={ey - 12} textAnchor="end" fontFamily="var(--num)" fontSize="14" fontWeight="600" fill="var(--accent)">{d.targetW.toFixed(1)}</text>
      {/* PROOF callout — the scary daily spike, and the trend that ignored it */}
      {proof && (
        <g>
          <line x1={sp.x} y1={sp.y} x2={sp.x} y2={sp.ty} stroke="var(--ink)" strokeWidth="1" strokeDasharray="2 2" opacity="0.55" />
          <circle cx={sp.x} cy={sp.ty} r="3" fill="none" stroke="var(--accent)" strokeWidth="1.6" />
          <text x={sp.x + 9} y={sp.y - 6} textAnchor="start" fontFamily="var(--num)" fontSize="13" fontWeight="600" fill="var(--ink)">+{spikeKg} kg</text>
          <text x={sp.x + 9} y={sp.y + 6} textAnchor="start" fontFamily="var(--mono)" fontSize="7.5" letterSpacing="0.06em" fill="var(--ink-3)" fontWeight="700">WATER · TREND HELD</text>
        </g>
      )}
    </svg>
  );
}

// trend legend row — shared
function TrendLegend() {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
      <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
        <span style={{ width: 14, height: 0, borderTop: '1.3px solid var(--ink-3)', opacity: 0.5 }} />
        <span style={{ fontFamily: 'var(--mono)', fontSize: 9, letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--ink-3)', fontWeight: 600 }}>Daily</span>
      </span>
      <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
        <span style={{ width: 16, height: 3, borderRadius: 2, background: 'var(--accent)' }} />
        <span style={{ fontFamily: 'var(--mono)', fontSize: 9, letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--accent)', fontWeight: 700 }}>Trend</span>
      </span>
    </div>
  );
}

// verdict chip for the cut-strategy explainer (Use / Cap / Skip)
function Verdict({ tone, children }) {
  const map = {
    green: { fg: 'var(--green)', bg: 'var(--green-soft)' },
    amber: { fg: 'var(--amber)', bg: 'var(--amber-soft)' },
    red:   { fg: 'var(--accent)', bg: 'var(--accent-soft)' },
  }[tone] || {};
  return (
    <span style={{ flexShrink: 0, fontFamily: 'var(--mono)', fontSize: 9, letterSpacing: '0.06em', textTransform: 'uppercase', fontWeight: 700, color: map.fg, background: map.bg, borderRadius: 100, padding: '3px 8px' }}>{children}</span>
  );
}

// ── shared engine read for all three treatments ──────────────────────────────
function usePosterData() {
  const c = window.useCampData(); const d = c.d; const S = c.S;
  const locked = [
    { t: 'Sport & weigh-in rules', s: `${d.sport} · ${c.weighShort}` },
    { t: 'Class limit & body target', s: `${d.targetW.toFixed(1)} kg${c.equip ? ` · ${S.equipmentKg} kg gear` : ''}` },
    { t: 'Cut size & timeline', s: `${d.cut.toFixed(1)} kg over ${d.weeks} wks` },
    { t: 'Safe cut pace', s: `${d.ratePct.toFixed(2)}%/wk` },
  ];
  const strats = [
    { t: 'Low-fibre day', v: 'Use', tone: 'green', s: 'Clears the gut — about 1% of scale weight.' },
    { t: 'Low sodium', v: 'Use', tone: 'green', s: '2–3 days drops ~0.5–1% in water.' },
    { t: 'Sweat / sauna', v: 'Cap', tone: 'amber', s: 'Never past 5% of bodyweight — inside your rehydration window.' },
    { t: 'Low carb', v: 'Skip', tone: 'red', s: 'Glycogen is your engine; 24h to reload. Not worth it.' },
    { t: 'Water load', v: 'Skip', tone: 'red', s: 'Hard to time, easy to get wrong. We leave it off.' },
  ];
  const stratIntro = d.water
    ? `Your ${c.weighShort} weigh-in leaves time to rebuild, so a measured water taper is on the table. We've sorted what's worth doing from what isn't:`
    : `Your ${c.weighShort} weigh-in means no rehydration window — the cut is all real weight, so water tricks are off the table:`;
  const compRules = [
    { k: 'Gear allowance', v: c.equip ? `+${S.equipmentKg} kg · weigh in your ${d.sport === 'Judo' || d.sport.startsWith('BJJ') ? 'gi' : 'kit'}` : 'None — underwear only' },
    { k: 'Re-weigh cap', v: S.reweigh ? `≤ +${S.reweighPct}% of the limit` : 'No second weigh-in' },
    { k: 'Hydration check', v: S.reweigh ? 'Random morning control' : 'Not used in your sport' },
    { k: 'Weigh-in timing', v: c.weighShort },
  ];
  const timeStr = c.FC.weighInTime || d.weighInTime || '6:50 AM';
  const total = locked.length + 4;
  return { c, d, S, locked, strats, stratIntro, compRules, timeStr, total };
}

// ── shared sharpen-lever bodies (the "intelligence we already have") ──────────
function StrategyBody({ strats, intro }) {
  return (
    <React.Fragment>
      <div style={{ fontFamily: 'var(--sans)', fontSize: 12.5, lineHeight: 1.5, color: 'var(--ink-2)', textWrap: 'pretty' }}>{intro}</div>
      <div style={{ marginTop: 12, display: 'flex', flexDirection: 'column', gap: 11 }}>
        {strats.map((s, i) => (
          <div key={i} style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}>
            <Verdict tone={s.tone}>{s.v}</Verdict>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontFamily: 'var(--sans)', fontSize: 13, fontWeight: 600, color: 'var(--ink)' }}>{s.t}</div>
              <div style={{ fontFamily: 'var(--sans)', fontSize: 12, color: 'var(--ink-3)', marginTop: 1, lineHeight: 1.4 }}>{s.s}</div>
            </div>
          </div>
        ))}
      </div>
    </React.Fragment>
  );
}
function CompRulesBody({ compRules }) {
  return (
    <React.Fragment>
      <div style={{ fontFamily: 'var(--sans)', fontSize: 12.5, lineHeight: 1.5, color: 'var(--ink-2)', textWrap: 'pretty' }}>We keep your sport’s rules loaded, so every target you see is already legal for your division:</div>
      <div style={{ marginTop: 12 }}>
        {compRules.map((r, i) => (
          <div key={i} style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 12, padding: '8px 0', borderTop: i ? '1px dotted var(--rule-2)' : 'none' }}>
            <span style={{ flexShrink: 0, fontFamily: 'var(--mono)', fontSize: 10, letterSpacing: '0.04em', textTransform: 'uppercase', color: 'var(--ink-3)', fontWeight: 600 }}>{r.k}</span>
            <span style={{ fontFamily: 'var(--sans)', fontSize: 13, fontWeight: 600, color: 'var(--ink)', textAlign: 'right' }}>{r.v}</span>
          </div>
        ))}
      </div>
      <div style={{ marginTop: 11, fontFamily: 'var(--sans)', fontSize: 11.5, fontStyle: 'italic', color: 'var(--ink-3)', lineHeight: 1.45 }}>Confirm your event’s exact caps in setup — gear &amp; re-weigh can vary by promotion.</div>
    </React.Fragment>
  );
}
function CycleBody() {
  return (
    <React.Fragment>
      <div style={{ fontFamily: 'var(--sans)', fontSize: 12.5, lineHeight: 1.5, color: 'var(--ink-2)', textWrap: 'pretty' }}>If you track your cycle, we subtract the water your body holds in the late-luteal phase — roughly <strong style={{ fontWeight: 600 }}>0.5–1 kg</strong> — before we judge your pace. A normal hormonal swing never gets read as “off track,” so your weekly verdict stays honest across the month.</div>
      <div style={{ marginTop: 10, fontFamily: 'var(--mono)', fontSize: 9, letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--ink-3)', fontWeight: 700 }}>Turns on if you choose to track · optional</div>
    </React.Fragment>
  );
}
function WeighinBody({ timeStr }) {
  return (
    <React.Fragment>
      <div style={{ fontFamily: 'var(--sans)', fontSize: 12.5, lineHeight: 1.5, color: 'var(--ink-2)', textWrap: 'pretty' }}>Pick your wake-up window and we lock your daily fasted weigh-in to it — same time, one tap, with a nudge if you miss it. That single morning reading is what feeds the trend, so the smoothing stays clean.</div>
      <div style={{ marginTop: 10, fontFamily: 'var(--mono)', fontSize: 9, letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--ink-3)', fontWeight: 700 }}>Default {timeStr} · change anytime</div>
    </React.Fragment>
  );
}
// the 4 lever descriptors (title + summary + body), shared across treatments
function buildLevers(data) {
  const { d, strats, stratIntro, compRules, timeStr } = data;
  return [
    { key: 'strategy', title: 'Recommended cut strategy', summary: d.water ? 'A measured water taper — and what to avoid' : 'All real weight — water tricks ruled out', body: <StrategyBody strats={strats} intro={stratIntro} /> },
    { key: 'rules', title: 'Competition rules', summary: 'Your promotion’s rulebook, already loaded', body: <CompRulesBody compRules={compRules} /> },
    { key: 'cycle', title: 'Cycle-aware targets', summary: 'A hormonal water swing never reads as off-track', body: <CycleBody /> },
    { key: 'weighin', title: 'Weigh-in time & reminders', summary: 'One fasted reading a morning, locked to your wake-up', body: <WeighinBody timeStr={timeStr} /> },
  ];
}

// expand/collapse chevron
function Chevron({ open }) {
  return (
    <svg width="13" height="13" viewBox="0 0 14 14" fill="none" style={{ flexShrink: 0, transform: open ? 'rotate(180deg)' : 'none', transition: 'transform 0.18s ease' }}>
      <path d="M3 5 L7 9 L11 5" stroke="var(--ink-3)" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

// the ticked-off "Your plan" locked list — shared, optional progress bar
function PlanTicks({ locked, total, tinted }) {
  const ProgressBar = window.ProgressBar;
  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 10 }}>
        <span style={{ fontFamily: 'var(--sans)', fontSize: 15, fontWeight: 700, color: 'var(--ink)' }}>Your plan</span>
        <span style={{ fontFamily: 'var(--sans)', fontSize: 12.5, fontWeight: 600, color: 'var(--ink-2)' }}><span style={{ color: 'var(--ink)', fontWeight: 700 }}>{locked.length}</span> of {total} set</span>
      </div>
      <div style={{ marginTop: 11, marginBottom: 4 }}>{ProgressBar && <ProgressBar done={locked.length} total={total} />}</div>
      <div>
        {locked.map((it, i) => (
          <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 11, padding: '9px 0', borderTop: i ? '1px solid var(--rule)' : 'none' }}>
            <span style={{ flexShrink: 0, width: 18, height: 18, borderRadius: '50%', background: 'var(--green-soft)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><svg width="10" height="8" viewBox="0 0 11 9" fill="none"><path d="M1 4.5 L4 7.5 L10 1" stroke="var(--green)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg></span>
            <span style={{ flex: 1, minWidth: 0, fontFamily: 'var(--sans)', fontSize: 14, fontWeight: 600, color: 'var(--ink)' }}>{it.t}</span>
            <span style={{ flexShrink: 0, fontFamily: 'var(--sans)', fontSize: 12, color: 'var(--ink-3)', textAlign: 'right', maxWidth: '46%' }}>{it.s}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// the clay matchup masthead — shared, copy or number hero
function ClayHead({ d, c, hero }) {
  return (
    <div style={{ background: CLAY, color: 'var(--ink)', padding: hero === 'number' ? '22px 24px 26px' : '24px 24px 26px' }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10 }}>
        <span style={{ fontFamily: 'var(--display)', fontSize: 12, letterSpacing: '0.2em', textTransform: 'uppercase', color: 'var(--accent)', fontWeight: 600 }}>{d.firstName ? `${d.firstName}’s camp` : 'Your camp'}</span>
        <div style={{ display: 'flex', alignItems: 'center', gap: 9 }}>
          <span style={{ fontFamily: 'var(--mono)', fontSize: 9.5, letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--ink-3)', fontWeight: 600 }}>{d.weeks} wks out</span>
          {c.glyph && window.SportGlyph && <window.SportGlyph name={c.glyph} size={28} ink="var(--ink)" accent="var(--accent)" />}
        </div>
      </div>
      {hero === 'number' ? (
        <React.Fragment>
          <div style={{ fontFamily: 'var(--display)', fontSize: 22, letterSpacing: '0.02em', fontWeight: 600, color: 'var(--ink)', marginTop: 13 }}>{(d.division || d.sport).toUpperCase()} · {d.sport.toUpperCase()}</div>
          <div style={{ textAlign: 'center', marginTop: 14 }}>
            <div style={{ fontFamily: 'var(--sans)', fontSize: 13, fontWeight: 600, color: 'var(--ink-3)' }}>{d.startW.toFixed(1)} → {d.targetW.toFixed(1)} kg make-weight</div>
            <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'center', gap: 9, marginTop: 4 }}>
              <span style={{ fontFamily: 'var(--num)', fontWeight: 200, fontSize: 88, lineHeight: 0.9, letterSpacing: '-0.04em', color: 'var(--ink)' }}>−{d.cut.toFixed(1)}</span>
              <span style={{ fontFamily: 'var(--display)', fontSize: 20, letterSpacing: '0.06em', fontWeight: 600, color: 'var(--ink-3)' }}>KG</span>
            </div>
          </div>
        </React.Fragment>
      ) : (
        <React.Fragment>
          <div style={{ fontFamily: 'var(--display)', fontSize: 30, letterSpacing: '0.01em', fontWeight: 600, lineHeight: 1.0, color: 'var(--ink)', marginTop: 15 }}>{(d.division || d.sport).toUpperCase()} · {d.sport.toUpperCase()}</div>
          <div style={{ height: 3, width: 58, background: 'var(--accent)', margin: '13px 0 15px' }} />
          <div style={{ fontFamily: 'var(--sans)', fontSize: 18, fontWeight: 700, lineHeight: 1.28, color: 'var(--ink)', textWrap: 'balance' }}>{c.tip.headline}</div>
          <div style={{ fontFamily: 'var(--sans)', fontSize: 13.5, lineHeight: 1.55, color: 'var(--ink-2)', marginTop: 9, textWrap: 'pretty' }}>{c.tip.body}</div>
        </React.Fragment>
      )}
    </div>
  );
}

// ═════════════════════════════════════════════════════════════════════════════
// 1 · EDITORIAL (copy-led) — coach read is the hero; white explorable sharpen
// cards; fullest information. The make-weight readout sits bare above the proof
// chart, and the trend point gets its own clear lead + proof.
// ═════════════════════════════════════════════════════════════════════════════
function RevealPosterPlus() {
  const data = usePosterData(); const { c, d, locked, total } = data;
  const [openKey, setOpenKey] = React.useState('strategy');
  if (!c.tip) return null;
  const Num = window.Num, ToneBadge = window.ToneBadge, Kicker = window.Kicker;
  const levers = buildLevers(data);

  return (
    <window.RevealFrame label="Reveal · Editorial" footer={<window.PrimaryNext sub="Your camp is ready — yours to keep." />}>
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
        <ClayHead d={d} c={c} hero="copy" />
        <div style={{ padding: '20px 22px 24px', display: 'flex', flexDirection: 'column', gap: 22 }}>

          {/* make-weight readout (bare) */}
          <div>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
              <span style={{ fontFamily: 'var(--mono)', fontSize: 10.5, letterSpacing: '0.18em', color: 'var(--ink-3)', fontWeight: 600 }}>MAKE-WEIGHT TARGET</span>
              {ToneBadge ? <ToneBadge tone="green">SAFE PACE</ToneBadge> : null}
            </div>
            <div style={{ display: 'flex', alignItems: 'flex-end', gap: 12, marginTop: 12 }}>
              {Num ? <Num size={72} weight={300} color="var(--ink)">{d.targetW.toFixed(1)}</Num> : <span style={{ fontFamily: 'var(--num)', fontWeight: 300, fontSize: 72 }}>{d.targetW.toFixed(1)}</span>}
              <div style={{ paddingBottom: 9 }}>
                <div style={{ fontFamily: 'var(--mono)', fontSize: 13, color: 'var(--ink-3)', fontWeight: 600 }}>KG</div>
                <div style={{ fontFamily: 'var(--mono)', fontSize: 12, color: 'var(--accent)', fontWeight: 700, marginTop: 5 }}>−{d.cut.toFixed(1)} FROM TODAY</div>
              </div>
            </div>
          </div>

          {/* THE POINT — proven. clear lead + annotated chart + caption */}
          <div style={{ borderTop: '1px solid var(--rule)', paddingTop: 18 }}>
            <Kicker color="var(--accent)">Why you can trust the number</Kicker>
            <div style={{ fontFamily: 'var(--sans)', fontSize: 19, fontWeight: 700, lineHeight: 1.22, letterSpacing: '-0.01em', color: 'var(--ink)', marginTop: 9, textWrap: 'balance' }}>We read the trend, not the noise.</div>
            <div style={{ marginTop: 14 }}><PosterGlide c={c} width={349} height={196} /></div>
            <div style={{ marginTop: 12, display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
              <TrendLegend />
            </div>
            <div style={{ fontFamily: 'var(--sans)', fontSize: 13, color: 'var(--ink-2)', marginTop: 11, lineHeight: 1.5, textWrap: 'pretty' }}>That Saturday spike was water — up over a kilo. Your trend never moved, so your plan didn’t either. <strong style={{ fontWeight: 600 }}>You train; we do the weight math</strong> and flag you only when it’s real.</div>
          </div>

          {/* plan ticks */}
          <PlanTicks locked={locked} total={total} />

          {/* sharpen — white explorable cards (fullest info) */}
          <div>
            <Kicker color="var(--accent)">Sharpens once you set up camp</Kicker>
            <div style={{ fontFamily: 'var(--sans)', fontSize: 12.5, color: 'var(--ink-3)', marginTop: 7, lineHeight: 1.45, textWrap: 'pretty' }}>Four levers personalise your cut in setup — the engine already has a head start on each. Tap to see what it knows.</div>
            <div style={{ marginTop: 14, display: 'flex', flexDirection: 'column', gap: 11 }}>
              {levers.map((lv) => {
                const open = openKey === lv.key;
                return (
                  <div key={lv.key} style={{ background: 'var(--paper)', border: '1px solid var(--rule)', borderRadius: 'var(--radius)', overflow: 'hidden' }}>
                    <button onClick={() => setOpenKey(open ? null : lv.key)} style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 12, padding: '15px 16px', textAlign: 'left' }}>
                      <span style={{ flexShrink: 0, width: 18, height: 18, borderRadius: '50%', border: '1.5px solid var(--rule-2)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{open && <span style={{ width: 7, height: 7, borderRadius: '50%', background: 'var(--accent)' }} />}</span>
                      <span style={{ flex: 1, minWidth: 0 }}>
                        <span style={{ display: 'block', fontFamily: 'var(--sans)', fontSize: 14.5, fontWeight: 600, color: 'var(--ink)' }}>{lv.title}</span>
                        <span style={{ display: 'block', fontFamily: 'var(--sans)', fontSize: 12.5, color: 'var(--ink-3)', marginTop: 2, lineHeight: 1.4 }}>{lv.summary}</span>
                      </span>
                      <Chevron open={open} />
                    </button>
                    {open && <div style={{ padding: '0 16px 16px 46px' }}><div style={{ borderTop: '1px solid var(--rule)', paddingTop: 13 }}>{lv.body}</div></div>}
                  </div>
                );
              })}
            </div>
          </div>
        </div>
      </div>
    </window.RevealFrame>
  );
}

// ═════════════════════════════════════════════════════════════════════════════
// 2 · DASHBOARD (number-led) — the make-weight number + a metric grid lead; the
// proof chart is a contained card; the plan is one condensed line; sharpen levers
// are tight expandable rows in a single filled container. Data-forward, condensed.
// ═════════════════════════════════════════════════════════════════════════════
function RevealPosterData() {
  const data = usePosterData(); const { c, d, locked, total } = data;
  const [openKey, setOpenKey] = React.useState(null);
  if (!c.tip) return null;
  const Num = window.Num, ToneBadge = window.ToneBadge, Kicker = window.Kicker;
  const levers = buildLevers(data);
  const metrics = [
    { k: 'TO LOSE', v: '−' + d.cut.toFixed(1), u: 'kg', t: 'var(--accent)' },
    { k: 'SAFE PACE', v: d.ratePct.toFixed(2), u: '%/wk', t: 'var(--green)' },
    { k: 'TIMELINE', v: String(d.weeks), u: 'wks', t: 'var(--ink)' },
    { k: d.water ? 'REAL-WEIGHT BASE' : 'ALL REAL WEIGHT', v: (d.water ? d.campKg : d.cut).toFixed(1), u: 'kg', t: 'var(--ink)' },
  ];
  return (
    <window.RevealFrame label="Reveal · Dashboard" footer={<window.PrimaryNext sub="Your camp is ready — yours to keep." />}>
      <div style={{ padding: '8px 18px 24px', display: 'flex', flexDirection: 'column', gap: 14 }}>
        {/* status strip */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '4px 2px 2px' }}>
          <span style={{ flexShrink: 0, width: 38, height: 38, borderRadius: '50%', background: 'var(--green-soft)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><svg width="16" height="12" viewBox="0 0 16 12" fill="none"><path d="M1.5 6.5 L6 11 L14.5 1.5" stroke="var(--green)" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/></svg></span>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontFamily: 'var(--sans)', fontSize: 16, fontWeight: 700, color: 'var(--ink)', letterSpacing: '-0.01em' }}>Your camp is ready.</div>
            <div style={{ fontFamily: 'var(--sans)', fontSize: 12.5, color: 'var(--ink-3)' }}>{d.firstName ? d.firstName + ' · ' : ''}{d.sport} · {(d.division || d.sport)} · {d.weeks} wks out</div>
          </div>
        </div>
        {/* number hero card */}
        <div style={{ background: 'var(--paper)', border: '1px solid var(--rule)', borderRadius: 'var(--radius)', padding: 18 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <span style={{ fontFamily: 'var(--mono)', fontSize: 10.5, letterSpacing: '0.18em', color: 'var(--ink-3)', fontWeight: 600 }}>MAKE-WEIGHT TARGET</span>
            {ToneBadge ? <ToneBadge tone="green">SAFE PACE</ToneBadge> : null}
          </div>
          <div style={{ display: 'flex', alignItems: 'flex-end', gap: 12, marginTop: 10 }}>
            {Num ? <Num size={76} weight={200} color="var(--ink)">{d.targetW.toFixed(1)}</Num> : <span style={{ fontFamily: 'var(--num)', fontWeight: 200, fontSize: 76 }}>{d.targetW.toFixed(1)}</span>}
            <div style={{ paddingBottom: 10 }}>
              <div style={{ fontFamily: 'var(--mono)', fontSize: 13, color: 'var(--ink-3)', fontWeight: 600 }}>KG</div>
              <div style={{ fontFamily: 'var(--mono)', fontSize: 12, color: 'var(--accent)', fontWeight: 700, marginTop: 5 }}>−{d.cut.toFixed(1)} FROM TODAY</div>
            </div>
          </div>
        </div>
        {/* metric grid — filled cards */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
          {metrics.map(m => (
            <div key={m.k} style={{ background: 'var(--paper-2)', border: '1px solid var(--rule)', borderRadius: 'var(--radius)', padding: '14px 15px', position: 'relative', overflow: 'hidden' }}>
              <span style={{ position: 'absolute', top: 0, left: 0, width: 3, height: '100%', background: m.t, opacity: 0.55 }} />
              <div style={{ fontFamily: 'var(--mono)', fontSize: 9.5, letterSpacing: '0.13em', color: 'var(--ink-3)', fontWeight: 600 }}>{m.k}</div>
              <div style={{ display: 'flex', alignItems: 'baseline', gap: 3, marginTop: 9 }}><span style={{ fontFamily: 'var(--num)', fontWeight: 400, fontSize: 26, letterSpacing: '-0.02em', color: m.t }}>{m.v}</span><span style={{ fontFamily: 'var(--mono)', fontSize: 10, color: 'var(--ink-3)', fontWeight: 600 }}>{m.u}</span></div>
            </div>
          ))}
        </div>
        {/* proof chart — contained card */}
        <div style={{ background: 'var(--paper)', border: '1px solid var(--rule)', borderRadius: 'var(--radius)', padding: '15px 16px 16px' }}>
          <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 10 }}>
            <span style={{ fontFamily: 'var(--sans)', fontSize: 14.5, fontWeight: 700, color: 'var(--ink)' }}>Trend, not noise</span>
            <TrendLegend />
          </div>
          <div style={{ marginTop: 12 }}><PosterGlide c={c} width={313} height={176} /></div>
          <div style={{ fontFamily: 'var(--sans)', fontSize: 12.5, color: 'var(--ink-2)', marginTop: 11, lineHeight: 1.5, textWrap: 'pretty' }}>A +1 kg water morning didn’t move the trend — or your plan. We flag you only when the trend really drifts.</div>
        </div>
        {/* condensed plan line */}
        <div style={{ background: 'var(--paper-2)', border: '1px solid var(--rule)', borderRadius: 'var(--radius)', padding: '14px 16px' }}>
          <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 10 }}>
            <span style={{ fontFamily: 'var(--sans)', fontSize: 14.5, fontWeight: 700, color: 'var(--ink)' }}>Your plan</span>
            <span style={{ fontFamily: 'var(--sans)', fontSize: 12.5, fontWeight: 600, color: 'var(--ink-2)' }}><span style={{ color: 'var(--ink)', fontWeight: 700 }}>{locked.length}</span> of {total} set</span>
          </div>
          <div style={{ marginTop: 10, fontFamily: 'var(--sans)', fontSize: 12.5, color: 'var(--ink-3)', lineHeight: 1.5 }}>{locked.map(i => i.t).join(' · ')} — <span style={{ color: 'var(--green)', fontWeight: 600 }}>locked</span>.</div>
        </div>
        {/* sharpen — tight rows in one filled container */}
        <div style={{ background: 'var(--paper-2)', border: '1px solid var(--rule)', borderRadius: 'var(--radius)', padding: '4px 16px 6px' }}>
          <div style={{ padding: '13px 0 2px' }}><Kicker color="var(--accent)">Sharpens in setup · tap to explore</Kicker></div>
          {levers.map((lv, i) => {
            const open = openKey === lv.key;
            return (
              <div key={lv.key} style={{ borderTop: i ? '1px solid var(--rule)' : 'none' }}>
                <button onClick={() => setOpenKey(open ? null : lv.key)} style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 11, padding: '12px 0', textAlign: 'left' }}>
                  <span style={{ flex: 1, minWidth: 0 }}>
                    <span style={{ display: 'block', fontFamily: 'var(--sans)', fontSize: 14, fontWeight: 600, color: 'var(--ink)' }}>{lv.title}</span>
                    <span style={{ display: 'block', fontFamily: 'var(--sans)', fontSize: 12, color: 'var(--ink-3)', marginTop: 1, lineHeight: 1.35 }}>{lv.summary}</span>
                  </span>
                  <Chevron open={open} />
                </button>
                {open && <div style={{ padding: '0 0 14px 0' }}><div style={{ borderTop: '1px solid var(--rule)', paddingTop: 12 }}>{lv.body}</div></div>}
              </div>
            );
          })}
        </div>
      </div>
    </window.RevealFrame>
  );
}

// ═════════════════════════════════════════════════════════════════════════════
// 3 · CHECKLIST (plan-led) — clay number hero; the ticked plan LEADS (this is what
// they're getting); the proof chart is a quiet secondary card; sharpen levers are
// minimal bordered rows with a "to set" tag; coach read is a closing footnote.
// Clay-tinted cards, green-forward. Most minimal information treatment.
// ═════════════════════════════════════════════════════════════════════════════
function RevealPosterPlan() {
  const data = usePosterData(); const { c, d, locked, total } = data;
  if (!c.tip) return null;
  const Kicker = window.Kicker;
  const pending = [
    { t: 'Recommended cut strategy', s: d.water ? 'measured water taper' : 'all real weight' },
    { t: 'Competition rules', s: 'gear · re-weigh · weigh-in' },
    { t: 'Cycle-aware targets', s: 'optional, if tracked' },
    { t: 'Weigh-in time & reminders', s: 'locked to your morning' },
  ];
  return (
    <window.RevealFrame label="Reveal · Checklist" footer={<window.PrimaryNext sub="Your camp is ready — yours to keep." />}>
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
        <ClayHead d={d} c={c} hero="number" />
        <div style={{ padding: '20px 22px 24px', display: 'flex', flexDirection: 'column', gap: 20 }}>

          {/* plan LEADS — clay-tinted card */}
          <div style={{ background: '#f4ecdd', border: '1px solid var(--rule-2)', borderRadius: 'var(--radius)', padding: '16px 18px' }}>
            <PlanTicks locked={locked} total={total} tinted />
          </div>

          {/* sharpens — minimal bordered rows with a to-set tag */}
          <div>
            <Kicker color="var(--accent)">Sharpens once you set up camp</Kicker>
            <div style={{ marginTop: 12, display: 'flex', flexDirection: 'column', gap: 9 }}>
              {pending.map((p, i) => (
                <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 11, background: '#f4ecdd', border: '1px solid var(--rule-2)', borderRadius: 'var(--radius-ctl)', padding: '12px 14px' }}>
                  <span style={{ flexShrink: 0, width: 16, height: 16, borderRadius: '50%', border: '1.5px solid var(--rule-2)' }} />
                  <span style={{ flex: 1, minWidth: 0 }}>
                    <span style={{ display: 'block', fontFamily: 'var(--sans)', fontSize: 14, fontWeight: 600, color: 'var(--ink)' }}>{p.t}</span>
                    <span style={{ display: 'block', fontFamily: 'var(--sans)', fontSize: 12, color: 'var(--ink-3)', marginTop: 1 }}>{p.s}</span>
                  </span>
                  <span style={{ flexShrink: 0, fontFamily: 'var(--mono)', fontSize: 8, letterSpacing: '0.05em', textTransform: 'uppercase', fontWeight: 700, color: 'var(--ink-3)', background: 'var(--paper)', border: '1px solid var(--rule)', borderRadius: 100, padding: '3px 8px' }}>To set</span>
                </div>
              ))}
            </div>
          </div>

          {/* proof chart — quiet secondary card */}
          <div style={{ background: 'var(--paper)', border: '1px solid var(--rule)', borderRadius: 'var(--radius)', padding: '14px 16px 15px' }}>
            <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 10 }}>
              <span style={{ fontFamily: 'var(--mono)', fontSize: 10, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--ink-3)', fontWeight: 700 }}>Trend, not noise</span>
              <TrendLegend />
            </div>
            <div style={{ marginTop: 11 }}><PosterGlide c={c} width={313} height={168} /></div>
            <div style={{ fontFamily: 'var(--sans)', fontSize: 12, color: 'var(--ink-2)', marginTop: 10, lineHeight: 1.5, textWrap: 'pretty' }}>One scary morning isn’t off-track. We read the trend and flag the drift — you just train.</div>
          </div>

          {/* coach read — closing footnote */}
          <div style={{ borderLeft: '3px solid var(--accent)', paddingLeft: 14 }}>
            <div style={{ fontFamily: 'var(--sans)', fontSize: 14.5, fontWeight: 700, lineHeight: 1.3, color: 'var(--ink)', textWrap: 'balance' }}>{c.tip.headline}</div>
            <div style={{ fontFamily: 'var(--sans)', fontSize: 12.5, color: 'var(--ink-3)', marginTop: 6, lineHeight: 1.5, textWrap: 'pretty' }}>{c.tip.body}</div>
          </div>
        </div>
      </div>
    </window.RevealFrame>
  );
}

Object.assign(window, {
  RevealPosterPlus, RevealPosterData, RevealPosterPlan,
  // shared bits reused by the research-led treatments in fc-reveal-poster2.jsx
  PosterGlide, TrendLegend, Verdict, usePosterData, ClayHead, PlanTicks, buildLevers, Chevron, POSTER_CLAY: CLAY,
});
