// hero-demo.jsx — HeroInsurifyDemo: self-playing, looping product demonstration
// for the homepage hero. Five-beat narrative (type → detect → map → gap →
// resolve) driven by an explicit time-based state machine (pure function of t),
// so pause / jump / replay can never desync. CSS keyframes + rAF only — no
// animation libraries. Animates transform/opacity/stroke-dashoffset only; the
// card's dimensions are fixed from first paint (the note pane reserves its
// final height with an invisible sizer).

// ── Exact content (do not alter) ─────────────────────────────────────────────
const HD_SEGS = [
  { t: '58F with chronic axial low-back pain, worse with extension. Failed ' },
  { t: '4 months of PT and NSAIDs', hl: true, row: 0 },
  { t: '. Exam: paraspinal tenderness, pain with ' },
  { t: 'facet loading', hl: true, row: 1 },
  { t: '. ' },
  { t: 'MRI 1/26: facet arthropathy L4-5', hl: true, row: 2 },
  { t: '. Plan: ' },
  { t: 'left L4-5 medial branch block', hl: true, plan: true },
  { t: '.' }
];
const HD_SEGS5 = [
  { t: ' ' },
  { t: 'Baseline VAS 7/10 at rest', hl: true, row: 3, vas: true },
  { t: '.' }
];
const HD_CRITERIA = [
  '≥3 months conservative therapy',
  'Facet-mediated pain on exam',
  'Imaging within 12 months',
  'Baseline VAS pain score'
];
const HD_SUGGESTION = 'Add a baseline VAS pain score to complete this authorization.';
// Illustrative paraphrases only — never presented as verbatim payer text.
const HD_POLICY = [
  'Documented failure of at least 3 months of conservative therapy, including physical therapy and pharmacotherapy.',
  'Exam findings consistent with facet-mediated pain, such as tenderness over the facet joints and pain with loading maneuvers.',
  'Advanced imaging of the lumbar spine within the past 12 months consistent with facet arthropathy at the treated level.',
  'A documented baseline pain score (for example, VAS) recorded before the procedure.'
];

// ── Timeline builder — everything derives from one master time t ────────────
function hdBuildTimeline(speed) {
  const CH = 21 * speed;           // base ms/char, ± natural variance
  let seed = 987654321;
  const rand = () => { seed = (seed * 1103515245 + 12345) % 2147483648; return seed / 2147483648; };

  // Per-char cumulative times for the main note
  const full1 = HD_SEGS.map(s => s.t).join('');
  const charTimes1 = [];
  let acc = 350 * speed;
  for (let i = 0; i < full1.length; i++) {
    acc += CH + (rand() - 0.5) * 16 * speed;
    if (full1[i] === '.' ) acc += 90 * speed; // breath at sentence ends
    charTimes1.push(acc);
  }
  const D1 = acc;

  // Segment char ranges + highlight-on times (200ms after phrase completes)
  let idx = 0;
  const segs1 = HD_SEGS.map(s => {
    const start = idx, end = idx + s.t.length; idx = end;
    return { ...s, start, end, hlAt: s.hl ? charTimes1[end - 1] + 200 : Infinity };
  });
  const planSeg = segs1.find(s => s.plan);
  const chipsAt = charTimes1[planSeg.end - 1] + 150;

  // Beat 3 — rows 0–2, ~800ms apart (scan 300ms → check draws → MET)
  const S = [D1 + 350, D1 + 1150, D1 + 1950];
  // Beat 4 — the gap
  const S3 = D1 + 3400;
  const gapAt = S3 + 350;
  const suggestionAt = S3 + 750;
  // Beat 5 — resolution typing
  const RT = S3 + 2000 * speed + (2000 * (1 - speed)); // hold the tension a full beat
  const full5 = HD_SEGS5.map(s => s.t).join('');
  const charTimes5 = [];
  let acc5 = RT;
  for (let i = 0; i < full5.length; i++) { acc5 += 26 * speed + (rand() - 0.5) * 14 * speed; charTimes5.push(acc5); }
  let idx5 = 0;
  const segs5 = HD_SEGS5.map(s => {
    const start = idx5, end = idx5 + s.t.length; idx5 = end;
    return { ...s, start, end };
  });
  const VE = acc5;
  const vasGreenAt = VE + 300;
  const flipAt = VE + 450;
  const countFourAt = VE + 600;
  const readyAt = VE + 900;
  const holdEnd = readyAt + 2500 * speed;
  const fadeEnd = holdEnd + 400;
  const LOOP = fadeEnd + 120;

  const countUpTo = (arr, t) => {
    let n = 0;
    while (n < arr.length && arr[n] <= t) n++;
    return n;
  };

  const snapshot = (t) => {
    const chars1 = countUpTo(charTimes1, t);
    const chars5 = t >= RT ? countUpTo(charTimes5, t) : 0;
    const rows = HD_CRITERIA.map((_, i) => {
      if (i < 3) {
        if (t < S[i]) return 'idle';
        if (t < S[i] + 300) return 'scan';
        return 'met';
      }
      if (t < S3) return 'idle';
      if (t < gapAt) return 'scan';
      if (t < flipAt) return 'gap';
      return 'met';
    });
    const count =
      (t >= S[0] + 450 ? 1 : 0) + (t >= S[1] + 450 ? 1 : 0) +
      (t >= S[2] + 450 ? 1 : 0) + (t >= countFourAt ? 1 : 0);
    return {
      t,
      chars1, chars5,
      typing: (chars1 < full1.length && t > 200) || (t >= RT && chars5 < full5.length),
      hl: segs1.map(s => t >= s.hlAt),
      chipsIn: t >= chipsAt,
      rows,
      count,
      suggestion: t >= suggestionAt && t < flipAt,
      vasGreen: t >= vasGreenAt,
      ready: t >= readyAt,
      fading: t >= holdEnd,
      beat: t < S[0] ? 0 : t < S3 ? 1 : 2
    };
  };

  return {
    LOOP, snapshot, segs1, segs5, full1, full5,
    jumps: [80, S[0] + 40, S3 - 150],   // Reads · Maps · Closes the gap
    END: holdEnd - 50                    // completed state (reduced motion)
  };
}

// ── Clock: chained setTimeouts drive t; pause/jump/visibility handled here ────
// Mobile Safari throttles rAF and transitionend for offscreen/background
// content, which used to strand the card mid-fade at opacity 0. Rules:
//   · the pre-clock and fallback state is the COMPLETED loop at full opacity
//   · the machine is a chained setTimeout, restarted on every phase (re)entry
//   · pausing mid-fade resets to a clean loop start — never parked mid-fade
//   · a watchdog snaps to the completed state and restarts if ticks stall >2s
function useHdClock(timeline, active, reduced) {
  const [snap, setSnap] = React.useState(() => timeline.snapshot(timeline.END));
  const originRef = React.useRef(null);   // performance.now() at t=0
  const frozenRef = React.useRef(0);      // t while paused
  const timerRef = React.useRef(0);
  const dogRef = React.useRef(0);
  const lastTickRef = React.useRef(0);
  const keyRef = React.useRef('');

  const emit = React.useCallback((t) => {
    const s = timeline.snapshot(t);
    const key = [s.chars1, s.chars5, s.hl.join(''), s.chipsIn, s.rows.join(''),
      s.count, s.suggestion, s.vasGreen, s.ready, s.fading, s.typing, s.beat].join('|');
    if (key !== keyRef.current) { keyRef.current = key; setSnap(s); }
  }, [timeline]);

  React.useEffect(() => {
    if (reduced) { emit(timeline.END); return; }
    if (!active) {
      // freeze — but never park inside the fade window
      if (originRef.current != null) {
        let ft = (performance.now() - originRef.current) % timeline.LOOP;
        if (ft >= timeline.END) ft = 0;
        frozenRef.current = ft;
        originRef.current = null;
      }
      clearTimeout(timerRef.current);
      clearInterval(dogRef.current);
      // whatever is on screen while paused must be fully visible; before the
      // machine has ever run (or at a loop boundary) that means COMPLETED
      emit(frozenRef.current > 0 ? frozenRef.current : timeline.END);
      return;
    }
    originRef.current = performance.now() - frozenRef.current;
    lastTickRef.current = performance.now();
    const tick = () => {
      const now = performance.now();
      lastTickRef.current = now;
      const raw = now - originRef.current;
      // negative t = watchdog hold: show the completed state until t ≥ 0
      emit(raw < 0 ? timeline.END : raw % timeline.LOOP);
      timerRef.current = setTimeout(tick, 45);
    };
    tick();
    // Watchdog: ticks stalled >2s past budget → completed at full opacity,
    // then the loop restarts from the top.
    dogRef.current = setInterval(() => {
      if (performance.now() - lastTickRef.current > 2000) {
        clearTimeout(timerRef.current);
        emit(timeline.END);
        frozenRef.current = 0;
        originRef.current = performance.now() + 1200; // brief completed hold
        tick();
      }
    }, 1000);
    return () => { clearTimeout(timerRef.current); clearInterval(dogRef.current); };
  }, [active, reduced, timeline, emit]);

  const jump = React.useCallback((t0) => {
    frozenRef.current = t0;
    if (originRef.current != null) originRef.current = performance.now() - t0;
    emit(t0);
  }, [emit]);

  return [snap, jump];
}

// ── Note text renderer (shared) ──────────────────────────────────────────────
function HdNoteText({ tl, snap, hoverRow, phraseRefs, onPhraseHover }) {
  const renderSegs = (segs, chars, isBeat5) => segs.map((s, i) => {
    const visible = Math.max(0, Math.min(chars, s.end) - s.start);
    if (visible <= 0) return null;
    const txt = s.t.slice(0, visible);
    if (!s.hl) return <React.Fragment key={(isBeat5 ? 'b' : 'a') + i}>{txt}</React.Fragment>;
    const complete = visible === s.t.length;
    const on = isBeat5 ? complete : (complete && snap.hl[i]);
    const isVas = !!s.vas;
    const tone = isVas ? (snap.vasGreen ? 'g' : 'amber') : 'g';
    const mapped = s.row !== undefined;
    const dimOthers = hoverRow != null;
    const isTarget = mapped && hoverRow === s.row;
    return (
      <span key={(isBeat5 ? 'b' : 'a') + i}
        ref={mapped ? (el) => { phraseRefs.current[s.row] = el; } : undefined}
        onMouseEnter={mapped && onPhraseHover ? () => onPhraseHover(s.row) : undefined}
        onMouseLeave={mapped && onPhraseHover ? () => onPhraseHover(null) : undefined}
        className={'hd-phrase' + (on ? (tone === 'amber' ? ' hd-hl-amber' : ' hd-hl') : '') +
          (isTarget ? ' hd-phrase-target' : '') +
          (dimOthers && !isTarget ? ' hd-dimmable' : '')}>
        {txt}
      </span>
    );
  });
  return (
    <React.Fragment>
      {renderSegs(tl.segs1, snap.chars1, false)}
      {renderSegs(tl.segs5, snap.chars5, true)}
      {snap.typing && <span className="hd-caret" />}
    </React.Fragment>
  );
}

// ── Criterion row (shared) ───────────────────────────────────────────────────
function HdRow({ i, state, compact, hoverRow, onHover, onOpen, rowRefs, open }) {
  const met = state === 'met', gap = state === 'gap', scan = state === 'scan';
  return (
    <div className={'hd-row hd-row-' + state + (hoverRow === i ? ' hd-row-hover' : '')}
      ref={(el) => { rowRefs.current[i] = el; }}
      role="button" tabIndex={0}
      aria-label={HD_CRITERIA[i] + ' — ' + (met ? 'met' : gap ? 'gap' : 'evaluating') + '. View policy basis.'}
      aria-expanded={open === i}
      onMouseEnter={onHover ? () => onHover(i) : undefined}
      onMouseLeave={onHover ? () => onHover(null) : undefined}
      onClick={(e) => { e.stopPropagation(); onOpen(open === i ? null : i); }}
      onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onOpen(open === i ? null : i); } }}
      style={{
        display: 'flex', alignItems: 'center', gap: 10,
        padding: compact ? '6px 10px' : '7px 10px',
        borderRadius: 7, position: 'relative', cursor: 'pointer',
        border: '1px solid ' + (gap ? '#F1D9BB' : 'var(--line)'),
        background: gap ? 'var(--warn-tint)' : '#fff'
      }}>
      {scan && <span className="hd-shimmer" aria-hidden="true" />}
      <span className={'hd-mark' + (met ? ' hd-mark-met' : gap ? ' hd-mark-gap' : '')} style={{
        width: 17, height: 17, borderRadius: 999, flexShrink: 0,
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center'
      }}>
        {met &&
        <svg width="9" height="9" viewBox="0 0 10 10" fill="none" aria-hidden="true">
          <path className="hd-check" d="M1.5 5.5L4 8l4.5-6" stroke="currentColor" strokeWidth="1.8"
            strokeLinecap="round" strokeLinejoin="round" pathLength="1" />
        </svg>}
        {gap &&
        <svg width="9" height="9" viewBox="0 0 10 10" fill="none" aria-hidden="true">
          <path d="M5 1.5v4M5 8v.5" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
        </svg>}
      </span>
      <span style={{
        fontSize: compact ? 12.5 : 13, color: 'var(--ink-2)',
        fontWeight: gap ? 500 : 400, minWidth: 0,
        overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'
      }}>{HD_CRITERIA[i]}</span>
      <span className={'mono hd-tag' + ((met || gap) ? ' hd-tag-in' : '')} style={{
        marginLeft: 'auto', fontSize: 10.5, letterSpacing: '.06em', flexShrink: 0,
        color: gap ? '#8A5A17' : 'var(--accent-ink)'
      }}>
        {met ? 'MET' : gap ? 'GAP' : ''}
      </span>
      {open === i &&
      <div className="hd-popover" style={{ [i >= 2 ? 'bottom' : 'top']: 'calc(100% + 6px)' }}
        onClick={(e) => e.stopPropagation()}>
        <div className="mono" style={{ fontSize: 10, letterSpacing: '.14em', color: 'var(--ink-3)', marginBottom: 6 }}>
          AETNA · CPB 0722
        </div>
        <div style={{ fontSize: 12.5, lineHeight: 1.5, color: 'var(--ink-2)' }}>{HD_POLICY[i]}</div>
        <div style={{ fontSize: 10.5, marginTop: 6, color: 'var(--ink-4)' }}>
          Illustrative paraphrase — not verbatim payer language.
        </div>
      </div>}
    </div>
  );
}

// ── Main component ───────────────────────────────────────────────────────────
function HeroInsurifyDemo() {
  const reduced = typeof window !== 'undefined' && window.matchMedia &&
    window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  const [compact, setCompact] = React.useState(() =>
    typeof window !== 'undefined' && window.matchMedia('(max-width: 760px)').matches);
  const timeline = React.useMemo(() => hdBuildTimeline(compact ? 0.78 : 1), [compact]);

  const [hoverPaused, setHoverPaused] = React.useState(false);
  const [inView, setInView] = React.useState(false);
  const [tabVisible, setTabVisible] = React.useState(true);
  const [fontsReady, setFontsReady] = React.useState(false);
  const [hoverRow, setHoverRow] = React.useState(null);
  const [openPop, setOpenPop] = React.useState(null);
  const [connector, setConnector] = React.useState(null);
  const [flashRow, setFlashRow] = React.useState(null);

  const cardRef = React.useRef(null);
  const rowRefs = React.useRef([]);
  const phraseRefs = React.useRef([]);
  const noteScrollRef = React.useRef(null);

  const active = inView && tabVisible && fontsReady && !hoverPaused && openPop == null;
  const [snap, jump] = useHdClock(timeline, active, reduced);

  // Viewport / tab visibility / fonts
  React.useEffect(() => {
    const el = cardRef.current;
    if (!el) return;
    const io = new IntersectionObserver((es) => setInView(es[0].isIntersecting), { threshold: 0.25 });
    io.observe(el);
    const onVis = () => setTabVisible(!document.hidden);
    document.addEventListener('visibilitychange', onVis);
    if (document.fonts && document.fonts.ready) {
      document.fonts.ready.then(() => setFontsReady(true));
    } else { setFontsReady(true); }
    const mq = window.matchMedia('(max-width: 760px)');
    const onMq = (e) => setCompact(e.matches);
    mq.addEventListener ? mq.addEventListener('change', onMq) : mq.addListener(onMq);
    return () => {
      io.disconnect();
      document.removeEventListener('visibilitychange', onVis);
      mq.removeEventListener ? mq.removeEventListener('change', onMq) : mq.removeListener(onMq);
    };
  }, []);

  // Esc closes the popover
  React.useEffect(() => {
    if (openPop == null) return;
    const onKey = (e) => { if (e.key === 'Escape') setOpenPop(null); };
    const onClick = () => setOpenPop(null);
    window.addEventListener('keydown', onKey);
    window.addEventListener('click', onClick);
    return () => { window.removeEventListener('keydown', onKey); window.removeEventListener('click', onClick); };
  }, [openPop]);

  // Provenance connector (desktop): row ↔ phrase line, drawn in an SVG overlay
  React.useEffect(() => {
    if (compact || hoverRow == null) { setConnector(null); return; }
    const row = rowRefs.current[hoverRow];
    const phrase = phraseRefs.current[hoverRow];
    const card = cardRef.current;
    if (!row || !phrase || !card) { setConnector(null); return; }
    const c = card.getBoundingClientRect();
    const r = row.getBoundingClientRect();
    const p = phrase.getBoundingClientRect();
    setConnector({
      x1: p.right - c.left + 3, y1: p.top - c.top + p.height / 2,
      x2: r.left - c.left - 3, y2: r.top - c.top + r.height / 2
    });
  }, [hoverRow, compact, snap.chars5]);

  // Mobile: keep the newest typed line in view
  React.useEffect(() => {
    const el = noteScrollRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [snap.chars1, snap.chars5, compact]);

  // Row flash: restartable and cancellable. A fixed timer that ignored the
  // previous one meant rapid taps raced each other (and it outlived unmount).
  const flashTimer = React.useRef(null);
  React.useEffect(() => () => clearTimeout(flashTimer.current), []);
  const onRowTap = (i) => {
    clearTimeout(flashTimer.current);
    setFlashRow(i);
    flashTimer.current = setTimeout(() => setFlashRow(null), 900);
  };

  const dots = ['Reads', 'Maps', 'Closes the gap'];

  const noteBody = (
    <HdNoteText tl={timeline} snap={snap} hoverRow={hoverRow}
      phraseRefs={phraseRefs} onPhraseHover={compact ? null : setHoverRow} />
  );

  return (
    <div ref={cardRef} className={'mock hd-card' + (hoverRow != null ? ' hd-has-hover' : '') + (flashRow != null ? ' hd-flash-' + flashRow : '')}
      data-demo-state={snap.ready ? 'ready' : snap.rows[3] === 'gap' ? 'gap' : 'run'}
      onMouseEnter={() => setHoverPaused(true)}
      onMouseLeave={() => { setHoverPaused(false); setHoverRow(null); }}
      aria-label="Product demonstration: Insurify reads the clinical note as it is written, maps the payer's criteria, finds the documentation gap, and shows the fix before submission."
      style={{ borderRadius: 16, overflow: 'hidden', background: '#fff', position: 'relative' }}>
      <style>{`
        .hd-caret { display: inline-block; width: 1.5px; height: 1em; background: var(--accent);
          vertical-align: -0.15em; margin-left: 1px; animation: hdBlink 1.06s steps(1) infinite; }
        @keyframes hdBlink { 0%, 55% { opacity: 1; } 56%, 100% { opacity: 0; } }
        .hd-phrase { border-radius: 3px; transition: background .3s ease, color .3s ease, opacity .25s ease, box-shadow .25s ease; }
        .hd-hl { background: var(--accent-tint); color: var(--accent-ink); padding: 1px 4px; }
        .hd-hl-amber { background: #F6E3C6; color: #8A5A17; padding: 1px 4px; }
        .hd-phrase-target { box-shadow: 0 0 0 2px var(--accent); background: var(--accent-tint); color: var(--accent-ink); padding: 1px 4px; }
        .hd-has-hover .hd-dimmable, .hd-has-hover .hd-note-plain { opacity: .55; }
        .hd-note-plain { transition: opacity .25s ease; }
        .hd-row { transition: border-color .25s ease, background .25s ease; overflow: visible; }
        .hd-row-met { animation: hdSettle .3s ease; }
        @keyframes hdSettle { 0% { transform: translateY(-2px); } 100% { transform: translateY(0); } }
        .hd-row-gap { animation: hdGapPulse 1.1s ease 2; }
        @keyframes hdGapPulse { 0%,100% { box-shadow: 0 0 0 0 rgba(200,138,23,0); } 50% { box-shadow: 0 0 0 4px rgba(200,138,23,.18); } }
        .hd-row-hover { border-color: var(--accent) !important; }
        .hd-shimmer { position: absolute; inset: 0; overflow: hidden; border-radius: 7px; pointer-events: none; }
        .hd-shimmer::after { content: ""; position: absolute; top: 0; bottom: 0; width: 40%;
          background: linear-gradient(100deg, transparent, rgba(31,92,61,.09), transparent);
          animation: hdScan .32s linear infinite; }
        @keyframes hdScan { from { transform: translateX(-120%); } to { transform: translateX(320%); } }
        .hd-mark { background: var(--paper-2); color: var(--ink-4); transition: background .2s ease, color .2s ease; }
        .hd-mark-met { background: var(--accent-tint); color: var(--accent-ink); }
        .hd-mark-gap { background: #F6E3C6; color: #8A5A17; }
        .hd-check { stroke-dasharray: 1; stroke-dashoffset: 1; animation: hdDraw .25s ease forwards; }
        @keyframes hdDraw { to { stroke-dashoffset: 0; } }
        .hd-tag { opacity: 1; transition: opacity .25s ease; }
        .hd-tag-in { opacity: 1; }
        .hd-bar { transform-origin: left center; transition: transform .5s cubic-bezier(.34,1.4,.4,1); }
        .hd-chips { opacity: 1; transform: none; transition: opacity .4s ease, transform .55s cubic-bezier(.34,1.5,.4,1); }
        .hd-chips-pre { opacity: 0; transform: translateX(14px); }
        .hd-live-dot { animation: hdLive 2.4s ease-in-out infinite; }
        @keyframes hdLive { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
        .hd-suggest { animation: hdFadeUp .4s ease both; }
        @keyframes hdFadeUp { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: translateY(0); } }
        .hd-flip { animation: hdFlip .35s ease; }
        @keyframes hdFlip { 0% { transform: rotateX(70deg); opacity: .3; } 100% { transform: rotateX(0); opacity: 1; } }
        .hd-ready { animation: hdFadeUp .35s ease both; }
        .hd-tick { display: inline-block; animation: hdFadeUp .3s ease both; }
        .hd-fade { transition: opacity .18s ease; }
        .hd-connector path { stroke-dasharray: 6 4; animation: hdDash .5s ease both; }
        @keyframes hdDash { from { opacity: 0; } to { opacity: 1; } }
        .hd-popover { position: absolute; right: 0; z-index: 6; width: 250px;
          background: var(--paper); border: 1px solid var(--line); border-radius: 10px;
          padding: 12px 14px; text-align: left; cursor: default;
          box-shadow: 0 12px 32px -12px rgba(15,20,20,.25); animation: hdFadeUp .18s ease both; }
        .hd-dots { display: flex; align-items: center; gap: 14px; }
        .hd-dot-btn { appearance: none; border: 0; background: transparent; padding: 2px 4px;
          font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .08em;
          color: var(--ink-4); cursor: pointer; display: inline-flex; align-items: center; gap: 6px;
          text-transform: uppercase; border-radius: 4px; }
        .hd-dot-btn .hd-dot { width: 5px; height: 5px; border-radius: 999px; background: var(--line); transition: background .2s ease; }
        .hd-dot-btn.hd-dot-on { color: var(--ink-2); }
        .hd-dot-btn.hd-dot-on .hd-dot { background: var(--accent); }
        @keyframes hdFlash { 0%,100% { box-shadow: none; } 30% { box-shadow: 0 0 0 3px var(--accent-tint-2); } }
        .hd-flash-0 .hd-phrase:nth-of-type(1), .hd-flash-1 .hd-phrase:nth-of-type(2),
        .hd-flash-2 .hd-phrase:nth-of-type(3), .hd-flash-3 .hd-phrase:nth-of-type(5) {
          animation: hdFlash .9s ease; background: var(--accent-tint); }
        @media (prefers-reduced-motion: reduce) {
          .hd-caret, .hd-shimmer::after { animation: none; display: none; }
          .hd-row-met, .hd-row-gap, .hd-check, .hd-chips-in, .hd-live-dot, .hd-suggest,
          .hd-flip, .hd-ready, .hd-tick { animation: none; }
          .hd-check { stroke-dashoffset: 0; }
          .hd-bar { transition: none; }
        }
      `}</style>

      {/* Top bar */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 10, padding: '13px 18px',
        borderBottom: '1px solid var(--line)', background: 'var(--paper-2)'
      }}>
        <span style={{
          width: 24, height: 24, borderRadius: 7, background: 'var(--accent)', color: '#fff',
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          fontSize: 12, fontWeight: 600, flexShrink: 0
        }}>I</span>
        <span style={{ fontSize: 14, fontWeight: 500 }}>Insurify</span>
        <span className="mono" style={{
          fontSize: 12, color: 'var(--accent-ink)', letterSpacing: '.06em',
          display: 'inline-flex', alignItems: 'center', gap: 5
        }}>
          <span className={snap.chipsIn ? 'hd-live-dot' : ''} style={{ width: 5, height: 5, borderRadius: 999, background: 'var(--accent)', display: 'inline-block' }} />
          LIVE
        </span>
        <span style={{ marginLeft: 'auto', display: 'inline-flex', alignItems: 'center', gap: 10, minWidth: 0 }}>
          {snap.ready ?
          <span className="hd-ready mono" style={{
            fontSize: 11, letterSpacing: '.06em', color: 'var(--accent-ink)',
            background: 'var(--accent-tint)', border: '1px solid var(--accent-tint-2)',
            padding: '3px 8px', borderRadius: 999, whiteSpace: 'nowrap'
          }}>Ready to submit ✓</span> :
          <span className={'mono hd-chips' + (snap.chipsIn ? '' : ' hd-chips-pre')} style={{
            fontSize: 12, color: 'var(--ink-3)', letterSpacing: '.04em', whiteSpace: 'nowrap',
            overflow: 'hidden', textOverflow: 'ellipsis'
          }}>
            Aetna · CPB 0722 · Lumbar MBB
          </span>}
        </span>
      </div>

      {/* Body */}
      <div className="hd-fade" style={{ opacity: snap.fading ? 0 : 1, position: 'relative' }}>
        {compact ?
        /* ── Compact stacked variant (<760px) ── */
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10, padding: '14px 16px 12px' }}>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
            <div className="eyebrow" style={{ fontSize: 11 }}>Payer criteria</div>
            {HD_CRITERIA.map((_, i) =>
            <HdRow key={i} i={i} state={snap.rows[i]} compact
              hoverRow={null} onHover={null}
              onOpen={(v) => { setOpenPop(v); if (v != null) onRowTap(v); }}
              rowRefs={rowRefs} open={openPop} />
            )}
          </div>
          <div>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
              <span className="eyebrow" style={{ fontSize: 11 }}>Authorization readiness</span>
              <span className="mono" style={{ fontSize: 13, fontWeight: 500 }}>
                <span key={snap.count} className="hd-tick">{snap.count}</span> of 4
              </span>
            </div>
            <div style={{ height: 6, background: 'var(--line-2)', borderRadius: 999, marginTop: 6, overflow: 'hidden' }}>
              <div className="hd-bar" style={{ width: '100%', height: '100%', background: 'var(--accent)', transform: 'scaleX(' + (snap.count / 4) + ')' }} />
            </div>
          </div>
          <div>
            <div className="eyebrow" style={{ fontSize: 11, marginBottom: 6 }}>Clinical note</div>
            <div ref={noteScrollRef} style={{ height: 46, overflow: 'hidden', position: 'relative' }}>
              <p className="hd-note-plain" style={{ margin: 0, fontSize: 13, lineHeight: 1.7, color: 'var(--ink-2)' }}>
                {noteBody}
              </p>
            </div>
          </div>
          {snap.suggestion &&
          <div className="hd-suggest" style={{ fontSize: 12, color: '#8A5A17', lineHeight: 1.45 }}>
            {HD_SUGGESTION}
          </div>}
        </div> :

        /* ── Desktop split variant ── */
        <div style={{ display: 'grid', gridTemplateColumns: '1.1fr 1fr' }}>
          {/* Connector overlay */}
          {connector &&
          <svg className="hd-connector" aria-hidden="true" style={{
            position: 'absolute', inset: 0, width: '100%', height: '100%',
            pointerEvents: 'none', zIndex: 5
          }}>
            <path d={'M ' + connector.x1 + ' ' + connector.y1 +
              ' C ' + (connector.x1 + 34) + ' ' + connector.y1 + ', ' +
              (connector.x2 - 34) + ' ' + connector.y2 + ', ' + connector.x2 + ' ' + connector.y2}
              stroke="var(--accent)" strokeWidth="1" fill="none" />
            <circle cx={connector.x1} cy={connector.y1} r="2" fill="var(--accent)" />
            <circle cx={connector.x2} cy={connector.y2} r="2" fill="var(--accent)" />
          </svg>}

          {/* Left: the note */}
          <div style={{ padding: '20px 24px', borderRight: '1px solid var(--line)' }}>
            <div className="eyebrow" style={{ fontSize: 11, marginBottom: 12 }}>Clinical note</div>
            <div style={{ position: 'relative' }}>
              {/* invisible sizer reserves the final height — no CLS */}
              <p aria-hidden="true" style={{ margin: 0, fontSize: 14.5, lineHeight: 1.7, visibility: 'hidden' }}>
                {timeline.full1}<span style={{ padding: '1px 4px' }}>{timeline.full5}</span>
              </p>
              <p className="hd-note-plain" style={{
                position: 'absolute', inset: 0, margin: 0,
                fontSize: 14.5, lineHeight: 1.7, color: 'var(--ink-2)'
              }}>
                {noteBody}
              </p>
            </div>
          </div>

          {/* Right: criteria + readiness */}
          <div style={{ padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 8 }}>
            <div className="eyebrow" style={{ fontSize: 11, marginBottom: 4 }}>Payer criteria</div>
            {HD_CRITERIA.map((_, i) =>
            <div key={i} className={snap.rows[i] === 'met' && i === 3 ? 'hd-flip' : ''}>
              <HdRow i={i} state={snap.rows[i]} compact={false}
                hoverRow={hoverRow} onHover={setHoverRow}
                onOpen={setOpenPop} rowRefs={rowRefs} open={openPop} />
            </div>
            )}
            <div className={snap.suggestion ? 'hd-suggest' : ''} style={{
              fontSize: 12, color: '#8A5A17', lineHeight: 1.45, minHeight: 18,
              opacity: snap.suggestion ? 1 : 0, transition: 'opacity .35s ease'
            }}>
              {HD_SUGGESTION}
            </div>
            <div style={{ marginTop: 2 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
                <span className="eyebrow" style={{ fontSize: 11 }}>Authorization readiness</span>
                <span className="mono" style={{ fontSize: 13, fontWeight: 500 }}>
                  <span key={snap.count} className="hd-tick">{snap.count}</span> of 4
                </span>
              </div>
              <div style={{ height: 6, background: 'var(--line-2)', borderRadius: 999, marginTop: 7, overflow: 'hidden' }}>
                <div className="hd-bar" style={{ width: '100%', height: '100%', background: 'var(--accent)', transform: 'scaleX(' + (snap.count / 4) + ')' }} />
              </div>
            </div>
          </div>
        </div>}

        {/* Step dots + replay */}
        <div style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          padding: compact ? '2px 16px 10px' : '0 24px 12px'
        }}>
          <div className="hd-dots" role="group" aria-label="Demo steps">
            {dots.map((d, i) =>
            <button key={d} className={'hd-dot-btn' + (snap.beat === i ? ' hd-dot-on' : '')}
              aria-label={'Jump to step: ' + d}
              onClick={(e) => { e.stopPropagation(); jump(timeline.jumps[i]); }}>
              <span className="hd-dot" />{d}
            </button>
            )}
          </div>
          <button className="hd-dot-btn" aria-label="Replay demo"
            onClick={(e) => { e.stopPropagation(); jump(0); }}
            style={{ fontSize: 13 }}>↺</button>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { HeroInsurifyDemo });
