// features.jsx — new interactive features

// ─────────────────────────────────────────────────────────────
// SunlitGlass — stained-glass with live sun position
// ─────────────────────────────────────────────────────────────
function SunlitGlass({ theme, height = 220, seed = 1 }) {
  const [now, setNow] = React.useState(new Date());
  React.useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 60000);
    return () => clearInterval(id);
  }, []);
  // map 6am–8pm to 0..1
  const h = now.getHours() + now.getMinutes() / 60;
  const dayPos = Math.max(0, Math.min(1, (h - 6) / 14));
  const isNight = h < 6 || h > 20;
  const sunX = 50 + (dayPos - 0.5) * 260; // sweep across
  const sunY = 110 - Math.sin(dayPos * Math.PI) * 80; // arc
  const intensity = isNight ? 0.15 : (1 - Math.abs(dayPos - 0.5) * 1.2);
  const palette = [theme.accent, theme.gold, '#3a5a7a', '#3a2858', '#3a5a3a'];

  // Tall Gothic lancet with abstract Celtic cross — lit by the sun
  const stoneStroke = theme.ink;
  const arch = "M40 240 L40 110 Q40 28 100 14 Q160 28 160 110 L160 240 Z";
  // Sun-driven brightness falloff across the field
  const sunBrightness = isNight ? 0 : Math.max(0, 1 - Math.abs(100 - sunX) / 110) * intensity;
  return (
    <div style={{ width: '100%', height, position: 'relative', overflow: 'hidden', background: theme.bgAlt, borderTop: `1px solid ${theme.rule}`, borderBottom: `1px solid ${theme.rule}` }}>
      <svg viewBox="0 0 200 240" preserveAspectRatio="xMidYMid meet" width="100%" height="100%" style={{ display: 'block' }}>
        <defs>
          <radialGradient id={`sun-${seed}`} cx="0.5" cy="0.5" r="0.5">
            <stop offset="0%" stopColor={isNight ? '#cfd8e8' : '#fff5d8'} stopOpacity={isNight ? 0.45 : 0.95}/>
            <stop offset="60%" stopColor={isNight ? '#cfd8e8' : '#fff5d8'} stopOpacity="0"/>
          </radialGradient>
          <clipPath id={`lancet-sun-${seed}`}>
            <path d={arch}/>
          </clipPath>
          <radialGradient id={`bg-sun-${seed}`} cx="0.5" cy="0.55" r="0.65">
            <stop offset="0%" stopColor={palette[(seed + 1) % palette.length]} stopOpacity="0.85"/>
            <stop offset="100%" stopColor={palette[(seed + 3) % palette.length]} stopOpacity="0.95"/>
          </radialGradient>
        </defs>

        {/* sun halo behind glass */}
        <circle cx={sunX} cy={sunY + 30} r="100" fill={`url(#sun-${seed})`} opacity={isNight ? 0.7 : 1}/>

        {/* stone arch */}
        <path d={arch} fill={theme.bgAlt} stroke={stoneStroke} strokeOpacity="0.55" strokeWidth="3"/>

        <g clipPath={`url(#lancet-sun-${seed})`}>
          {/* Background glass with sun-driven opacity */}
          <rect x="0" y="0" width="200" height="240" fill={`url(#bg-sun-${seed})`} opacity={isNight ? 0.55 : 0.7 + sunBrightness * 0.3}/>

          {/* Light bloom across the field */}
          {!isNight && sunBrightness > 0.1 && (
            <rect x="0" y="0" width="200" height="240" fill="#fff5d8" opacity={sunBrightness * 0.35}/>
          )}

          {/* Lead came lattice */}
          {(() => {
            const lines = [];
            for (let y = 30; y <= 240; y += 22) {
              lines.push(<line key={`h${y}`} x1="20" y1={y} x2="180" y2={y} stroke={stoneStroke} strokeOpacity="0.18" strokeWidth="0.6"/>);
            }
            for (let x = 50; x <= 150; x += 22) {
              lines.push(<line key={`v${x}`} x1={x} y1="20" x2={x} y2="240" stroke={stoneStroke} strokeOpacity="0.18" strokeWidth="0.6"/>);
            }
            return lines;
          })()}

          {/* Celtic cross — nimbus halo */}
          <circle cx="100" cy="118" r="34" fill={theme.gold} opacity={isNight ? 0.35 : 0.42 + sunBrightness * 0.3}/>
          <circle cx="100" cy="118" r="34" fill="none" stroke={stoneStroke} strokeOpacity="0.55" strokeWidth="3"/>
          <circle cx="100" cy="118" r="27" fill="none" stroke={stoneStroke} strokeOpacity="0.4" strokeWidth="1.2"/>

          {/* Vertical bar */}
          <rect x="92" y="58" width="16" height="158" fill={palette[seed % palette.length]} opacity={isNight ? 0.6 : 0.92}
                stroke={stoneStroke} strokeOpacity="0.6" strokeWidth="2"/>
          {/* Horizontal crossbar */}
          <rect x="56" y="110" width="88" height="16" fill={palette[seed % palette.length]} opacity={isNight ? 0.6 : 0.92}
                stroke={stoneStroke} strokeOpacity="0.6" strokeWidth="2"/>

          {/* Center jewel */}
          <circle cx="100" cy="118" r="6" fill={theme.gold} stroke={stoneStroke} strokeOpacity="0.6" strokeWidth="1.2"/>

          {/* Small jewels along the cross arms */}
          <circle cx="100" cy="78" r="3" fill={theme.gold} stroke={stoneStroke} strokeOpacity="0.5" strokeWidth="0.8"/>
          <circle cx="100" cy="200" r="3" fill={theme.gold} stroke={stoneStroke} strokeOpacity="0.5" strokeWidth="0.8"/>
          <circle cx="68" cy="118" r="3" fill={theme.gold} stroke={stoneStroke} strokeOpacity="0.5" strokeWidth="0.8"/>
          <circle cx="132" cy="118" r="3" fill={theme.gold} stroke={stoneStroke} strokeOpacity="0.5" strokeWidth="0.8"/>
        </g>

        <path d={arch} fill="none" stroke={stoneStroke} strokeOpacity="0.55" strokeWidth="2"/>
      </svg>
      <div style={{ position: 'absolute', top: 8, left: 12, ...smallCaps(theme, 8), color: theme.inkMute, fontFamily: theme.fonts.mono }}>
        {isNight ? '✦ vigil light' : '☀ ' + now.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// DailyOfficeCard — small widget showing next office + countdown
// ─────────────────────────────────────────────────────────────
function DailyOfficeCard({ theme, onOpen }) {
  const [now, setNow] = React.useState(new Date());
  React.useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 30000);
    return () => clearInterval(id);
  }, []);
  const next = nextOffice(now);
  const cur = currentOffice(now);
  const hrs = Math.floor(next.when / 60);
  const mins = next.when % 60;
  const offices = OFFICES;
  const minsNow = now.getHours() * 60 + now.getMinutes();
  return (
    <div style={{ padding: '12px 22px 0' }}>
      <button onClick={onOpen} style={{ width: '100%', background: theme.paper, border: `1px solid ${theme.rule}`, padding: 16, cursor: 'pointer', textAlign: 'left' }}>
        <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 10 }}>
          <span style={{ ...smallCaps(theme, 9), color: theme.gold }}>The Daily Office</span>
          <span style={{ ...smallCaps(theme, 9), color: theme.inkMute }}>now: {cur.alt}</span>
        </div>
        <div style={{ fontFamily: theme.fonts.display, fontSize: 22, color: theme.ink, fontWeight: 500, lineHeight: 1.1 }}>
          <span style={{ fontStyle: 'italic', color: theme.accent }}>{next.alt}</span> <span style={{ color: theme.inkSoft }}>begins in</span>
        </div>
        <div style={{ fontFamily: theme.fonts.display, fontSize: 32, color: theme.ink, fontWeight: 500, marginTop: 4 }}>
          {hrs > 0 && <>{hrs}<span style={{ ...smallCaps(theme, 11), color: theme.inkMute, marginLeft: 3 }}>h</span> </>}
          {String(mins).padStart(2, '0')}<span style={{ ...smallCaps(theme, 11), color: theme.inkMute, marginLeft: 3 }}>m</span>
        </div>
        {/* timeline of offices */}
        <div style={{ position: 'relative', marginTop: 14, height: 26 }}>
          <div style={{ position: 'absolute', top: 11, left: 0, right: 0, height: 1, background: theme.rule }}/>
          {offices.map((o, i) => {
            const pct = (o.hour * 60 + o.minute) / (24 * 60) * 100;
            const passed = (o.hour * 60 + o.minute) <= minsNow;
            return (
              <div key={o.id} style={{ position: 'absolute', left: pct + '%', top: 0, transform: 'translateX(-50%)', textAlign: 'center' }}>
                <div style={{ width: 7, height: 7, borderRadius: '50%', background: passed ? theme.gold : theme.bgAlt, border: `1px solid ${theme.gold}`, margin: '8px auto 0' }}/>
                <div style={{ ...smallCaps(theme, 7), color: theme.inkMute, marginTop: 2, fontSize: 7 }}>{o.alt.slice(0, 3)}</div>
              </div>
            );
          })}
          <div style={{ position: 'absolute', left: minsNow / (24 * 60) * 100 + '%', top: 6, width: 1, height: 16, background: theme.accent }}/>
        </div>
      </button>
    </div>
  );
}

// Modal showing the current office
function OfficeModal({ theme, onClose }) {
  const cur = currentOffice();
  const c = OFFICE_CONTENT[cur.id] || OFFICE_CONTENT.compline;
  return (
    <ModalShell theme={theme} onClose={onClose} title={cur.name} subtitle={cur.alt}>
      <div style={{ padding: '18px 22px' }}>
        <MissalRule theme={theme} label="Versicle" />
        <div style={{ fontFamily: theme.fonts.display, fontSize: 19, color: theme.ink, fontStyle: 'italic', lineHeight: 1.4 }}>
          ℣  {c.versicle}
        </div>
        <div style={{ fontFamily: theme.fonts.display, fontSize: 19, color: theme.accent, fontStyle: 'italic', lineHeight: 1.4, marginTop: 6 }}>
          ℟  {c.response}
        </div>
        <MissalRule theme={theme} label="Psalmody" />
        <div style={{ fontFamily: theme.fonts.display, fontSize: 17, color: theme.ink }}>{c.psalm}</div>
        <MissalRule theme={theme} label="Canticle" />
        <div style={{ fontFamily: theme.fonts.display, fontSize: 17, color: theme.ink }}>{c.canticle}</div>
        <MissalRule theme={theme} label="" />
        <div style={{ fontFamily: theme.fonts.display, fontStyle: 'italic', fontSize: 14, color: theme.inkSoft, lineHeight: 1.5 }}>
          The grace of our Lord Jesus Christ, and the love of God, and the fellowship of the Holy Spirit, be with us all evermore.
          <span style={{ ...smallCaps(theme, 10), color: theme.gold, fontStyle: 'normal', display: 'block', marginTop: 6 }}>Amen.</span>
        </div>
      </div>
    </ModalShell>
  );
}

// ─────────────────────────────────────────────────────────────
// LectionaryModal — full readings as a missal page
// ─────────────────────────────────────────────────────────────
function LectionaryModal({ theme, onClose, reading }) {
  const r = LECTIONARY_FULL[reading] || LECTIONARY_FULL.gospel;
  return (
    <ModalShell theme={theme} onClose={onClose} title={r.title} subtitle={r.citation}>
      <div style={{ padding: '18px 22px', background: theme.paper }}>
        <div style={{ fontFamily: theme.fonts.display, fontSize: 16, lineHeight: 1.65, color: theme.ink, fontWeight: 400 }}>
          {r.body.map((p, i) => (
            <p key={i} style={{ margin: '0 0 14px', textIndent: i === 0 ? 0 : 18 }}>
              {i === 0 && <DropCap letter={p.charAt(0)} theme={theme} size={48} />}
              {i === 0 ? p.slice(1) : p}
            </p>
          ))}
        </div>
        <MissalRule theme={theme} label="Hear what the Spirit is saying" />
        <div style={{ display: 'flex', gap: 8 }}>
          <button style={{ flex: 1, padding: '12px', background: theme.ink, color: theme.paper, border: 'none', ...smallCaps(theme, 10), cursor: 'pointer' }}>Send to Bible app</button>
          <button onClick={() => softChime(660)} style={{ flex: 1, padding: '12px', background: 'transparent', color: theme.ink, border: `1px solid ${theme.ink}`, ...smallCaps(theme, 10), cursor: 'pointer' }}>♪ Listen</button>
        </div>
      </div>
    </ModalShell>
  );
}

// ─────────────────────────────────────────────────────────────
// SaintRibbon — slim ribbon between season & hero
// ─────────────────────────────────────────────────────────────
function SaintRibbon({ theme }) {
  const s = saintToday();
  if (!s) return null;
  return (
    <div style={{
      display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
      padding: '4px 18px',
      background: theme.paper, color: theme.gold,
      borderBottom: `1px solid ${theme.ruleSoft}`,
      ...smallCaps(theme, 9),
    }}>
      <span>★</span>
      <span style={{ color: theme.inkSoft, fontStyle: 'italic', textTransform: 'none', letterSpacing: 0, fontFamily: theme.fonts.display, fontSize: 13 }}>
        Today we remember
      </span>
      <span style={{ color: theme.accent }}>{s.name}</span>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// softChime — a brief gentle tone played between prayer lines.
// Wrapped in try/catch so that if AudioContext is blocked (Safari
// before first user gesture, content-blockers, etc.) the prayer flow
// still advances. Previously this function was undefined, which threw
// a ReferenceError every cue and froze the modal on line 0.
// ─────────────────────────────────────────────────────────────
function softChime(freq = 880) {
  try {
    const Ctx = window.AudioContext || window.webkitAudioContext;
    if (!Ctx) return;
    if (!window.__chimeCtx) window.__chimeCtx = new Ctx();
    const ctx = window.__chimeCtx;
    if (ctx.state === 'suspended' && ctx.resume) { try { ctx.resume(); } catch (e) {} }
    const t = ctx.currentTime;
    const osc = ctx.createOscillator();
    const gain = ctx.createGain();
    osc.type = 'sine';
    osc.frequency.setValueAtTime(freq, t);
    gain.gain.setValueAtTime(0, t);
    gain.gain.linearRampToValueAtTime(0.06, t + 0.02);
    gain.gain.exponentialRampToValueAtTime(0.0001, t + 1.0);
    osc.connect(gain).connect(ctx.destination);
    osc.start(t);
    osc.stop(t + 1.05);
  } catch (e) { /* silent — audio is a nice-to-have */ }
}

// ─────────────────────────────────────────────────────────────
// PrayWithUs — Lord's Prayer at a meditative cadence, then this
// week's Prayer of the Day, then close.
// ─────────────────────────────────────────────────────────────
const LORDS_PRAYER = [
  'Our Father, who art in heaven,',
  'hallowed be thy Name.',
  'Thy kingdom come,',
  'thy will be done,',
  'on earth as it is in heaven.',
  'Give us this day our daily bread.',
  'And forgive us our trespasses,',
  'as we forgive those who trespass against us.',
  'And lead us not into temptation,',
  'but deliver us from evil.',
  'For thine is the kingdom,',
  'and the power, and the glory,',
  'for ever and ever.',
  'Amen.',
];

function PrayWithUsModal({ theme, onClose }) {
  const [phase, setPhase] = React.useState('lords'); // 'lords' | 'collect'
  const [idx, setIdx] = React.useState(0);
  const [paused, setPaused] = React.useState(false);

  // Pull this week's Collect (the Prayer of the Day) — auto-changes by Sunday.
  const [collect] = React.useState(() => {
    try { return (typeof getCollect === 'function') ? getCollect() : null; } catch (e) { return null; }
  });

  // Auto-advance through the Lord's Prayer; on the last line, transition
  // to the Collect phase after a short pause.
  React.useEffect(() => {
    if (phase !== 'lords' || paused) return;
    if (idx >= LORDS_PRAYER.length - 1) {
      if (!collect || !collect.text) return;
      const t = setTimeout(() => setPhase('collect'), 4200);
      return () => clearTimeout(t);
    }
    const t = setTimeout(() => {
      softChime(idx % 2 === 0 ? 880 : 660);
      setIdx(i => i + 1);
    }, 3800);
    return () => clearTimeout(t);
  }, [idx, paused, phase, collect]);

  // Initial chime on mount (gracefully no-ops if AudioContext is blocked).
  React.useEffect(() => { softChime(880); }, []);

  // Soft chime on phase change to mark the transition.
  React.useEffect(() => { if (phase === 'collect') softChime(550); }, [phase]);

  const restart = () => { setPhase('lords'); setIdx(0); setPaused(false); };

  if (phase === 'collect' && collect) {
    return (
      <ModalShell theme={theme} onClose={onClose} title="Pray with us" subtitle="This Week's Prayer">
        <div style={{ padding: '40px 28px 60px', minHeight: 380, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', textAlign: 'center', background: theme.paper, animation: 'fadein 0.8s ease both' }}>
          {collect.name && (
            <div style={{ ...smallCaps(theme, 9), color: theme.accent, marginBottom: 18 }}>
              {collect.name}
            </div>
          )}
          <div style={{
            fontFamily: theme.fonts.display, fontSize: 22, fontStyle: 'italic',
            color: theme.ink, lineHeight: 1.55, fontWeight: 400, maxWidth: 540,
          }}>
            {collect.text} <span style={{ ...smallCaps(theme, 11), color: theme.gold, fontStyle: 'normal' }}>Amen.</span>
          </div>
          <div style={{ marginTop: 36, display: 'flex', gap: 16 }}>
            <button onClick={restart} style={{ background: 'transparent', border: `1px solid ${theme.gold}`, color: theme.ink, ...smallCaps(theme, 10), padding: '8px 16px', cursor: 'pointer' }}>
              ↺ From the top
            </button>
            <button onClick={onClose} style={{ background: theme.ink, border: 'none', color: theme.paper, ...smallCaps(theme, 10), padding: '10px 18px', cursor: 'pointer' }}>
              ✓ Amen
            </button>
          </div>
        </div>
      </ModalShell>
    );
  }

  return (
    <ModalShell theme={theme} onClose={onClose} title="Pray with us" subtitle="The Lord's Prayer">
      <div style={{ padding: '40px 28px 60px', minHeight: 380, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', textAlign: 'center', background: theme.paper }}>
        {LORDS_PRAYER.map((line, i) => {
          const dist = Math.abs(i - idx);
          if (dist > 2) return null;
          return (
            <div key={i} style={{
              fontFamily: theme.fonts.display,
              fontSize: dist === 0 ? 28 : 16,
              fontStyle: 'italic',
              fontWeight: dist === 0 ? 500 : 400,
              color: dist === 0 ? theme.ink : theme.inkMute,
              opacity: dist === 0 ? 1 : 0.4,
              padding: '8px 0',
              transition: 'all 0.6s ease',
              lineHeight: 1.3,
            }}>{line}</div>
          );
        })}
        <div style={{ marginTop: 32, display: 'flex', gap: 12, flexWrap: 'wrap', justifyContent: 'center' }}>
          <button onClick={() => setPaused(p => !p)} style={{ background: 'transparent', border: `1px solid ${theme.gold}`, color: theme.ink, ...smallCaps(theme, 10), padding: '8px 16px', cursor: 'pointer' }}>
            {paused ? '▶ Continue' : '∥ Pause'}
          </button>
          <button onClick={() => setIdx(0)} style={{ background: 'transparent', border: `1px solid ${theme.gold}`, color: theme.ink, ...smallCaps(theme, 10), padding: '8px 16px', cursor: 'pointer' }}>
            ↺ From the top
          </button>
          {idx >= LORDS_PRAYER.length - 1 && collect && collect.text && (
            <button onClick={() => setPhase('collect')} style={{ background: theme.ink, border: 'none', color: theme.paper, ...smallCaps(theme, 10), padding: '8px 16px', cursor: 'pointer' }}>
              Continue → This Week's Prayer
            </button>
          )}
        </div>
        <div style={{ marginTop: 22, ...smallCaps(theme, 9), color: theme.inkMute }}>
          {idx + 1} / {LORDS_PRAYER.length}
        </div>
      </div>
    </ModalShell>
  );
}

// ─────────────────────────────────────────────────────────────
// PrayerChain — handoff between visitors
// ─────────────────────────────────────────────────────────────
function PrayerChainCard({ theme }) {
  const carried = React.useMemo(() => {
    const stored = localStorage.getItem('mumc_carried');
    if (stored) return JSON.parse(stored);
    const prayers = [
      { from: 'Eleanor', for: 'her grandson’s safe return' },
      { from: 'Thomas',  for: 'patience with his neighbor' },
      { from: 'Sarah',   for: 'a clear answer' },
      { from: 'James',   for: 'the family of a dear friend' },
    ];
    const pick = prayers[Math.floor(Math.random() * prayers.length)];
    localStorage.setItem('mumc_carried', JSON.stringify(pick));
    return pick;
  }, []);
  const [released, setReleased] = React.useState(false);

  return (
    <div style={{ padding: '14px 22px 0' }}>
      <div style={{
        background: `linear-gradient(180deg, ${theme.paper}, ${theme.bgAlt})`,
        padding: 18, border: `1px solid ${theme.gold}55`, position: 'relative',
      }}>
        <div style={{ ...smallCaps(theme, 9), color: theme.gold, marginBottom: 8 }}>† The Prayer Chain</div>
        {!released ? (
          <>
            <div style={{ fontFamily: theme.fonts.display, fontSize: 18, color: theme.ink, lineHeight: 1.3, fontWeight: 400 }}>
              You are carrying <span style={{ fontStyle: 'italic', color: theme.accent }}>{carried.from}</span>’s prayer
            </div>
            <div style={{ fontFamily: theme.fonts.display, fontSize: 17, color: theme.inkSoft, fontStyle: 'italic', marginTop: 4 }}>
              for {carried.for}.
            </div>
            <div style={{ fontFamily: theme.fonts.body, fontSize: 12, color: theme.inkMute, lineHeight: 1.5, marginTop: 10 }}>
              Hold them in mind for a moment. When you’re ready, release the prayer and pass it onward — the next visitor will carry one of yours.
            </div>
            <button onClick={() => { softChime(660); setReleased(true); localStorage.removeItem('mumc_carried'); }} style={{ marginTop: 14, padding: '10px 16px', background: theme.gold, color: theme.paper, border: 'none', ...smallCaps(theme, 10), cursor: 'pointer' }}>
              ☆ Release & pass on
            </button>
          </>
        ) : (
          <div style={{ fontFamily: theme.fonts.display, fontStyle: 'italic', fontSize: 17, color: theme.ink, padding: '10px 0', textAlign: 'center' }}>
            Thank you. The prayer continues. <span style={{ ...smallCaps(theme, 9), color: theme.gold, fontStyle: 'normal' }}>Amen.</span>
          </div>
        )}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Sit With Someone button — pairs visitors with a host
// ─────────────────────────────────────────────────────────────
function SitWithSomeone({ theme }) {
  const [stage, setStage] = React.useState('idle'); // idle | pairing | matched
  const hosts = [
    { name: 'Margaret W.', sub: 'Pew 7 · 18 years here', tag: 'left aisle' },
    { name: 'David & Joy K.', sub: 'Pew 12 · families welcome', tag: 'right aisle' },
    { name: 'Pastor Jonathan', sub: 'Will meet you at the door', tag: 'narthex' },
  ];
  const [match, setMatch] = React.useState(null);

  const start = () => {
    setStage('pairing');
    setTimeout(() => {
      setMatch(hosts[Math.floor(Math.random() * hosts.length)]);
      setStage('matched');
      softChime(880);
    }, 1600);
  };

  return (
    <div style={{ padding: '14px 22px 0' }}>
      <div style={{ background: theme.paper, padding: 18, border: `1px solid ${theme.rule}` }}>
        <div style={{ ...smallCaps(theme, 9), color: theme.accent, marginBottom: 6 }}>Coming this Sunday?</div>
        <div style={{ fontFamily: theme.fonts.display, fontSize: 22, color: theme.ink, fontWeight: 500, lineHeight: 1.15 }}>
          Sit with someone <span style={{ fontStyle: 'italic' }}>familiar</span>.
        </div>
        <div style={{ fontFamily: theme.fonts.body, fontSize: 13, color: theme.inkSoft, lineHeight: 1.45, marginTop: 6 }}>
          We’ll pair you with a member who has volunteered to host newcomers. They’ll save you a seat and answer your questions.
        </div>

        {stage === 'idle' && (
          <button onClick={start} style={{ marginTop: 14, width: '100%', padding: '12px', background: theme.ink, color: theme.paper, border: 'none', ...smallCaps(theme, 10), cursor: 'pointer' }}>
            ✦ &nbsp; Pair me with a host
          </button>
        )}
        {stage === 'pairing' && (
          <div style={{ marginTop: 16, textAlign: 'center', padding: '14px 0', fontFamily: theme.fonts.display, fontStyle: 'italic', color: theme.inkSoft, fontSize: 15, animation: 'breathe 1.2s infinite' }}>
            Asking the saints…
          </div>
        )}
        {stage === 'matched' && match && (
          <div style={{ marginTop: 14, padding: 14, background: theme.bgAlt, border: `1px solid ${theme.gold}` }}>
            <div style={{ ...smallCaps(theme, 9), color: theme.gold, marginBottom: 4 }}>You're paired with</div>
            <div style={{ fontFamily: theme.fonts.display, fontSize: 22, fontWeight: 500, color: theme.ink, fontStyle: 'italic' }}>{match.name}</div>
            <div style={{ fontFamily: theme.fonts.body, fontSize: 12, color: theme.inkSoft, marginTop: 2 }}>{match.sub}</div>
            <div style={{ ...smallCaps(theme, 9), color: theme.accent, marginTop: 8 }}>↳ Find them in the {match.tag}</div>
          </div>
        )}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// YearWheel — circular liturgical year
// ─────────────────────────────────────────────────────────────
function YearWheel({ theme, size = 280 }) {
  const r = size / 2;
  const cx = r, cy = r;

  // Compute actual liturgical season boundaries for the current year.
  // Falls back to a static approximation if the helper isn't loaded.
  let seasons = null;
  let todayPct = 0;
  let labelYears = '';
  try {
    if (typeof getLiturgicalSeasons === 'function') {
      seasons = getLiturgicalSeasons();
      todayPct = seasons.today;
      labelYears = `${seasons.startYear} — ${seasons.endYear}`;
    }
  } catch (e) { /* fall through */ }

  if (!seasons) {
    // Approximate fallback when the helper isn't loaded
    seasons = {
      advent:    { from: 0,    to: 0.07 },
      christmas: { from: 0.07, to: 0.10 },
      epiphany:  { from: 0.10, to: 0.22 },
      lent:      { from: 0.22, to: 0.35 },
      easter:    { from: 0.35, to: 0.50 },
      pentecost: { from: 0.50, to: 0.97 },
      reign:     { from: 0.97, to: 1 },
    };
    labelYears = String(new Date().getFullYear());
  }

  // Arc order goes clockwise from the top: Advent at 12 o'clock,
  // running through the year and ending with Christ the King.
  // Pentecost Day gets a small red wedge between Easter and the long
  // green Season-after-Pentecost arc — same visual width as Reign.
  const arcs = [
    { ...seasons.advent,       name: 'Advent',    col: '#3a2858' },
    { ...seasons.christmas,    name: 'Christmas', col: '#b8893d' },
    { ...seasons.epiphany,     name: 'Epiphany',  col: '#4a7042' },
    { ...seasons.lent,         name: 'Lent',      col: '#4a3858' },
    { ...seasons.easter,       name: 'Easter',    col: '#a87838' },
    seasons.pentecostDay && { ...seasons.pentecostDay, name: '', col: '#9a2828' },
    { ...seasons.pentecost,    name: 'Pentecost', col: '#3a5a3a' },
    { ...seasons.reign,        name: 'Reign',     col: '#c8983d' },
  ].filter(Boolean);

  const polar = (pct, radius) => {
    const a = pct * Math.PI * 2 - Math.PI / 2;
    return [cx + Math.cos(a) * radius, cy + Math.sin(a) * radius];
  };
  const arcPath = (from, to, ri, ro) => {
    const [x1, y1] = polar(from, ri);
    const [x2, y2] = polar(to, ri);
    const [x3, y3] = polar(to, ro);
    const [x4, y4] = polar(from, ro);
    const large = (to - from) > 0.5 ? 1 : 0;
    return `M${x1} ${y1} A${ri} ${ri} 0 ${large} 1 ${x2} ${y2} L${x3} ${y3} A${ro} ${ro} 0 ${large} 0 ${x4} ${y4} Z`;
  };

  const ri = r - 56, ro = r - 14;
  const [tx, ty] = polar(todayPct, (ri + ro) / 2);

  // Render labels for nearly all arcs; very thin ones get a smaller font.
  const minLabelArc = 0.012;
  const tightLabelArc = 0.04;

  return (
    <div style={{ display: 'flex', justifyContent: 'center', padding: '4px 0' }}>
      <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
        {/* outer & inner rims */}
        <circle cx={cx} cy={cy} r={ro} fill="none" stroke={theme.gold} strokeWidth="0.7" opacity="0.5"/>
        <circle cx={cx} cy={cy} r={ri} fill="none" stroke={theme.gold} strokeWidth="0.7" opacity="0.5"/>
        {/* arcs */}
        {arcs.map((a, i) => (
          <g key={i}>
            <path d={arcPath(a.from, a.to, ri, ro)} fill={a.col} opacity="0.85"/>
            <path d={arcPath(a.from, a.to, ri, ro)} fill="none" stroke={theme.paper} strokeWidth="1"/>
          </g>
        ))}
        {/* today indicator — pointer plus dot */}
        <line x1={cx} y1={cy} x2={tx} y2={ty} stroke={theme.ink} strokeWidth="1.2" opacity="0.5"/>
        <circle cx={tx} cy={ty} r="7" fill={theme.paper} stroke={theme.ink} strokeWidth="1.5"/>
        <circle cx={tx} cy={ty} r="2.5" fill={theme.ink}/>
        {/* center label */}
        <text x={cx} y={cy - 8} textAnchor="middle" style={{ fontFamily: theme.fonts.display, fontSize: 13, fontStyle: 'italic', fill: theme.inkSoft }}>The Church's Year</text>
        <text x={cx} y={cy + 14} textAnchor="middle" style={{ fontFamily: theme.fonts.display, fontSize: 18, fontWeight: 500, fill: theme.accent, letterSpacing: '0.02em' }}>{labelYears}</text>
        {/* arc labels — small caps. Tight arcs (Christmas, Reign) get a smaller font. */}
        {arcs.map((a, i) => {
          const span = a.to - a.from;
          if (span < minLabelArc) return null;
          if (!a.name) return null; // intentionally untitled (e.g., Pentecost Day wedge)
          const tight = span < tightLabelArc;
          const mid = (a.from + a.to) / 2;
          const [lx, ly] = polar(mid, (ri + ro) / 2);
          return (
            <text key={i} x={lx} y={ly} textAnchor="middle" dominantBaseline="middle"
                  style={{ fontFamily: theme.fonts.mono, fontSize: tight ? 6.5 : 8, letterSpacing: tight ? '0.08em' : '0.15em', fill: theme.paper, fontWeight: 500 }}>
              {a.name.toUpperCase()}
            </text>
          );
        })}
      </svg>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// ModalShell — full-screen modal with paper texture and close
// ─────────────────────────────────────────────────────────────
function ModalShell({ theme, onClose, title, subtitle, children }) {
  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 200,
      background: theme.bg, animation: 'fadein 0.25s',
      display: 'flex', flexDirection: 'column',
    }} className="paper-grain">
      <div style={{ padding: '54px 22px 16px', background: theme.paper, borderBottom: `1px solid ${theme.rule}`, flexShrink: 0 }}>
        <button onClick={() => onClose()} style={{ background: 'transparent', border: 'none', cursor: 'pointer', ...smallCaps(theme, 9), color: theme.inkMute, padding: 0, marginBottom: 10 }}>‹ &nbsp; Close</button>
        {subtitle && <div style={{ ...smallCaps(theme, 9), color: theme.accent, marginBottom: 6 }}>{subtitle}</div>}
        <h1 style={{ fontFamily: theme.fonts.display, fontWeight: 500, fontSize: 32, lineHeight: 1, margin: 0, color: theme.ink, fontStyle: 'italic' }}>{title}</h1>
      </div>
      <div style={{ flex: 1, overflow: 'auto' }}>
        {children}
      </div>
    </div>
  );
}

Object.assign(window, {
  SunlitGlass, DailyOfficeCard, OfficeModal, LectionaryModal, SaintRibbon,
  PrayWithUsModal, PrayerChainCard, SitWithSomeone, YearWheel, ModalShell,
});
