// ui.jsx — shared chrome: brand tokens, primitives, mock UI components.

const W = 1920, H = 1080;

// Track current second on the root for comment anchoring.
function TimeLabel() {
  const t = useTime();
  React.useEffect(() => {
    const el = document.getElementById('root');
    if (!el) return;
    const m = Math.floor(t / 60);
    const s = Math.floor(t % 60);
    el.setAttribute('data-screen-label', `${m}:${String(s).padStart(2, '0')}`);
  }, [Math.floor(t)]);
  return null;
}

// ── Top-of-stage chrome: tiny brand stamp + scene caption ────────────────────
function BrandStamp({ caption, sceneNo, totalScenes }) {
  return (
    <div style={{
      position: 'absolute', top: 36, left: 56, right: 56,
      display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      fontFamily: 'var(--sans)', color: 'var(--ink-soft)',
      letterSpacing: '0.16em', textTransform: 'uppercase', fontSize: 13, fontWeight: 500,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
        <WillowMark size={20} />
        <span style={{ color: 'var(--ink)' }}>Willowbridge</span>
        <span style={{ opacity: 0.5 }}>·</span>
        <span>Willowcare Demo Practice</span>
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
        <span>{caption}</span>
        {sceneNo != null && (
          <>
            <span style={{ opacity: 0.5 }}>·</span>
            <span style={{ fontFamily: 'var(--mono)', letterSpacing: '0.04em' }}>
              {String(sceneNo).padStart(2, '0')}/{String(totalScenes).padStart(2, '0')}
            </span>
          </>
        )}
      </div>
    </div>
  );
}

function WillowMark({ size = 24, color = null }) {
  // Canonical WillowBridge icon — mirrors apps/web/public/icon.svg + Login.tsx
  // emblem (a circle with a stem breaking through the top and paired teardrop
  // leaves running down the stem). Two variants:
  //   - badge (default, no `color` prop): full icon with navy rounded-square
  //     bg + bone strokes, suitable for any background
  //   - line  (when `color` is passed): outline-only in `color`, transparent bg
  if (color != null) {
    return (
      <svg width={size} height={size} viewBox="0 0 100 100" fill="none" aria-hidden="true">
        <g transform="translate(50 50) scale(0.82) translate(-50 -50)"
           stroke={color} strokeLinecap="round">
          <path d="M 55.944 14.468 A 38 38 0 1 1 44.056 14.468" strokeWidth="2.6" fill="none"/>
          <path d="M 50 7 L 50 76" strokeWidth="2.6"/>
          <g fill={color} stroke="none">
            <g transform="translate(50 13) rotate(-90) scale(0.55)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
            <g transform="translate(49.7 21) rotate(-30) scale(0.95)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
            <g transform="translate(50.3 21) rotate(-150) scale(0.95)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
            <g transform="translate(49.7 32) rotate(-25) scale(1.05)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
            <g transform="translate(50.3 32) rotate(-155) scale(1.05)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
            <g transform="translate(49.7 44) rotate(-22) scale(1.08)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
            <g transform="translate(50.3 44) rotate(-158) scale(1.08)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
            <g transform="translate(49.7 56) rotate(-20) scale(1.0)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
            <g transform="translate(50.3 56) rotate(-160) scale(1.0)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
            <g transform="translate(49.7 67) rotate(-18) scale(0.85)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
            <g transform="translate(50.3 67) rotate(-162) scale(0.85)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
          </g>
        </g>
      </svg>
    );
  }
  return (
    <svg width={size} height={size} viewBox="0 0 100 100" fill="none" aria-hidden="true">
      <rect width="100" height="100" rx="22" fill="var(--wb-logo-bg, #16284a)"/>
      <g transform="translate(50 50) scale(0.82) translate(-50 -50)"
         stroke="var(--wb-logo-fg, #f1e8d3)" strokeLinecap="round">
        <path d="M 55.944 14.468 A 38 38 0 1 1 44.056 14.468" strokeWidth="2.6" fill="none"/>
        <path d="M 50 7 L 50 76" strokeWidth="2.6"/>
        <g fill="var(--wb-logo-fg, #f1e8d3)" stroke="none">
          <g transform="translate(50 13) rotate(-90) scale(0.55)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
          <g transform="translate(49.7 21) rotate(-30) scale(0.95)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
          <g transform="translate(50.3 21) rotate(-150) scale(0.95)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
          <g transform="translate(49.7 32) rotate(-25) scale(1.05)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
          <g transform="translate(50.3 32) rotate(-155) scale(1.05)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
          <g transform="translate(49.7 44) rotate(-22) scale(1.08)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
          <g transform="translate(50.3 44) rotate(-158) scale(1.08)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
          <g transform="translate(49.7 56) rotate(-20) scale(1.0)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
          <g transform="translate(50.3 56) rotate(-160) scale(1.0)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
          <g transform="translate(49.7 67) rotate(-18) scale(0.85)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
          <g transform="translate(50.3 67) rotate(-162) scale(0.85)"><path d="M 0 0 Q 5 -3 11 0 Q 5 3 0 0 Z"/></g>
        </g>
      </g>
    </svg>
  );
}

// ── Sprite-style fade-in wrapper that accepts a child render fn ──────────────
function Fade({ start, end, dur = 0.5, exit = 0.4, children, style }) {
  return (
    <Sprite start={start} end={end}>
      {({ localTime, duration }) => {
        const exitStart = duration - exit;
        let o = 1, ty = 0;
        if (localTime < dur) {
          const t = Easing.easeOutCubic(clamp(localTime / dur, 0, 1));
          o = t; ty = (1 - t) * 14;
        } else if (localTime > exitStart) {
          const t = Easing.easeInQuad(clamp((localTime - exitStart) / exit, 0, 1));
          o = 1 - t; ty = -t * 8;
        }
        return (
          <div style={{ position: 'absolute', inset: 0, opacity: o, transform: `translateY(${ty}px)`, ...style }}>
            {typeof children === 'function' ? children({ localTime, duration }) : children}
          </div>
        );
      }}
    </Sprite>
  );
}

// ── Window frame mimicking the WillowBridge web app shell ────────────────────
function AppShell({ route = '/worklist', user = 'Mara K.', children, w = 1620, h = 880, x = 150, y = 130 }) {
  return (
    <div style={{
      position: 'absolute', left: x, top: y, width: w, height: h,
      background: 'var(--wb-paper)',
      borderRadius: 14,
      border: '1px solid var(--wb-rule)',
      boxShadow: '0 30px 80px -30px rgba(14,28,48,0.25), 0 2px 0 rgba(255,255,255,0.6) inset',
      overflow: 'hidden',
      fontFamily: 'var(--sans)',
    }}>
      {/* Title bar */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 14,
        padding: '14px 22px',
        background: 'var(--wb-bg)',
        borderBottom: '1px solid var(--wb-rule)',
        fontSize: 14, color: 'var(--wb-ink-3)',
      }}>
        <div style={{ display: 'flex', gap: 7 }}>
          <Dot c="var(--wb-rule-strong)" /><Dot c="var(--wb-rule-strong)" /><Dot c="var(--wb-rule-strong)" />
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginLeft: 18 }}>
          <WillowMark size={20} />
          <span style={{ fontFamily: 'var(--serif)', fontWeight: 500, fontSize: 17, color: 'var(--wb-ink)', letterSpacing: '-0.01em' }}>Willowbridge</span>
        </div>
        <div style={{
          marginLeft: 24,
          padding: '4px 14px',
          background: 'var(--wb-paper)',
          borderRadius: 999,
          border: '1px solid var(--wb-rule)',
          fontFamily: 'var(--mono)', fontSize: 12, color: 'var(--wb-ink-3)',
        }}>
          demo.willowbridge.app{route}
        </div>
        <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 12 }}>
          <span style={{ fontSize: 13, color: 'var(--wb-ink-2)' }}>{user}</span>
          <Avatar name={user} />
        </div>
      </div>

      {/* Left rail */}
      <div style={{ display: 'flex', height: 'calc(100% - 53px)' }}>
        <SideRail route={route} />
        <div style={{ flex: 1, position: 'relative', overflow: 'hidden' }}>
          {children}
        </div>
      </div>
    </div>
  );
}

function Dot({ c }) {
  return <span style={{ width: 11, height: 11, borderRadius: 11, background: c, display: 'inline-block' }} />;
}

function Avatar({ name }) {
  const initials = name.split(' ').map(s => s[0]).slice(0, 2).join('');
  return (
    <div style={{
      width: 30, height: 30, borderRadius: 30,
      background: 'var(--willow)', color: '#fff',
      fontSize: 12, fontWeight: 600,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      letterSpacing: '0.04em',
    }}>{initials}</div>
  );
}

function SideRail({ route }) {
  // athena-marketplace tenants: no /rcm — billing flows through athena
  // and the Willowbridge RCM surface is hidden (ADR 0021).
  const items = [
    ['/worklist',  'Worklist'],
    ['/chart',     'Charts'],
    ['/billing',   'Billing'],
    ['/audit',     'Audit'],
    ['/admin',     'Admin'],
  ];
  return (
    <div style={{
      width: 220, padding: '24px 18px',
      borderRight: '1px solid var(--wb-rule)',
      background: 'var(--wb-panel)',
      display: 'flex', flexDirection: 'column', gap: 4,
    }}>
      {items.map(([r, label]) => {
        const active = route.startsWith(r);
        return (
          <div key={r} style={{
            padding: '10px 14px', borderRadius: 8,
            background: active ? 'var(--wb-primary)' : 'transparent',
            color: active ? 'var(--wb-paper)' : 'var(--wb-ink-2)',
            fontSize: 14, fontWeight: active ? 600 : 500,
            display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          }}>
            <span>{label}</span>
            {active && <span style={{
              fontFamily: 'var(--mono)', fontSize: 11, opacity: 0.65,
            }}>{r}</span>}
          </div>
        );
      })}
      <div style={{ flex: 1 }} />
      <div style={{
        padding: 12, borderRadius: 8,
        background: 'var(--wb-primary-soft)',
        fontSize: 12, color: 'var(--wb-primary)', lineHeight: 1.4,
      }}>
        <div style={{ fontWeight: 600, marginBottom: 4 }}>Period</div>
        <div className="mono" style={{ fontFamily: 'var(--mono)' }}>2026-05</div>
      </div>
    </div>
  );
}

// ── Pills & chips ────────────────────────────────────────────────────────────
// Tonal system matches marketing/index.html pill-badge variants:
//   alert / steel / teal / primary  +  neutral, dark (chrome-tone)
// Legacy names (willow/amber/ok/danger) alias onto the canonical tones.
function Pill({ tone = 'neutral', children, mono = true, size = 'md' }) {
  const tones = {
    neutral:   { bg: 'var(--wb-panel)',         fg: 'var(--wb-ink-2)',  bd: 'var(--wb-rule)' },
    primary:   { bg: 'var(--wb-primary-soft)',  fg: 'var(--wb-primary)', bd: 'transparent' },
    steel:     { bg: 'var(--wb-steel-soft)',    fg: 'var(--wb-steel)',   bd: 'transparent' },
    teal:      { bg: 'var(--wb-teal-soft)',     fg: 'var(--wb-teal)',    bd: 'transparent' },
    alert:     { bg: 'var(--wb-alert-soft)',    fg: 'var(--wb-alert)',   bd: 'transparent' },
    dark:      { bg: 'var(--wb-primary)',       fg: 'var(--wb-paper)',   bd: 'var(--wb-primary)' },
    // legacy aliases
    willow:    { bg: 'var(--wb-primary-soft)',  fg: 'var(--wb-primary)', bd: 'transparent' },
    amber:     { bg: 'var(--wb-steel-soft)',    fg: 'var(--wb-steel)',   bd: 'transparent' },
    danger:    { bg: 'var(--wb-alert-soft)',    fg: 'var(--wb-alert)',   bd: 'transparent' },
    ok:        { bg: 'var(--wb-primary-soft)',  fg: 'var(--wb-primary)', bd: 'transparent' },
  };
  const c = tones[tone] || tones.neutral;
  const pad = size === 'sm' ? '3px 8px' : size === 'lg' ? '8px 16px' : '5px 12px';
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 6,
      padding: pad,
      background: c.bg, color: c.fg,
      border: `1px solid ${c.bd}`,
      borderRadius: 999,
      fontFamily: mono ? 'var(--mono)' : 'var(--sans)',
      fontSize: size === 'lg' ? 15 : size === 'sm' ? 11 : 12,
      fontWeight: 500,
      letterSpacing: mono ? '0.02em' : 0,
    }}>{children}</span>
  );
}

// ── Cursor (for narrative/pointing) ──────────────────────────────────────────
function Cursor({ x, y, label, tone = 'ink' }) {
  return (
    <div style={{
      position: 'absolute', left: x, top: y, pointerEvents: 'none',
      transition: 'none',
      zIndex: 50,
    }}>
      <svg width="22" height="26" viewBox="0 0 22 26" fill="none">
        <path d="M2 2 L 2 21 L 7 17 L 11 25 L 14 23.5 L 10 16 L 17 16 Z"
              fill={tone === 'light' ? '#fff' : 'var(--ink)'}
              stroke={tone === 'light' ? 'var(--ink)' : '#fff'} strokeWidth="1.2" />
      </svg>
      {label && (
        <div style={{
          position: 'absolute', left: 22, top: 20,
          padding: '4px 10px',
          background: 'var(--ink)', color: 'var(--paper)',
          fontFamily: 'var(--mono)', fontSize: 11,
          borderRadius: 6, whiteSpace: 'nowrap',
        }}>{label}</div>
      )}
    </div>
  );
}

// ── Hero text builder: roman + italic-serif emphasis (Willowbridge house style)
function Hero({ children, size = 96, color = 'var(--ink)', align = 'left', lineHeight = 1.05 }) {
  return (
    <div style={{
      fontFamily: 'var(--sans)', fontWeight: 500,
      fontSize: size, color, letterSpacing: '-0.02em',
      lineHeight, textAlign: align, textWrap: 'pretty',
    }}>{children}</div>
  );
}
function Em({ children, size }) {
  return <span style={{
    fontFamily: 'var(--serif)', fontStyle: 'italic', fontWeight: 400,
    fontSize: size, color: 'var(--willow-deep)',
    letterSpacing: '-0.01em',
  }}>{children}</span>;
}

// ── Tween helper: animate a number ramping (easeOut) over a window ──────────
function rampNumber(t, start, dur, from, to, ease = Easing.easeOutCubic) {
  if (t <= start) return from;
  if (t >= start + dur) return to;
  return from + (to - from) * ease((t - start) / dur);
}

// ── Tiny chart-line drawer for graphs ───────────────────────────────────────
function MiniChart({ width, height, points, color = 'var(--willow)', dotIndex = -1, fillBelow = true }) {
  const min = Math.min(...points);
  const max = Math.max(...points);
  const range = max - min || 1;
  const dx = width / (points.length - 1);
  const ys = points.map(p => height - ((p - min) / range) * (height - 12) - 6);
  const path = points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${i * dx} ${ys[i]}`).join(' ');
  return (
    <svg width={width} height={height} style={{ display: 'block' }}>
      {fillBelow && (
        <path d={`${path} L ${width} ${height} L 0 ${height} Z`} fill={color} opacity="0.12" />
      )}
      <path d={path} fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
      {dotIndex >= 0 && (
        <circle cx={dotIndex * dx} cy={ys[dotIndex]} r="4" fill={color} stroke="#fff" strokeWidth="2" />
      )}
    </svg>
  );
}

// ── Background paper texture ────────────────────────────────────────────────
// Tide is a clean cool-blue surface — no warm paper dots. Just the wb-bg color.
function PaperBG() {
  return (
    <div style={{
      position: 'absolute', inset: 0,
      background: 'var(--wb-bg)',
    }} />
  );
}

// ── Scene title block (consistent leader for each scene) ────────────────────
function SceneTitle({ kicker, title, x = 110, y = 110, color = 'var(--ink)', kickerColor = 'var(--willow)' }) {
  return (
    <div style={{ position: 'absolute', left: x, top: y, fontFamily: 'var(--sans)' }}>
      <div style={{
        fontSize: 13, letterSpacing: '0.22em', textTransform: 'uppercase',
        fontWeight: 600, color: kickerColor, marginBottom: 14,
      }}>{kicker}</div>
      <div style={{ fontSize: 56, fontWeight: 500, color, letterSpacing: '-0.02em', lineHeight: 1.05 }}>
        {title}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Marketing-style primitives (cohesive with the brand site screenshots)
// ─────────────────────────────────────────────────────────────────────────────

// Soft pastel patient avatar — circle with initials. Tone palette mirrors the
// "Today's patients" rows on the marketing landing page.
function PatientAvatar({ initials, tone = 'peach', size = 56 }) {
  const tones = {
    peach: { bg: '#f4dcd6', fg: '#7a443a' }, // Edith Marsh
    slate: { bg: '#e6e8ed', fg: '#3a4961' }, // Roy Tanaka
    sage:  { bg: '#dde8e3', fg: '#2c6b5e' }, // Beatrice Liu
    blue:  { bg: '#dce5f1', fg: '#1e3a64' }, // Jorge Fuentes
    coral: { bg: '#f1e1d8', fg: '#7a4032' },
    mint:  { bg: '#d8e6dd', fg: '#2c6b4a' },
  };
  const c = tones[tone] || tones.slate;
  return (
    <div style={{
      width: size, height: size, borderRadius: size,
      background: c.bg, color: c.fg,
      fontFamily: 'var(--sans)', fontWeight: 600,
      fontSize: size * 0.32, letterSpacing: '0.02em',
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      flexShrink: 0,
    }}>{initials}</div>
  );
}

// Marketing-style status pill — UPPERCASE label, mono, rounded-rect (not
// full pill), soft tinted background. Matches "CALL DUE / REVIEW DUE /
// RPM SETUP / READY TO BILL" badges on the landing page.
function StatusPill({ tone = 'alert', children }) {
  const tones = {
    alert: { bg: '#f1dcdd', fg: '#9d4753' }, // CALL DUE
    steel: { bg: '#d8dde6', fg: '#3a4961' }, // REVIEW DUE
    teal:  { bg: '#d6e4e3', fg: '#2c6b6e' }, // RPM SETUP
    primary: { bg: '#19294a', fg: '#f1e8d3' }, // READY TO BILL
    soft:  { bg: '#e3e8f1', fg: '#3a4961' },
  };
  const c = tones[tone] || tones.steel;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center',
      padding: '4px 10px',
      background: c.bg, color: c.fg,
      borderRadius: 5,
      fontFamily: 'var(--mono)',
      fontSize: 11, fontWeight: 600,
      letterSpacing: '0.12em', textTransform: 'uppercase',
    }}>{children}</span>
  );
}

// Marketing-style pill action button — full radius, accent bg, white text,
// with → arrow. Used for "Place call →", "Open chart →", etc.
function ActionButton({ tone = 'primary', children, size = 'md' }) {
  const tones = {
    primary: { bg: '#19294a', fg: '#fff' },        // Open chart / Submit
    alert:   { bg: '#a94957', fg: '#fff' },        // Place call (coral)
    teal:    { bg: '#34707b', fg: '#fff' },        // Pair device
    ghost:   { bg: '#fff', fg: '#19294a', bd: '1px solid var(--wb-rule)' },
  };
  const c = tones[tone] || tones.primary;
  const pad = size === 'sm' ? '7px 14px' : '11px 18px';
  const fs  = size === 'sm' ? 13 : 14;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 8,
      padding: pad,
      background: c.bg, color: c.fg, border: c.bd || 'none',
      borderRadius: 999,
      fontFamily: 'var(--sans)', fontWeight: 500, fontSize: fs,
      letterSpacing: '-0.005em',
    }}>
      {children}
      <span style={{ fontSize: fs + 1, opacity: 0.85 }}>→</span>
    </span>
  );
}

// Mini progress bar — short ruler showing minutes-captured vs target.
function MiniProgress({ value, max, tone = 'primary', width = 120 }) {
  const pct = clamp(value / max, 0, 1);
  const tones = {
    primary: '#19294a',
    alert:   '#a94957',
    teal:    '#34707b',
    steel:   '#5b6e87',
  };
  const c = tones[tone] || tones.primary;
  return (
    <div style={{ width, height: 4, background: '#e3e8f1', borderRadius: 4, overflow: 'hidden' }}>
      <div style={{ width: `${pct * 100}%`, height: '100%', background: c, borderRadius: 4 }} />
    </div>
  );
}

// Marketing-style browser chrome (3 traffic-light dots + URL pill)
function BrowserChrome({ url, w = 1200 }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 14,
      padding: '14px 22px',
      background: '#fff',
      borderBottom: '1px solid var(--wb-rule)',
    }}>
      <div style={{ display: 'flex', gap: 7 }}>
        <Dot c="#dcdfe5" /><Dot c="#dcdfe5" /><Dot c="#dcdfe5" />
      </div>
      <div style={{
        flex: 1, marginLeft: 12,
        padding: '7px 18px',
        background: '#f4f6fa',
        borderRadius: 999,
        fontFamily: 'var(--mono)', fontSize: 13, color: 'var(--wb-ink-3)',
        textAlign: 'left',
      }}>{url}</div>
    </div>
  );
}

// Encounter banner — full-width dark-navy bar with pulsing dot, mono timer,
// encounter title + subtitle, and a white "Document call" pill on the right.
// Mirrors the banner used at the top of the marketing hero mockup.
function EncounterBanner({ timer, title, subtitle, action = 'Document call', paused = false, w = '100%' }) {
  return (
    <div style={{
      width: w, boxSizing: 'border-box',
      display: 'flex', alignItems: 'center', gap: 22,
      padding: '18px 26px',
      background: '#19294a', color: '#fff',
      fontFamily: 'var(--sans)',
    }}>
      {/* Pulse dot */}
      <div style={{ position: 'relative', width: 14, height: 14 }}>
        <div style={{
          width: 14, height: 14, borderRadius: 14,
          background: paused ? '#7a8aa3' : '#69a9ff',
          animation: paused ? 'none' : 'wb-pulse 1.4s ease-out infinite',
        }} />
      </div>
      {/* Timer */}
      <div style={{
        fontFamily: 'var(--mono)', fontWeight: 500,
        fontSize: 26, color: '#fff',
        letterSpacing: '0.04em', fontVariantNumeric: 'tabular-nums',
        minWidth: 100,
      }}>{timer}</div>
      {/* Title block */}
      <div style={{ lineHeight: 1.3, minWidth: 0, flex: '1 1 auto' }}>
        <div style={{
          fontSize: 17, fontWeight: 600, color: '#fff',
          whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
        }}>
          {paused ? 'Paused · outside chart' : title}
        </div>
        <div style={{
          fontSize: 13, color: '#a8b5cd', marginTop: 2,
          whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
        }}>
          {subtitle}
        </div>
      </div>
      {/* Action */}
      <div style={{ marginLeft: 'auto', flexShrink: 0 }}>
        <span style={{
          display: 'inline-flex', alignItems: 'center', gap: 8,
          padding: '9px 18px',
          background: '#fff', color: '#19294a',
          borderRadius: 999,
          fontSize: 14, fontWeight: 500,
        }}>
          <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
            <path d="M2 3h10v8H2z" stroke="currentColor" strokeWidth="1.3"/>
            <path d="M2 3l5 5 5-5" stroke="currentColor" strokeWidth="1.3"/>
          </svg>
          {action}
        </span>
      </div>
      <style>{`@keyframes wb-pulse { 0% { box-shadow: 0 0 0 0 rgba(105,169,255,0.55);} 70% { box-shadow: 0 0 0 14px rgba(105,169,255,0);} 100% { box-shadow: 0 0 0 0 rgba(105,169,255,0);} }`}</style>
    </div>
  );
}

// Decorative offset blob — soft pastel circle that sits behind cards
// (peach + light blue accents in the marketing stat cards).
function CornerBlob({ tone = 'peach', size = 110, x, y, opacity = 0.55 }) {
  const colors = {
    peach: '#f0d8d3',
    blue:  '#dfe6f1',
    sage:  '#dde9e3',
    mint:  '#d8e6dd',
  };
  return (
    <div style={{
      position: 'absolute', left: x, top: y,
      width: size, height: size, borderRadius: size,
      background: colors[tone] || colors.peach,
      opacity, filter: 'blur(2px)',
      pointerEvents: 'none',
    }} />
  );
}

// Numbered step marker — light blue circle with navy digit (1/2/3/4)
function StepNum({ n, size = 32 }) {
  return (
    <div style={{
      width: size, height: size, borderRadius: size,
      background: '#dce5f1', color: '#1e3a64',
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      fontFamily: 'var(--sans)', fontWeight: 500, fontSize: size * 0.44,
      flexShrink: 0,
    }}>{n}</div>
  );
}

Object.assign(window, {
  W, H, TimeLabel, BrandStamp, WillowMark, Fade,
  AppShell, Avatar, SideRail, Dot,
  Pill, Cursor, Hero, Em, rampNumber, MiniChart, PaperBG, SceneTitle,
  // marketing primitives:
  PatientAvatar, StatusPill, ActionButton, MiniProgress, BrowserChrome,
  EncounterBanner, CornerBlob, StepNum,
});
