// ============================================================
// TANK’S TRADING DESK — home-sections.jsx
// Homepage sections (KPI ticker · system pipeline · premarket
// plan hero · market data sheet). Top-level functions become
// globals (same pattern as kpis.jsx / structural-bias.jsx) and
// are consumed by app.jsx.
// ============================================================

const _DS = {};
const { Pill: DSPill, AuditRow: DSAuditRow, Button: DSButton, Disclaimer: DSDisclaimer } = _DS;

const { useState: hsUseState, useEffect: hsUseEffect, useRef: hsUseRef } = React;

// ---------- shared formatters (fall back if globals not present) ----------
const hsUSD0 = (n) => '$' + Math.round(n).toLocaleString();
const hsSignedUSD = (n) => (n >= 0 ? '+' : '−') + '$' + Math.abs(Math.round(n)).toLocaleString();

// ============================================================
// KPI TICKER — removed (V-27): superseded by HomeKpiTriad; carried an
// invalid role="marquee" and stale benchmark framing.
// ============================================================

// ============================================================
// SYSTEM PIPELINE — Market Data → Bias / Sensing → Daily Plan → Trade with Tank
// ============================================================
function SystemPipeline({ active, onSelect, onJoin, styleVariant, headStyle, staleStyle, staleSim }) {
  const Arrow = () => <div className="sp-arrow" aria-hidden="true">→</div>;
  const is = (id) => active === id;
  const [sess, setSess] = hsUseState('');
  const [sessISO, setSessISO] = hsUseState('');
  hsUseEffect(() => {
    const R = window.__resources || {};
    fetch(R.premarket || 'data/premarket_sectors.json').then((r) => r.ok ? r.json() : Promise.reject()).then((d) => {setSess(d.meta && d.meta.session_label || '');setSessISO(d.meta && d.meta.session || '');}).catch(() => {});
  }, []);
  const [mins, setMins] = hsUseState(null);
  const [etInfo, setEtInfo] = hsUseState(null);
  hsUseEffect(() => {
    const calc = () => {
      try {
        const now = new Date();
        const et = new Date(now.toLocaleString('en-US', { timeZone: 'America/New_York' }));
        const open = new Date(et);open.setHours(9, 30, 0, 0);
        let diff = Math.round((open - et) / 60000);
        if (diff < 0) diff += 24 * 60;
        setMins(diff);
        // sun position + ET clock — market open (9:30–16:00 ET) travels under "Trade with Us"
        // (80% → 100%); the rest of the day (16:00 → next 9:30) travels Market Data → end of Daily Plan (0% → 80%)
        const h = et.getHours(),m = et.getMinutes();
        const tmin = h * 60 + m;
        const openM = 570,closeM = 960;
        let pct;
        if (tmin >= openM && tmin < closeM) {
          pct = 80 + (tmin - openM) / (closeM - openM) * 20;
        } else {
          const since = tmin >= closeM ? tmin - closeM : tmin + 1440 - closeM;
          pct = since / 1050 * 80;
        }
        setEtInfo({ label: (h < 10 ? '0' : '') + h + ':' + (m < 10 ? '0' : '') + m + ' ET', pct: Math.max(1, Math.min(99, pct)) });
      } catch (e) {setMins(null);setEtInfo(null);}
    };
    calc();
    const t = setInterval(calc, 30000);
    return () => clearInterval(t);
  }, []);
  const liveLabel = (() => {
    if (mins == null) return 'Market open';
    const h = Math.floor(mins / 60),m = mins % 60;
    return (h > 0 ? h + 'h ' : '') + m + 'm to market open';
  })();
  const clockShort = mins == null ? '—' : (Math.floor(mins / 60) > 0 ? Math.floor(mins / 60) + 'h ' : '') + mins % 60 + 'm';
  // progress through the planning window (prev close 16:00 ET → open 9:30 ET ≈ 1050 min)
  // sun position along the pipeline (see calc above)
  const dawnPct = etInfo ? etInfo.pct : 70;
  const planSt = ttdPlanStatus(sessISO, staleSim);
  return (
    <section className={`sys-pipe pipe-${styleVariant || 'chevron'} shead-${headStyle || 'classic'}`} aria-label="The system, left to right">
      <div className="sys-headrow">
        <div className="sys-head-l">
          <p className="sys-eyebrow">today's session</p>
          <h2 className="sys-headline"><span className="sys-headline-pre">Planning for session</span>{sess ? <span className="sys-headline-date"><span className="sys-headline-sep"> · </span>{sess}</span> : null}</h2>
        </div>
        <div className="sys-head-r">
          <h2 className="sys-headline sys-headline-live">{liveLabel}</h2>
          <div className="sys-clock">
            <span className="sys-clock-v">{clockShort}</span>
            <span className="sys-clock-l">to market open</span>
          </div>
        </div>
      </div>
      <div className="sys-phase" aria-hidden="true">
        <div className="phase-track">
          <span className="phase-line planning" />
          <span className="phase-node" />
          <span className="phase-line live" />
        </div>
      </div>
      <div className="dawn-datebar" aria-hidden="true">
        {staleStyle === 'pill' && planSt.stale ?
        <span className="dawn-date-pill is-coming">
            <b className="dawn-date-d">{planSt.todayLabel}</b>
            <span className="ddp-status"><span className="pc-dot" />plan in preparation{planSt.eta ? ' · ~' + planSt.eta : ' · any moment'}</span>
            <a className="ddp-recap" href="#plan">viewing {sess || 'previous session'} · recap ↓</a>
          </span> :
        <span className="dawn-date-pill"><b className="dawn-date-d">{sess || '—'}</b><span className="dawn-date-sub">pre-market · planning</span></span>
        }
      </div>
      <div className="dawn-track" aria-hidden="true" style={{ ['--open-pct']: dawnPct + '%' }}>
        <span className="dawn-line" />
        <span className="dawn-dot" />
        <span className={'dawn-time' + (dawnPct > 86 ? ' flip' : dawnPct < 5 ? ' flip-l' : '')}>{etInfo ? etInfo.label : ''}</span>
      </div>
      <div className="sys-pipe-row" role="tablist">
        <button className={`sp-stage ${is('data') ? 'active' : ''}`} role="tab" aria-selected={is('data')} onClick={() => onSelect('data')}><span className="sp-num" aria-hidden="true">1</span>Market Data<span className="sp-sub">raw · pre-open</span></button>
        <Arrow />
        <button className={`sp-stage ${is('bias') ? 'active' : ''}`} role="tab" aria-selected={is('bias')} onClick={() => onSelect('bias')}><span className="sp-num" aria-hidden="true">2</span>Structural Bias<span className="sp-sub">regime</span></button>
        <Arrow />
        <button className={`sp-stage ${is('sensing') ? 'active' : ''}`} role="tab" aria-selected={is('sensing')} onClick={() => onSelect('sensing')}><span className="sp-num" aria-hidden="true">3</span>Risk Sensing<span className="sp-sub">volatility</span></button>
        <Arrow />
        <button className={`sp-stage ${is('plan') ? 'active' : ''}`} role="tab" aria-selected={is('plan')} onClick={() => onSelect('plan')}><span className="sp-num" aria-hidden="true">4</span>Daily Plan<span className="sp-sub">discipline</span></button>
        <Arrow />
        <button className="sp-stage cta" onClick={onJoin}><span className="sp-num" aria-hidden="true">5</span>Join Us Free<span className="sp-sub">live</span></button>
      </div>
    </section>);

}

// ============================================================
// PRICE SCALE — QQQ spot · expected-move band · planned tier levels
// Pure presentational; receives resolved levels + tiers.
// ============================================================
function PriceScale({ spot, em, ticks, tiers, compact, session }) {
  // Plan-name convention shared with the watchlist + trade log: <date>_<L|S>_<tier>
  const PS_MS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  const psDateTag = session && /^\d{4}-\d{2}-\d{2}$/.test(session) ?
  (() => {const [, mo, da] = session.split('-').map(Number);return `${da}${PS_MS[mo - 1]}`;})() : '';
  const psTierTag = (label) => /aggressive/i.test(label || '') ? 'AGG' : 'T' + (/\d+/.exec(label || '') || ['?'])[0];
  const planName = (t) => psDateTag ? `${psDateTag}_${t.direction === 'long' ? 'L' : 'S'}_${psTierTag(t.label)}` : (t.direction === 'long' ? 'Long ' : 'Short ') + t.label;
  // Card geometry first — the scale must be tall enough to seat every tier
  // card (the plan can carry 1–6 non-gated tiers, not a fixed 2). cardH covers
  // a 2-line invalidation row.
  const cardH = 124,gap = 14;
  const minH = compact ? 300 : 320;
  // V-28: narrow container (phone) → stacked layout. The absolute card
  // overlay can't sit beside the axis under ~480px — cards collide with the
  // tick labels — so the scale keeps only axis + ticks and the cards render
  // below in normal flow.
  const psWrapRef = hsUseRef(null);
  const [psW, setPsW] = hsUseState(typeof window !== 'undefined' ? window.innerWidth : 1200);
  hsUseEffect(() => {
    const measure = () => setPsW(psWrapRef.current ? psWrapRef.current.clientWidth : window.innerWidth);
    measure();
    window.addEventListener('resize', measure);
    return () => window.removeEventListener('resize', measure);
  }, []);
  const psNarrow = psW < 460;
  const H = psNarrow ?
  (ticks || []).length * 56 + 20 :
  Math.max(minH, (tiers || []).length * (cardH + gap) + 8);
  const prices = [
  spot,
  ...ticks.map((t) => t.p),
  ...tiers.map((t) => t.level),
  em.low, em.high];

  const hi = Math.max(...prices) + 1.5;
  const lo = Math.min(...prices) - 1.5;
  const Y = (p) => (hi - p) / (hi - lo) * H;
  // Narrow ladder: ticks evenly spaced in price order (a true spatial scale
  // can't seat 4 labels inside a 5-pt cluster at phone width) — the dots keep
  // price ORDER, a note flags that spacing isn't to scale.
  const nyByP = {};
  if (psNarrow) [...ticks].sort((a, b) => b.p - a.p).forEach((t, i) => {nyByP[t.p] = i * 56 + 16;});
  const axisX = psNarrow ? Math.max(210, psW - 96) : compact ? 116 : 150;
  const cardX = compact ? 188 : 238;
  const busX = (axisX + cardX) / 2;

  // de-collide tier cards top→bottom, then a bottom-up containment pass so
  // the stack can never overflow the scale's bottom edge (H is sized to fit).
  let lastBottom = -Infinity;
  const placed = [...tiers].
  sort((a, b) => Y(a.level) - Y(b.level)).
  map((t) => {
    let top = Math.max(0, Math.min(H - cardH, Y(t.level) - cardH / 2));
    if (top < lastBottom + gap) top = lastBottom + gap;
    lastBottom = top + cardH;
    return { ...t, top, midY: Y(t.level) };
  });
  let nextTop = H; // bottom boundary
  for (let i = placed.length - 1; i >= 0; i--) {
    const c = placed[i];
    const maxTop = nextTop - cardH;
    if (c.top > maxTop) c.top = Math.max(0, maxTop);
    nextTop = c.top - gap;
  }
  placed.forEach((c) => {c.cardCenter = c.top + cardH / 2;});

  const tierCard = (t, stacked) =>
  <div key={t.id} className={`ps-tier-card ${t.state}${stacked ? ' stacked' : ''}`} style={stacked ? undefined : { left: cardX, right: 0, top: t.top }}>
      <div className="ps-tier-name">
        <span className="ps-tier-label">{planName(t)}</span>
        <span className="ps-tier-sub">{t.direction === 'long' ? 'Long' : 'Short'} {t.label}</span>
        {stacked && t.level != null ? <span className="ps-tier-at">@ {t.level}</span> : null}
        <span className={`ps-tier-state ${t.state}`}>{{ active: 'Allowed', armed: 'Armed', conditional: 'Conditional' }[t.state] || t.state}</span>
      </div>
      <div className="ps-tier-legs">
        {[['Entry', t.entry], ['Target', t.target], ['Stop', t.stop]].map(([k, v]) =>
      <div key={k}><div className="ps-tier-leg-l">{k}</div><div className="ps-tier-leg-v">{v}</div></div>
      )}
      </div>
      <div className="ps-tier-invalid"><span className="ps-tier-invalid-l">Thesis invalidation</span>{t.invalidation}</div>
    </div>;

  return (
    <div className="price-scale-wrap" ref={psWrapRef}>
      <div className="price-scale-h"><span>QQQ · planned levels</span><span style={{ color: '#95A9AB' }}>1D EM {em.low}–{em.high}</span></div>
      <div className="price-scale" style={{ height: H }}>
        {/* vertical axis + caps */}
        <div className="ps-axis" style={{ left: axisX, top: 4, bottom: 4 }} />

        {/* leaders — single straight connector per tier (wide layout only) */}
        {!psNarrow && placed.map((t) =>
        <div className="ps-leader" key={'L' + t.id} style={{ left: axisX + 6, top: t.cardCenter - 1, width: cardX - axisX - 6, height: 2 }} />
        )}

        {/* ticks */}
        {ticks.map((t) =>
        <div key={t.p} style={{ position: 'absolute', left: 0, top: psNarrow ? nyByP[t.p] : Y(t.p) + (t.dy || 0), width: axisX + 70, height: 0 }}>
            <div className="ps-tick-label" style={{ right: `calc(100% - ${axisX - 12}px)`, top: 0 }}>
              <span className="ps-tick-price" style={{ fontSize: t.big ? 15 : 11.5, color: t.big ? '#fff' : '#C7D2DA' }}>{t.label}</span>
              <div className="ps-tick-role" style={{ color: t.c }}>{t.role}</div>
            </div>
            <div className="ps-tick-dot" style={{ left: axisX, top: 0, width: t.big ? 11 : 9, height: t.big ? 11 : 9, background: t.hollow ? '#FFFFFF' : t.c, border: `2px solid ${t.c}`, boxShadow: t.big ? '0 0 0 3px rgba(33,55,68,0.22)' : 'none' }} />
          </div>
        )}

        {/* tier cards — absolute overlay on wide screens */}
        {!psNarrow && placed.map((t) => tierCard(t, false))}
      </div>
      {psNarrow && <div className="ps-ladder-note">levels in price order · spacing not to scale</div>}
      {/* tier cards — stacked below the scale on phones (V-28) */}
      {psNarrow && <div className="ps-tier-stack">{placed.map((t) => tierCard(t, true))}</div>}
    </div>);

}

// ============================================================
// TIER PLAYBOOK — full swing-tier breakdown below the price scale
// ============================================================
function TierPlaybook({ tiers }) {
  const rows = [
  ['Zone type', 'zoneType'],
  ['Zone / level', 'zone'],
  ['Swing activation', 'activation'],
  ['Thesis invalidation', 'invalidation'],
  ['Destination zones', 'destination']];

  return (
    <div className="tier-pb">
      <div className="tier-pb-head">Swing tier playbook</div>
      <div className="tier-pb-grid">
        {tiers.map((t) =>
        <div className={`tier-pb-card ${t.state}`} key={t.n}>
            <div className="tier-pb-top">
              <span className="tier-pb-name">Short Tier {t.n}</span>
              <span className={`tier-pb-state ${t.state}`}>{t.state === 'active' ? 'Allowed' : 'Armed'}</span>
            </div>
            <div className="tier-pb-setup">{t.setup}</div>
            <dl className="tier-pb-dl">
              {rows.map(([label, key]) =>
            <div className="tier-pb-row" key={key}>
                  <dt>{label}</dt><dd>{t[key]}</dd>
                </div>
            )}
            </dl>
          </div>
        )}
      </div>
    </div>);

}

// ============================================================
// TV CHARTING — live Advanced Chart widget (QQQ 30m) + deep-link
// ============================================================
function TvCharting() {
  return (
    <div className="tv-charting">
      <div className="tv-charting-head">
        <a className="tv-deeplink" href="https://www.tradingview.com/script/nQMXkpBp-QQQ-Pro-Plan-Zone/" target="_blank" rel="noopener noreferrer">Open on TradingView ↗</a>
        <span className="tv-charting-eb">QQQ Pro Plan Zone · charting</span>
      </div>
      <p className="tv-shot-cap">A live frame of the published <a href="https://www.tradingview.com/script/nQMXkpBp-QQQ-Pro-Plan-Zone/" target="_blank" rel="noopener noreferrer">QQQ Pro Plan Zone</a> chart on TradingView — it re-pulls on every visit and refreshes automatically each time the plan is republished.</p>
      <a className="tv-shot" href="https://www.tradingview.com/script/nQMXkpBp-QQQ-Pro-Plan-Zone/" target="_blank" rel="noopener noreferrer" aria-label="Open the QQQ Pro Plan Zone on TradingView">
        <img className="tv-shot-img" src={`https://s3.tradingview.com/n/nQMXkpBp_big.png?t=${Date.now()}`} alt="QQQ Pro Plan Zone — live chart from TradingView" loading="lazy" onError={(e) => {e.currentTarget.setAttribute('data-failed', '1');}} />
      </a>
    </div>);

}

// ============================================================
// DAILY PLAN HERO — today's plan, free, before the bell
// Self-fetches premarket_sectors.json (like the other embeds).
// ============================================================
// ============================================================
// TODAY'S-PLAN STATUS — is the published plan for the current ET
// trading day, or are we still waiting on this morning's plan?
// stale = weekday + published session date < today's ET date.
// sim: 'stale' | 'ready' forces the state for design preview.
// ============================================================
function ttdPlanStatus(sessionISO, sim) {
  try {
    const now = new Date();
    const et = new Date(now.toLocaleString('en-US', { timeZone: 'America/New_York' }));
    const dow = et.getDay();
    const pad = (n) => String(n).padStart(2, '0');
    const todayISO = et.getFullYear() + '-' + pad(et.getMonth() + 1) + '-' + pad(et.getDate());
    const WD = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
    const MO = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
    const todayLabel = WD[dow] + ' · ' + et.getDate() + ' ' + MO[et.getMonth()] + ' ' + et.getFullYear();
    const isWeekday = dow >= 1 && dow <= 5;
    let stale = isWeekday && !!sessionISO && sessionISO < todayISO;
    if (sim === 'stale') stale = true;
    if (sim === 'ready') stale = false;
    const minsTo8 = 8 * 60 - (et.getHours() * 60 + et.getMinutes());
    const eta = minsTo8 > 0 ? (Math.floor(minsTo8 / 60) > 0 ? Math.floor(minsTo8 / 60) + 'h ' : '') + minsTo8 % 60 + 'm' : null;
    return { stale, todayLabel, eta };
  } catch (e) {return { stale: false, todayLabel: '', eta: null };}
}

function PremarketPlanHero({ onJoin, staleStyle, staleSim }) {
  staleStyle = staleStyle || 'overlay'; // mobile renders without props — same default treatment
  const [pm, setPm] = hsUseState(null);
  const [showRecap, setShowRecap] = hsUseState(false);
  const [, setPcTick] = hsUseState(0);
  hsUseEffect(() => {const t = setInterval(() => setPcTick((x) => x + 1), 30000);return () => clearInterval(t);}, []);
  hsUseEffect(() => {
    const R = window.__resources || {};
    const url = R.premarket || 'data/premarket_sectors.json';
    fetch(url).then((r) => r.ok ? r.json() : Promise.reject(r.status)).then(setPm).catch(() => {});
  }, []);

  if (!pm) {
    return (
      <section className="plan-hero" id="plan">
        <div className="plan-hero-wrap">
          <div className="plan-hero-head">
            <p className="plan-hero-eb">today · pre-open</p>
            <h2 className="plan-hero-h">Daily Plan</h2>
          </div>
          <div className="plan-panel" style={{ minHeight: 220, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#95A9AB', fontFamily: 'var(--font-sans)' }}>Loading today's plan…</div>
        </div>
      </section>);

  }

  const m = pm.meta;
  const plan = pm.plan || null;
  const spot = m.anchor?.price;
  const em = m.expected_move?.d1 || null;
  // punchy day label from the session date, e.g. "Tuesday 9 Jun"
  const _WD = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
  const _MO = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  let dayLabel = m.session_label || 'Today';
  if (m.session && /^\d{4}-\d{2}-\d{2}$/.test(m.session)) {
    const [yy, mm, dd] = m.session.split('-').map(Number);
    const wd = new Date(Date.UTC(yy, mm - 1, dd)).getUTCDay();
    dayLabel = `${_WD[wd]} ${dd} ${_MO[mm - 1]}`;
  }
  // Levels + tiers come from the planner's plan block (all QQQ basis) — no
  // hardcoded literals. When the block is absent the price scale gives way to
  // an explicit "not yet published" panel instead of stale numbers.
  // tone → color on the DARK plan panel (#1C2F3A) — the light-theme tokens
  // (--ink navy, --accent navy) vanish here, so use the panel's own legible
  // equivalents: warn → soft terracotta, ink → cool grey-blue, accent → mustard.
  const toneColor = { warn: 'var(--loss-dk, #D08E76)', ink: '#C7D2DA', accent: 'var(--accent-lite, #DCB482)' };
  const anchorNote = (m.anchor?.note || 'last close').split('.')[0];
  const ticks = plan && plan.levels && plan.levels.length && spot != null ? [
  ...plan.levels.map((L) => ({ p: L.price, label: L.label || String(L.price), role: L.role, c: toneColor[L.tone] || toneColor.ink })),
  { p: spot, label: spot.toFixed(2), role: `QQQ spot · ${anchorNote}`, c: '#95A9AB', big: true }].
  sort((a, b) => b.p - a.p) : null;

  const allTiers = plan ? plan.tiers || [] : [];
  const scaleTiers = allTiers.filter((t) => t.state !== 'gated' && typeof t.level === 'number');

  const allowed = m.composite?.allowed || [];
  const blocked = m.composite?.blocked || [];
  // Risk Mode — first-class system output (V-04). Reads structured
  // plan.risk_mode ({ state, note }) when the planner ships it; until then
  // derives the state from the posture string and shows posture as the note.
  const RISK_STATES = ['Trade as planned', 'Reduce size', 'Delay entries', 'Stand down'];
  const riskMode = plan ? (() => {
    const rm = plan.risk_mode;
    if (rm && typeof rm === 'object') {
      const map = { trade: 0, reduce: 1, delay: 2, stand_down: 3 };
      return { idx: map[rm.state] != null ? map[rm.state] : 0, note: rm.note || plan.posture || '' };
    }
    const s = (String(rm || '') + ' ' + (plan.posture || '')).toLowerCase();
    let idx = 0;
    if (/stand[\s-]*down|no trades/.test(s)) idx = 3;else
    if (/delay/.test(s)) idx = 2;else
    if (/reduce|half size|smaller size/.test(s)) idx = 1;
    return { idx, note: plan.posture || '' };
  })() : null;
  const compact = typeof window !== 'undefined' && window.innerWidth < 760;
  const planSt = ttdPlanStatus(m.session, staleSim);
  const overlaid = planSt.stale && staleStyle === 'overlay' && !showRecap;

  return (
    <section className={`plan-hero ${overlaid ? 'is-overlaid' : ''}`} id="plan">
      <div className="plan-hero-wrap">
        <div className="plan-hero-head">
          <h2 className="plan-hero-h">Daily Plan{planSt.stale ? <span className="pc-chip">recap · {m.session_label}</span> : null}</h2>
          <div className="plan-hero-meta">
            <div className="phm"><span className="k">Session</span><span className="v">{m.session_label}</span></div>
            <div className="phm"><span className="k">Rulebook</span><span className="v">{(m.rulebook || '').replace(/\s*\(.*\)$/, '')}</span></div>
            <div className="phm"><span className="k">Composite</span><span className="v">{m.composite?.label}</span></div>
            {plan && plan.catalyst ? <div className="phm"><span className="k">Catalyst</span><span className="v">{plan.catalyst}</span></div> : null}
          </div>
          {/* V-16 — publish receipt. Shows the exact clock when the planner
                ships meta.published_at; date falls back to the session label. */}
          <div className="plan-pub" role="note">
            <span className="pub-dot" aria-hidden="true"></span>
            {(() => {
              const p = m.published_at;
              if (p) {
                const d = new Date(p);
                if (!isNaN(d)) return <>Published at {d.toLocaleTimeString('en-US', { timeZone: 'America/New_York', hour: '2-digit', minute: '2-digit', hour12: false })} ET on {m.session_label}</>;
              }
              return <>Published pre-open on {m.session_label}</>;
            })()}
          </div>
          {riskMode ?
          <div className="risk-track" role="status" aria-label={`Risk mode: ${RISK_STATES[riskMode.idx]}`}>
            <div className="rt-eb">risk mode — today's call</div>
            <div className="rt-seg">
              {RISK_STATES.map((s, i) =>
              <div key={s} className={`rt-cell ${i === riskMode.idx ? 'on ' + (riskMode.idx === 3 ? 's-stand' : riskMode.idx === 0 ? 's-trade' : 's-caut') : ''}`}><span className="d"></span><span className="t">{s}</span></div>
              )}
            </div>
          </div> : null}
        </div>

        {planSt.stale && staleStyle === 'banner' ?
        <div className="plan-coming-banner" role="status">
          <span className="pc-dot" aria-hidden="true" />
          <div className="pc-txt"><b>The {planSt.todayLabel} plan is being prepared.</b> It publishes before the bell{planSt.eta ? ' — expected in ~' + planSt.eta : ' — due any moment now'}.</div>
          <span className="pc-sub">below: {m.session_label} · yesterday's plan, as recap</span>
        </div> : null}

        <div className="plan-panel">
          <div className="allow-2col">
            <div className="a2-col ok">
              <p className="a2-h" style={{ fontSize: "18px" }}>Allowed</p>
              {allowed.map((t, i) => <div className="a2-row" key={i}><span className="a2-m">+</span><span>{t}</span></div>)}
            </div>
            <div className="a2-rule" />
            <div className="a2-col no">
              <p className="a2-h" style={{ fontSize: "18px" }}>Not allowed</p>
              {blocked.map((t, i) => <div className="a2-row" key={i}><span className="a2-m">−</span><span>{t}</span></div>)}
            </div>
          </div>
          {ticks ?
          <PriceScale spot={spot} em={em || { low: spot, high: spot }} ticks={ticks} tiers={scaleTiers} compact={compact} session={m.session} /> :
          <div className="plan-pending" role="status">
            <div className="plan-pending-eb">plan levels</div>
            <div className="plan-pending-h">Today's plan levels haven't been published yet</div>
            <p className="plan-pending-d">The plan block lands with the planner's morning file, roughly 1–2 hours before the open. The composite read above is live; the price scale, tier playbook and watchlist render here the moment it publishes.</p>
          </div>
          }

          <PlanCandidates tiers={allTiers} carries={plan ? plan.carry_forwards || [] : []} spot={spot} session={m.session} />

          <OpenPositions planPositions={plan ? plan.positions || [] : []} />

          <PlanContext tss={plan ? plan.tss : null} roadmap={plan ? plan.roadmap : null} />

          <TvCharting />
        </div>
      </div>
      {overlaid ?
      <div className="plan-overlay">
        <div className="po-card" role="status">
          <p className="po-eb"><span className="pc-dot" aria-hidden="true" />coming up</p>
          <h3 className="po-h">The {planSt.todayLabel} plan drops before the bell</h3>
          <p className="po-p">Published most mornings by ~8:00 ET{planSt.eta ? ' — about ' + planSt.eta + ' from now' : ' — due any moment now'}. Until it lands, yesterday's plan stays up as the working reference.</p>
          <button className="po-btn" onClick={() => setShowRecap(true)}>Review yesterday's plan ({m.session_label}) →</button>
        </div>
      </div> : null}
    </section>);

}

// ============================================================
// PLAN CONTEXT — trader-state spectrum + Markov forward roadmap.
// Same modules the Track Records day workbench renders (tradeplan.jsx),
// here fed by premarket_sectors.json plan.tss / plan.roadmap so the
// homepage plan carries the regime context, not just the levels.
// ============================================================
function PlanContext({ tss, roadmap }) {
  const [tssOpen, setTssOpen] = React.useState(false);
  const [selPath, setSelPath] = React.useState(null);
  if (!tss && !roadmap) return null;
  const pos = tss ? Math.max(2, Math.min(98, tss.pos != null ? tss.pos : 50)) : 50;
  const zone = tss ? tss.zone || (pos < 33 ? 'risk' : pos < 66 ? 'natural' : 'opportunity') : 'natural';
  const paths = roadmap && Array.isArray(roadmap.paths) && roadmap.paths.length ? roadmap.paths : null;
  const PATH_LAB = { flush: 'Flush', retest: 'Retest', drift: 'Base', breakout: 'Breakout' };
  return (
    <div className="plan-context">
      {tss ?
      <div className="pp-tss">
        <div className="pp-tss-eb">trader-state spectrum · where this plan sits <span className="pp-tss-hint">{tssOpen ? '▲ hide' : '▼ why'}</span></div>
        <button
          type="button"
          className={`pp-tss-bar ${tssOpen ? 'open' : ''}`}
          onClick={() => setTssOpen((v) => !v)}
          aria-expanded={tssOpen}
          aria-label="Toggle trader-state reasoning">
          <div className="pp-tss-zone risk-zone" />
          <div className="pp-tss-zone nat-zone" />
          <div className="pp-tss-zone opp-zone" />
          <div className="pp-tss-marker" style={{ left: `${pos}%` }}>
            <span className="pp-tss-marker-dot" />
            <span className="pp-tss-marker-label" style={{ transform: `translateX(-${pos}%)` }}>{tss.label}</span>
          </div>
        </button>
        <div className="pp-tss-scale" aria-hidden="true">
          <span className="pp-tss-sc l">Overbought</span>
          <span className="pp-tss-sc m">Natural</span>
          <span className="pp-tss-sc r">Oversold</span>
        </div>
        {tssOpen &&
        <div className={`pp-tss-detail ${zone}`}>
          <div className="pp-tss-detail-h">
            <b>{tss.label}</b>
            {tss.firm && <span className="pp-tss-firm">{tss.firm} firm</span>}
            <span className="pp-tss-verdict-tag">{zone === 'risk' ? 'risk zone' : zone === 'opportunity' ? 'opportunity zone' : 'natural zone'}</span>
          </div>
          <div className="pp-tss-detail-d">{tss.reason}</div>
        </div>
        }
      </div> : null}

      {paths ?
      <div className="pp-roadmap">
        <div className="pp-roadmap-eb">Markov HMM · forward roadmap</div>
        <div className="pp-roadmap-bar">
          {paths.map((p) => {
            const open = selPath === p.key;
            return (
              <button
                key={p.key}
                className={`pp-rm-seg ${p.key} ${open ? 'sel' : ''}`}
                style={{ flex: p.pct }}
                onClick={() => setSelPath(open ? null : p.key)}
                aria-pressed={open}
                title={`${p.label} · ${p.pct}%`}>
                <span className="pp-rm-pct">{p.pct}%</span>
                <em className="pp-rm-lab">{PATH_LAB[p.key] || p.label}</em>
              </button>);
          })}
        </div>
        {(() => {
          const p = paths.find((x) => x.key === selPath);
          if (!p) return (
            <div className="pp-rm-hint">Click a path above to see its mechanics, probability, and price footprint.</div>);
          return (
            <div className={`pp-rm-detail ${p.key}`}>
              <div className="pp-rm-detail-h"><b>{p.label}</b><span className="pp-rm-detail-pct">{p.pct}%</span></div>
              <div className="pp-rm-detail-d">{p.mech}</div>
              {p.footprint && <div className="pp-rm-detail-foot">{p.footprint}</div>}
            </div>);
        })()}
        {roadmap.markov_caption && roadmap.markov_caption.text ?
        <div className="pp-rm-markov">{roadmap.markov_caption.text}</div> : null}
      </div> : null}
    </div>);
}

// ============================================================
// PLAN CANDIDATES — today's plan tiers (incl. gated) + untriggered
// carry-forwards, all from the planner's plan block (QQQ basis).
// No longer reads trades.json — single-writer rule: the whole plan
// page is fed by premarket_sectors.json alone.
// ============================================================
function PlanCandidates({ tiers, carries, spot, session }) {
  const dist = (lvl) => {if (lvl == null || spot == null) return '\u2014';const d = lvl - spot;return (d >= 0 ? '+' : '\u2212') + Math.abs(d).toFixed(2);};
  const STATE_LABEL = { active: 'Allowed', armed: 'Armed', conditional: 'Conditional', gated: 'Gated' };
  // Full plan name in the trade-log convention: <date>_<L|S>_<tier>,
  // e.g. "11Jun_L_T1" / "11Jun_S_AGG" — same ids the log and calendar use.
  const MS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  const dateTag = session && /^\d{4}-\d{2}-\d{2}$/.test(session) ?
  (() => {const [, mo, da] = session.split('-').map(Number);return `${da}${MS[mo - 1]}`;})() : '';
  const tierTag = (label) => /aggressive/i.test(label || '') ? 'AGG' : 'T' + (/\d+/.exec(label || '') || ['?'])[0];
  // Gated (blocked) tiers are excluded — the watchlist is only plans that can
  // actually fire today (Tank, 2026-06-12).
  const today = (tiers || []).filter((t) => t.state !== 'gated').map((t) => {
    const friendly = (t.direction === 'long' ? 'Long ' : 'Short ') + t.label;
    return {
      key: t.id,
      label: dateTag ? `${dateTag}_${t.direction === 'long' ? 'L' : 'S'}_${tierTag(t.label)}` : friendly,
      sub: dateTag ? friendly : '',
      dir: t.direction,
      trigger: t.entry || t.zone || '\u2014',
      level: t.level,
      cond: t.activation || '\u2014',
      state: STATE_LABEL[t.state] || t.state,
      when: 'New \u00b7 today'
    };
  });
  const past = (carries || []).slice(0, 8).map((u, i) => ({
    key: 'u' + i, label: u.plan_id || 'Carry \u00b7 ' + (u.date || ''),
    dir: u.direction || '', trigger: u.qqq ? `${u.qqq.low}\u2013${u.qqq.high}` : '\u2014',
    level: u.qqq ? u.qqq.low : null, cond: u.activation || u.note || '\u2014', state: (u.status || 'carried').replace(/_/g, ' '), when: u.date || 'carry-forward'
  }));
  const rows = [...today, ...past];
  return (
    <div className="plan-cand">
      <div className="open-pos-head">
        <span className="open-pos-eb">Plan to watch</span>
        <span className="open-pos-note">today's tiers + untriggered carry-forwards · QQQ basis</span>
      </div>
      <div className="pc-table">
        <div className="pc-row pc-head">
          <span>Plan</span><span>Dir</span><span>Trigger zone</span><span>Condition to activate</span><span className="r"><span className="pc-h-long">Dist to PM</span><span className="pc-h-short">Dist</span></span><span className="r">Status</span>
        </div>
        {rows.length === 0 ?
        <div className="pc-empty">No active candidates into today.</div> :
        rows.map((r) =>
        <div className="pc-row" key={r.key}>
            <span className="pc-name">{r.label}{r.sub ? <em className="pc-name-sub">{r.sub}</em> : null}</span>
            <span className={`pc-dir ${r.dir}`}>{r.dir === 'short' ? '\u25BC Short' : r.dir === 'long' ? '\u25B2 Long' : '\u2014'}</span>
            <span className="pc-trig">{r.trigger}</span>
            <span className="pc-cond">{r.cond}</span>
            <span className="r pc-dist">{dist(r.level)}</span>
            <span className="r"><span className={`pc-state ${String(r.state).toLowerCase().replace(/\s/g, '-')}`}>{r.state}</span></span>
          </div>
        )}
      </div>
    </div>);

}

// ============================================================
// OPEN POSITIONS — carried into today. FACTS (fill / mark / P&L, NQ)
// come from performance.json (PMR's file, joined by trade-log id);
// INTENT (planned action + managed stop, QQQ basis) comes from the
// planner's plan.positions[] passed in as a prop.
// ============================================================
function OpenPositions({ planPositions }) {
  const [perf, setPerf] = hsUseState(null);
  hsUseEffect(() => {
    const R = window.__resources || {};
    fetch(R.performance || 'data/performance.json').then((r) => r.ok ? r.json() : Promise.reject(r.status)).then(setPerf).catch(() => {});
  }, []);
  const positions = perf && perf.open_positions || [];
  const intents = {};
  (planPositions || []).forEach((q) => {if (q && q.id) intents[q.id] = q;});
  const fmtP = (n) => n == null ? '\u2014' : Number(n).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 2 });
  const fmtUSD = (n) => (n >= 0 ? '+$' : '\u2212$') + Math.abs(Math.round(n)).toLocaleString();
  return (
    <div className="open-pos">
      <div className="open-pos-head">
        <span className="open-pos-eb">Open positions · planned action</span>
        <span className="open-pos-note">carried into today — how each is managed</span>
      </div>
      {positions.length === 0 ?
      <div className="open-pos-empty"><span className="dot" />Flat into the open — no positions carried overnight. Today starts clean.</div> :
      <div className="open-pos-list">
          {positions.map((p, i) => {
          const up = (p.unrealized_pnl_usd || 0) >= 0;
          const isLong = p.direction === 'long';
          const it = intents[p.id];
          const action = it && it.planned_action || p.planned_action || `Manage to plan · stop ${fmtP(p.stop_price)} · trail on follow-through, no adds`;
          const stopTxt = it && it.stop_qqq != null ?
          `${fmtP(it.stop_qqq)} QQQ${it.stop_nq != null ? ` (≈${fmtP(it.stop_nq)} NQ)` : ''}` :
          fmtP(p.stop_price);
          return (
            <div className="open-pos-row" key={p.id || i}>
                <div className="opr-l">
                  <div className="opr-top">
                    <span className={`opr-dir ${isLong ? 'long' : 'short'}`}>{isLong ? '\u25B2 Long' : '\u25BC Short'}</span>
                    <span className="opr-label">{p.label}</span>
                    <span className="opr-size">{p.size} {p.contract}</span>
                    <span className={`opr-pnl ${up ? 'up' : 'down'}`}>{fmtUSD(p.unrealized_pnl_usd)}</span>
                  </div>
                  <div className="opr-legs">
                    <span><i>entry</i> {fmtP(p.entry_price)}</span>
                    <span><i>mark</i> {fmtP(p.current_mark)}</span>
                    <span><i>stop</i> {stopTxt}</span>
                  </div>
                </div>
                <div className="opr-action">
                  <span className="opr-action-eb">Planned action</span>
                  <span className="opr-action-t">{action}</span>
                </div>
              </div>);
        })}
        </div>
      }
    </div>);

}

// ============================================================
// EMAIL CAPTURE — free, notify before the open (stub, persists locally)
// ============================================================
function EmailCapture() {
  const [email, setEmail] = hsUseState('');
  const [done, setDone] = hsUseState(false);
  hsUseEffect(() => {
    try {if (localStorage.getItem('18c:planEmail')) setDone(true);} catch (e) {}
  }, []);
  const submit = (e) => {
    e.preventDefault();
    if (!/.+@.+\..+/.test(email)) return;
    try {localStorage.setItem('18c:planEmail', email);} catch (e2) {}
    setDone(true);
  };
  if (done) {
    return (
      <div className="email-cap done">
        <div className="email-cap-done"><span className="dot" />You're on the list — today's plan lands in your inbox ~30 min before the US open. Free, always.</div>
      </div>);

  }
  return (
    <div className="email-cap">
      <div className="email-cap-copy">
        <div className="email-cap-eb">Free · before the open</div>
        <div className="email-cap-h">Get today's plan in your inbox</div>
        <div className="email-cap-sub">The full pre-market map, ~30 minutes before the bell. No price, no pitch — the map is free.</div>
      </div>
      <form className="email-cap-form" onSubmit={submit}>
        <input className="email-cap-input" type="email" required placeholder="you@email.com" aria-label="Your email" value={email} onChange={(e) => setEmail(e.target.value)} />
        <button className="email-cap-btn" type="submit">Email me the plan</button>
      </form>
    </div>);

}

// ============================================================
// TRADE WITH US — the live desk (Discord), live-only value
// ============================================================
function TradeWithTank({ onJoin }) {
  return (
    <section className="pad" id="cta">
      <div className="t1-cta">
        <div className="t1-cta-eb">the live desk</div>
        <div className="t1-cta-h">The map is free. <em>The operating room is paid.</em></div>
        <p className="t1-cta-p">
          The full daily plan above is public, every day. <b>The Tank’s Trading Desk Discord</b> is the live room: a daily 5-minute video walkthrough and live execution &amp; order commentary as the plan meets the tape. Post-market review isn't locked away — it's the calendar and performance dashboard on this site, free. You're paying to learn the process while it runs, not for signals.
        </p>
        <button className="t1-cta-btn" onClick={onJoin} style={{ marginTop: 14 }}>Join the Club →</button>
        {DSDisclaimer ? <div style={{ marginTop: 14 }}><DSDisclaimer onDark /></div> : null}
      </div>
    </section>);

}

// ============================================================
// MARKET DATA SHEET — today's raw indicator verification sheet.
// Mirrors the calendar workbench's Market Data tab (same .mdv-/.mdf-
// structure), driven by today's premarket_sectors.json sectors.
// ============================================================
function MarketDataSheet() {
  const [pm, setPm] = hsUseState(null);
  const [openSet, setOpenSet] = hsUseState([]);
  hsUseEffect(() => {
    const R = window.__resources || {};
    const url = R.premarket || 'data/premarket_sectors.json';
    fetch(url).then((r) => r.ok ? r.json() : Promise.reject(r.status)).then((d) => {setPm(d);setOpenSet((d.sectors || []).map((_, i) => i));}).catch(() => {});
  }, []);

  const statusCls = (s) => {
    const u = (s || '').toUpperCase();
    if (u === 'BULL' || u === 'BULLISH') return 'bull';
    if (u === 'BEAR' || u === 'BEARISH') return 'bear';
    if (u === 'NEUTRAL' || u === 'NEUT') return 'neut';
    return 'none';
  };
  const confCls = (c) => {
    const u = (c || '').toUpperCase();
    if (u === 'HIGH') return 'high';
    if (u === 'LOW') return 'low';
    return 'med';
  };
  const verdictCls = (v) => v === 'bullish' ? 'bull' : v === 'bearish' ? 'bear' : 'neut';

  if (!pm) {
    return (
      <section className="md-sheet-sec">
        <div className="md-sheet-wrap">
          <div className="md-sheet-head"><h2 className="md-sheet-h">Market Data</h2></div>
          <div style={{ minHeight: 160, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#95A9AB', fontFamily: 'var(--font-sans)' }}>Loading the raw data sheet…</div>
        </div>
      </section>);

  }

  const sectors = pm.sectors || [];
  return (
    <section className="md-sheet-sec" id="market-data">
      <div className="md-sheet-wrap">
        <div className="md-sheet-head">
          <h2 className="md-sheet-h">Market Data</h2>
          <div className="md-sheet-meta">
            <div className="mm"><span className="k">Session</span><span className="v">{pm.meta?.session_label}</span></div>
            <div className="mm"><span className="k">Rulebook</span><span className="v">{(pm.meta?.rulebook || '').replace(/\s*\(.*\)$/, '')}</span></div>
          </div>
        </div>
        <div className="md-scr-bar">
          <button className="md-scr-toggle" onClick={() => setOpenSet(openSet.length === (pm.sectors || []).length ? [] : (pm.sectors || []).map((_, i) => i))}>
            {openSet.length === (pm.sectors || []).length ? 'Collapse all' : 'Expand all'}
          </button>
        </div>
        <table className="md-scr">
          <thead><tr><th>Indicator</th><th>Reading</th><th className="c">Status</th><th className="c">Conf.</th></tr></thead>
          <tbody>
            {sectors.map((g, gi) => {
              const open = openSet.includes(gi);
              return [
              <tr className={'md-scr-mod' + (open ? ' open' : '')} key={'m' + (g.id || gi)} onClick={() => setOpenSet(open ? openSet.filter((i) => i !== gi) : [...openSet, gi])}>
                <td colSpan="2"><span className="md-scr-caret">{open ? '−' : '+'}</span>{g.num ? g.num + ' \u00b7 ' : ''}{g.full_name || g.name}</td>
                <td className={'c md-scr-mc ' + verdictCls(g.verdict)} colSpan="2">{g.score || g.tag}</td>
              </tr>,
              ...(open ? (g.indicators || []).map((r, ri) =>
              <tr className="md-scr-ir" key={(g.id || gi) + '-' + ri}>
                  <td className="md-scr-ind">{r.name}</td>
                  <td className="md-scr-read">{r.reading}</td>
                  <td className={'c md-scr-stat ' + statusCls(r.status)}>{r.status}</td>
                  <td className="c md-scr-conf">{r.conf}</td>
                </tr>) : [])];
            })}
          </tbody>
        </table>
      </div>
    </section>);

}

Object.assign(window, {
  SystemPipeline, PriceScale, TvCharting, PremarketPlanHero, OpenPositions, PlanCandidates, EmailCapture, TradeWithTank, MarketDataSheet
});