// screen-library.jsx — Library, Book detail, Reader (bilingual)
function frac(progress, bookId, total) {
  if (!total) return 0;
  const done = Object.keys(progress.reading[bookId] || {}).length;
  return Math.min(1, done / total);
}

function LibraryScreen({ content, progress, libTab, setLibTab, go }) {
  const c = useTheme();
  const ruhiPct = frac(progress, 'ruhi1', content.ruhiBook1.units.length);

  return (
    <div style={{ padding: '58px 18px 120px' }}>
      <h1 style={{ margin: '0 2px 4px', fontFamily: c.headingStack, fontWeight: 600, fontSize: 32, color: c.text }}>{c.t('library')}</h1>
      <p style={{ margin: '0 2px 18px', color: c.textMuted, fontSize: 14.5 }}>{c.t('library_sub')}</p>

      <div style={{ display: 'flex', gap: 8, marginBottom: 20, flexWrap: 'wrap' }}>
        <Chip active={libTab === 'jy'} onClick={() => setLibTab('jy')}>{c.t('tab_jy')}</Chip>
        <Chip active={libTab === 'children'} onClick={() => setLibTab('children')}>{c.t('tab_children')}</Chip>
        <Chip active={libTab === 'ruhi'} onClick={() => setLibTab('ruhi')}>{c.t('tab_ruhi')}</Chip>
      </div>

      {libTab === 'jy' && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
          {content.jyBooks.map(b => {
            const pct = frac(progress, b.id, b.lessons.length);
            return (
              <button key={b.id} disabled={!b.available} onClick={() => b.available && go({ name: 'book', kind: 'jy', bookId: b.id })}
                style={{ ...asfCardBtn(c), cursor: b.available ? 'pointer' : 'default', opacity: b.available ? 1 : 0.7 }}>
                <div style={{ display: 'flex', gap: 14, alignItems: 'center' }}>
                  <BookCover seed={b.id} src={b.coverSrc} label={c.L(b.title).toUpperCase()} w={58} h={80} dim={!b.available} />
                  <div style={{ flex: 1, textAlign: 'left', minWidth: 0 }}>
                    <div style={{ fontSize: 11.5, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase', color: c.accent }}>{c.L(b.strand)}</div>
                    <div style={{ fontSize: 16.5, fontWeight: 700, color: c.text, marginTop: 2 }}>{c.L(b.title)}</div>
                    {b.available ? (
                      <div style={{ display: 'flex', alignItems: 'center', gap: 9, marginTop: 9 }}>
                        <div style={{ flex: 1, height: 5, borderRadius: 3, background: c.border, overflow: 'hidden' }}>
                          <div style={{ width: `${pct * 100}%`, height: '100%', background: c.accent, borderRadius: 3 }} />
                        </div>
                        <span style={{ fontSize: 12, color: c.textMuted, fontWeight: 600 }}>{Math.round(pct * 100)}%</span>
                      </div>
                    ) : <div style={{ fontSize: 13, color: c.textMuted, marginTop: 6, fontStyle: 'italic' }}>{c.t('coming_soon')}</div>}
                  </div>
                </div>
              </button>
            );
          })}
        </div>
      )}

      {libTab === 'children' && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
          {content.childrenClasses.map(b => {
            const pct = frac(progress, b.id, b.lessons.length);
            return (
              <button key={b.id} disabled={!b.available} onClick={() => b.available && go({ name: 'book', kind: 'children', bookId: b.id })}
                style={{ ...asfCardBtn(c), cursor: b.available ? 'pointer' : 'default', opacity: b.available ? 1 : 0.7 }}>
                <div style={{ display: 'flex', gap: 14, alignItems: 'center' }}>
                  <BookCover seed={b.id} label={c.L(b.title).toUpperCase()} w={58} h={80} dim={!b.available} />
                  <div style={{ flex: 1, textAlign: 'left', minWidth: 0 }}>
                    <div style={{ fontSize: 11.5, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase', color: c.accent }}>{c.L(b.strand)}</div>
                    <div style={{ fontSize: 16.5, fontWeight: 700, color: c.text, marginTop: 2 }}>{c.L(b.title)}</div>
                    <div style={{ fontSize: 13, color: c.textMuted, marginTop: 5 }}>{b.lessons.length} {c.t('lessons')} · Thursday 6:00 PM</div>
                    {b.available ? (
                      <div style={{ display: 'flex', alignItems: 'center', gap: 9, marginTop: 9 }}>
                        <div style={{ flex: 1, height: 5, borderRadius: 3, background: c.border, overflow: 'hidden' }}>
                          <div style={{ width: `${pct * 100}%`, height: '100%', background: c.accent, borderRadius: 3 }} />
                        </div>
                        <span style={{ fontSize: 12, color: c.textMuted, fontWeight: 600 }}>{Math.round(pct * 100)}%</span>
                      </div>
                    ) : <div style={{ fontSize: 13, color: c.textMuted, marginTop: 6, fontStyle: 'italic' }}>{c.t('coming_soon')}</div>}
                  </div>
                </div>
              </button>
            );
          })}
        </div>
      )}

      {libTab === 'ruhi' && (
        <div>
          <button onClick={() => go({ name: 'book', kind: 'ruhi', bookId: 'ruhi1' })} style={{ ...asfCardBtn(c), padding: 0, overflow: 'hidden' }}>
            <div style={{ display: 'flex', gap: 16, padding: 18 }}>
              <BookCover seed="ruhi1" src={content.ruhiBook1.coverSrc} label={c.L(content.ruhiBook1.title).toUpperCase()} w={80} h={110} />
              <div style={{ flex: 1, textAlign: 'left' }}>
                <div style={{ fontSize: 11.5, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase', color: c.accent }}>{c.t('book1')}</div>
                <div style={{ fontFamily: c.headingStack, fontSize: 19, fontWeight: 600, color: c.text, lineHeight: 1.15, marginTop: 3 }}>{c.L(content.ruhiBook1.title)}</div>
                <div style={{ fontSize: 13, color: c.textMuted, marginTop: 6 }}>{content.ruhiBook1.units.length} {c.t('units')}</div>
                <div style={{ display: 'flex', alignItems: 'center', gap: 9, marginTop: 10 }}>
                  <div style={{ flex: 1, height: 5, borderRadius: 3, background: c.border, overflow: 'hidden' }}>
                    <div style={{ width: `${ruhiPct * 100}%`, height: '100%', background: c.accent, borderRadius: 3 }} />
                  </div>
                  <span style={{ fontSize: 12, color: c.textMuted, fontWeight: 600 }}>{Math.round(ruhiPct * 100)}%</span>
                </div>
              </div>
            </div>
          </button>
          <div style={{ ...asfCard(c), marginTop: 12, display: 'flex', gap: 12, alignItems: 'center', color: c.textMuted }}>
            <Icon name="layers" size={20} />
            <span style={{ fontSize: 13.5, lineHeight: 1.4 }}>{c.t('ruhi_more')}</span>
          </div>
        </div>
      )}
    </div>
  );
}

function routeBook(content, route) {
  if (route.kind === 'ruhi') return content.ruhiBook1;
  if (route.kind === 'children') return content.childrenClasses.find(b => b.id === route.bookId);
  return content.jyBooks.find(b => b.id === route.bookId);
}

function BookScreen({ content, progress, route, go, back }) {
  const c = useTheme();
  const isRuhi = route.kind === 'ruhi';
  const book = routeBook(content, route);
  const items = isRuhi ? book.units : book.lessons;
  const done = progress.reading[book.id] || {};

  return (
    <div style={{ padding: '0 0 120px' }}>
      <TopBar back={back} />
      <div style={{ padding: '6px 20px 0', display: 'flex', gap: 16 }}>
        <BookCover seed={book.id} src={book.coverSrc} label={c.L(book.title).toUpperCase()} w={92} h={126} />
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 11.5, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase', color: c.accent }}>{isRuhi ? c.L(book.label) : c.L(book.strand)}</div>
          <h1 style={{ margin: '4px 0 0', fontFamily: c.headingStack, fontWeight: 600, fontSize: 24, lineHeight: 1.12, color: c.text }}>{c.L(book.title)}</h1>
        </div>
      </div>
      <p style={{ padding: '16px 20px 8px', margin: 0, color: c.textMuted, fontSize: 15, lineHeight: 1.5 }}>{c.L(book.blurb)}</p>

      <SectionLabel style={{ margin: '14px 22px 10px' }}>{isRuhi ? c.t('units_h') : c.t('lessons')}</SectionLabel>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 10, padding: '0 18px' }}>
        {items.map((it, i) => {
          const isDone = !!done[it.id];
          const q = it.quoteId && content.quotes.find(x => x.id === it.quoteId);
          return (
            <button key={it.id} onClick={() => go({ name: 'reader', kind: route.kind, bookId: book.id, lessonId: it.id })} style={asfCardBtn(c)}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 13 }}>
                <div style={{ width: 34, height: 34, borderRadius: 999, flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center',
                  background: isDone ? c.accent : c.surfaceAlt, color: isDone ? c.accentInk : c.textMuted,
                  boxShadow: isDone ? 'none' : `inset 0 0 0 1px ${c.border}`, fontFamily: c.headingStack, fontWeight: 600, fontSize: 15 }}>
                  {isDone ? <Icon name="check" size={17} /> : (it.n || i + 1)}
                </div>
                <div style={{ flex: 1, textAlign: 'left' }}>
                  <div style={{ fontSize: 15.5, fontWeight: 700, color: c.text }}>{c.L(it.title)}</div>
                  <div style={{ fontSize: 12.5, color: c.textMuted, marginTop: 2, display: 'flex', gap: 9, alignItems: 'center' }}>
                    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4 }}><Icon name="clock" size={12} />{it.minutes} {c.t('min')}</span>
                    {q && <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, color: c.accent }}><Icon name="quote" size={12} fill />1 {c.t('quotes_n')}</span>}
                  </div>
                </div>
                <div style={{ color: c.textMuted }}><Icon name="right" size={17} /></div>
              </div>
            </button>
          );
        })}
      </div>
    </div>
  );
}

function ReaderScreen({ content, progress, setReadDone, route, go, back }) {
  const c = useTheme();
  const isRuhi = route.kind === 'ruhi';
  const book = routeBook(content, route);
  const items = isRuhi ? book.units : book.lessons;
  const idx = items.findIndex(x => x.id === route.lessonId);
  const lesson = items[idx];
  const q = lesson.quoteId && content.quotes.find(x => x.id === lesson.quoteId);
  const isDone = !!(progress.reading[book.id] || {})[lesson.id];

  if (!isRuhi && ((book.id === 'wellspring' && ['wj1', 'wj2', 'wj8', 'wj9'].includes(lesson.id)) || book.id === 'habits')) {
    return <LessonPager book={book} lesson={lesson} isDone={isDone} setReadDone={setReadDone} back={back} />;
  }

  if (route.kind === 'children' && lesson.childFlow) {
    return <ChildrenLessonPager book={book} lesson={lesson} isDone={isDone} setReadDone={setReadDone} back={back} />;
  }

  return (
    <div style={{ padding: '0 0 130px' }}>
      <TopBar back={back} label={isRuhi ? `${c.t('unit')} ${lesson.n}` : c.L(book.title)} />
      <div style={{ padding: '4px 24px 0' }}>
        <div style={{ fontSize: 12, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: c.accent }}>{isRuhi ? c.L(book.title) : c.L(book.strand)}</div>
        <h1 style={{ margin: '6px 0 0', fontFamily: c.headingStack, fontWeight: 600, fontSize: 27, lineHeight: 1.15, color: c.text }}>{c.L(lesson.title)}</h1>
      </div>

      <div style={{ padding: '20px 24px 0', display: 'flex', flexDirection: 'column', gap: 18 }}>
        {lesson.sections.map((s, i) => {
          if (s.kind === 'text') return c.L(s.body).split('\n\n').map((para, j) => (
            <p key={i + '-' + j} style={{ margin: 0, fontSize: 16.5, lineHeight: 1.66, color: c.text }}>{para}</p>
          ));
          if (s.kind === 'reflect') return (
            <div key={i} style={{ borderLeft: `3px solid ${c.accent}`, padding: '4px 0 4px 16px' }}>
              <div style={{ fontSize: 11.5, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: c.accent, marginBottom: 5 }}>{c.t('reflect')}</div>
              <p style={{ margin: 0, fontFamily: c.headingStack, fontSize: 18, lineHeight: 1.5, color: c.text, fontStyle: 'italic' }}>{c.L(s.body)}</p>
            </div>
          );
          if (s.kind === 'prayer') return (
            <div key={i} style={{ ...asfCard(c), background: c.surfaceAlt, textAlign: 'center', padding: '22px 20px' }}>
              <p style={{ margin: 0, fontFamily: c.headingStack, fontSize: 18.5, lineHeight: 1.55, color: c.text }}>{c.L(s.body)}</p>
              <div style={{ marginTop: 10, fontSize: 13, color: c.textMuted, fontStyle: 'italic' }}>— {s.source}</div>
            </div>
          );
          if (s.kind === 'activity') return (
            <div key={i} style={{ ...asfCard(c), display: 'flex', gap: 12, padding: '15px 16px' }}>
              <div style={{ color: c.accent, flexShrink: 0, marginTop: 1 }}><Icon name="user" size={19} /></div>
              <div>
                <div style={{ fontSize: 11.5, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase', color: c.accent, marginBottom: 4 }}>{c.L(s.label)}</div>
                <p style={{ margin: 0, fontSize: 15.5, lineHeight: 1.5, color: c.text }}>{c.L(s.body)}</p>
              </div>
            </div>
          );
          if (s.kind === 'questions' || s.kind === 'choice' || s.kind === 'wordbank' || s.kind === 'fillbank' || s.kind === 'attention' || s.kind === 'phrasepairs' || s.kind === 'writing' || s.kind === 'bookpage') {
            const base = `a_${book.id}_${lesson.id}_${i}`;
            if (s.kind === 'questions') return <QuestionsBlock key={i} s={s} base={base} />;
            if (s.kind === 'choice') return <ChoiceBlock key={i} s={s} base={base} />;
            if (s.kind === 'wordbank') return <WordbankBlock key={i} s={s} base={base} />;
            if (s.kind === 'fillbank') return <FillbankBlock key={i} s={s} base={base} />;
            if (s.kind === 'attention') return <AttentionBlock key={i} s={s} base={base} />;
            if (s.kind === 'phrasepairs') return <PhrasePairsBlock key={i} s={s} base={base} />;
            if (s.kind === 'writing') return <WritingBlock key={i} s={s} base={base} />;
            if (s.kind === 'bookpage') return <BookPageBlock key={i} s={s} />;
          }
          return null;
        })}

        {q && (
          <div style={{ ...asfCard(c), padding: 0, overflow: 'hidden', border: `1px solid ${c.accent}` }}>
            <div style={{ padding: '16px 18px' }}>
              <div style={{ fontSize: 11.5, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: c.accent, display: 'flex', alignItems: 'center', gap: 6 }}><Icon name="spark" size={14} />{c.t('memorize_this')}</div>
              <p style={{ margin: '10px 0 0', fontFamily: c.headingStack, fontSize: 18.5, lineHeight: 1.45, color: c.text }}>{c.L(q.text)}</p>
              <div style={{ marginTop: 8, fontSize: 13, color: c.textMuted, fontStyle: 'italic' }}>— {q.source}</div>
            </div>
            <button onClick={() => go({ name: 'practice', quoteId: q.id })} style={{ width: '100%', border: 'none', cursor: 'pointer', background: c.accent, color: c.accentInk, fontFamily: 'inherit', fontWeight: 700, fontSize: 14.5, padding: '13px' }}>{c.t('practice_now')}</button>
          </div>
        )}

        <button onClick={() => setReadDone(book.id, lesson.id, !isDone)} style={{
          marginTop: 4, border: isDone ? `1px solid ${c.border}` : 'none', cursor: 'pointer', borderRadius: 14,
          background: isDone ? 'transparent' : c.text, color: isDone ? c.textMuted : c.bg,
          fontFamily: 'inherit', fontWeight: 700, fontSize: 15.5, padding: '15px', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
        }}><Icon name="check" size={18} />{isDone ? c.t('completed_undo') : c.t('mark_complete')}</button>

        {idx < items.length - 1 && (
          <button onClick={() => go({ name: 'reader', kind: route.kind, bookId: book.id, lessonId: items[idx + 1].id })} style={{ ...asfCardBtn(c), display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
            <span style={{ fontSize: 14, color: c.textMuted }}>{c.t('next')} · {c.L(items[idx + 1].title)}</span>
            <Icon name="arrowR" size={18} />
          </button>
        )}
      </div>
    </div>
  );
}

function ChildrenLessonPager({ book, lesson, isDone, setReadDone, back }) {
  const c = useTheme();
  const flow = lesson.childFlow;
  const tabs = [
    { id: 'prayer', label: 'Prayer' },
    { id: 'song', label: 'Song' },
    { id: 'quote', label: 'Quote' },
    { id: 'story', label: 'Story' },
    { id: 'game', label: 'Game' },
    { id: 'coloring', label: 'Coloring' },
    { id: 'closing', label: 'Closing' },
  ];
  const [tab, setTab] = React.useState('prayer');
  const idx = tabs.findIndex(x => x.id === tab);
  const goNext = () => {
    if (idx < tabs.length - 1) setTab(tabs[idx + 1].id);
    else setReadDone(book.id, lesson.id, true);
  };
  const goBack = () => idx > 0 ? setTab(tabs[idx - 1].id) : back();
  const cur = tabs[idx] || tabs[0];

  return (
    <div style={{ minHeight: '100%', background: c.bg, display: 'flex', flexDirection: 'column', padding: '0 14px 28px' }}>
      <TopBar back={back} label={c.L(book.title)} />
      <div style={{ padding: '0 6px 10px' }}>
        <div style={{ fontSize: 11.5, fontWeight: 800, letterSpacing: '0.12em', textTransform: 'uppercase', color: c.accent }}>{c.L(book.strand)}</div>
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 12, marginTop: 7 }}>
          <h1 style={{ margin: 0, fontFamily: c.headingStack, fontWeight: 800, fontSize: 28, lineHeight: 1.04, color: c.text, letterSpacing: 0 }}>{c.L(lesson.title)}</h1>
          <div style={{
            whiteSpace: 'nowrap', borderRadius: 999, padding: '8px 11px', background: c.chip || c.surface,
            color: c.text, fontSize: 12, fontWeight: 800,
            boxShadow: c.dark ? 'inset 0 1px 0 rgba(255,255,255,0.12)' : '0 5px 12px rgba(40,70,30,0.14), inset 0 1px 0 rgba(255,255,255,0.8)',
          }}>{isDone ? 'Complete' : `${idx + 1}/${tabs.length}`}</div>
        </div>
        <div style={{
          display: 'inline-flex', alignItems: 'center', gap: 7, marginTop: 12,
          padding: '8px 12px', borderRadius: 999, background: c.chip || c.surface,
          color: c.text, fontSize: 12.5, fontWeight: 800,
          boxShadow: c.dark ? 'inset 0 1px 0 rgba(255,255,255,0.12)' : '0 5px 12px rgba(40,70,30,0.14), inset 0 1px 0 rgba(255,255,255,0.8)',
        }}>
          <Icon name="cal" size={14} />
          <span>Thursday 6:00 PM · Ashford Santa Fe Apartments</span>
        </div>
      </div>

      <div className="asf-scroll" style={{ display: 'flex', gap: 8, overflowX: 'auto', padding: '2px 2px 12px', flexShrink: 0 }}>
        {tabs.map((t, i) => {
          const active = t.id === tab;
          return (
            <button key={t.id} onClick={() => setTab(t.id)} style={{
              flex: '0 0 auto', border: 'none', cursor: 'pointer', borderRadius: 999, padding: '9px 13px',
              background: active ? (c.btn || c.accent) : c.surface,
              color: active ? (c.btnInk || c.accentInk) : c.textMuted,
              fontFamily: 'inherit', fontSize: 13, fontWeight: 800,
              boxShadow: active ? (c.btnShadow || 'none') : (c.dark ? `inset 0 0 0 1px ${c.border}` : '0 5px 12px rgba(40,70,30,0.10), inset 0 1px 0 rgba(255,255,255,0.9)'),
            }}>{i < idx || isDone ? '✓ ' : ''}{t.label}</button>
          );
        })}
      </div>

      <div style={{ ...asfCard(c), flex: '1 1 auto', minHeight: 0, padding: 0, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
        <div style={{ padding: '17px 18px 13px', borderBottom: `1px solid ${c.border}` }}>
          <div style={{ fontSize: 11.5, fontWeight: 800, letterSpacing: '0.12em', textTransform: 'uppercase', color: c.accent }}>Grade 1 · Lesson {lesson.n}</div>
          <h2 style={{ margin: '4px 0 0', fontFamily: c.headingStack, fontWeight: 800, fontSize: 22, color: c.text, lineHeight: 1.1 }}>{cur.label}</h2>
          <div style={{ marginTop: 12, display: 'flex', gap: 7 }}>
            {tabs.map((t, i) => {
              const active = t.id === tab;
              return (
                <button key={t.id} onClick={() => setTab(t.id)} aria-label={t.label} style={{
                  width: 22, height: 22, borderRadius: 999, border: 'none', cursor: 'pointer',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  background: active ? c.accent : (i < idx || isDone ? (c.soft2 || c.accentSoft) : c.surfaceAlt),
                  color: active ? c.accentInk : (i < idx || isDone ? (c.accent2 || c.accent) : c.textMuted),
                  fontFamily: c.headingStack, fontSize: 12, fontWeight: 800,
                }}>{i < idx || isDone ? '✓' : i + 1}</button>
              );
            })}
          </div>
        </div>

        <div className="asf-scroll" style={{ overflow: 'auto', padding: 18, minHeight: 0, flex: 1 }}>
          <ChildrenLessonTab flow={flow} tab={tab} />
        </div>

        <div style={{ display: 'flex', gap: 10, padding: 12, borderTop: `1px solid ${c.border}`, background: c.footer || c.surfaceAlt }}>
          <button onClick={goBack} style={{
            border: 'none', cursor: 'pointer', borderRadius: 999, padding: '12px 16px',
            background: c.dark ? 'rgba(255,255,255,0.08)' : 'rgba(33,51,26,0.08)',
            color: c.textMuted, fontFamily: 'inherit', fontWeight: 800, fontSize: 14,
          }}>{idx === 0 ? 'Close' : 'Back'}</button>
          <button onClick={goNext} style={{ ...asfPrimaryBtn(c), flex: 1, padding: '12px 16px' }}>
            {idx < tabs.length - 1 ? `Next: ${tabs[idx + 1].label}` : (isDone ? 'Completed' : 'Mark lesson complete')}
          </button>
        </div>
      </div>
    </div>
  );
}

function ChildrenLessonTab({ flow, tab }) {
  const c = useTheme();
  const [practice, setPractice] = React.useState('echo');
  const [beat, setBeat] = React.useState(0);
  const openDrawing = () => window.open(flow.coloring.image, '_blank', 'noopener');
  const printDrawing = () => {
    const win = window.open('', '_blank');
    if (!win) return;
    win.document.write(`<!doctype html><title>${c.L(flow.coloring.title)}</title><style>body{margin:0;padding:24px;text-align:center}img{max-width:100%;height:auto}</style><img src="${flow.coloring.image}" onload="window.print()">`);
    win.document.close();
  };

  if (tab === 'prayer') return (
    <ChildTabLayout icon="spark" title="Opening prayers">
      <p style={childP(c)}>{c.L(flow.prayer.teacher)}</p>
      <div style={childQuoteBox(c)}>
        <div style={childMiniLabel(c)}>Prayer to memorize</div>
        <p style={{ margin: 0, fontFamily: "'Spectral', Georgia, serif", fontSize: 21, lineHeight: 1.42, color: c.text }}>{c.L(flow.prayer.memorize)}</p>
      </div>
      <p style={{ ...childP(c), color: c.textMuted }}>{c.L(flow.prayer.note)}</p>
    </ChildTabLayout>
  );

  if (tab === 'song') return (
    <ChildTabLayout icon="play" title={c.L(flow.song.title)}>
      <p style={childP(c)}>{c.L(flow.song.note)}</p>
      <a href={flow.song.ruhiUrl} target="_blank" rel="noopener" style={childPrimaryLink(c)}>Open Ruhi songs</a>
      <div style={{ marginTop: 16, borderRadius: 18, background: c.surfaceAlt, padding: 15 }}>
        <div style={childMiniLabel(c)}>Lyrics</div>
        <p style={{ margin: 0, whiteSpace: 'pre-line', fontFamily: "'Spectral', Georgia, serif", fontSize: 18, lineHeight: 1.5, color: c.text }}>{c.L(flow.song.lyrics)}</p>
      </div>
      <button disabled style={{ ...childSecondaryBtn(c), width: '100%', marginTop: 12, opacity: 0.55 }}>Play in app later</button>
    </ChildTabLayout>
  );

  if (tab === 'quote') {
    const modes = [
      ['echo', 'Echo', 'Teacher says one phrase; children repeat it together.'],
      ['build', 'Build', 'Add one phrase at a time with gestures.'],
      ['missing', 'Missing word', 'Hide one word and let children fill it in aloud.'],
      ['pass', 'Pass it', 'Each child says the next phrase around the circle.'],
    ];
    return (
      <ChildTabLayout icon="quote" title="Quote practice">
        <p style={childP(c)}>{c.L(flow.quote.intro)}</p>
        <div style={childQuoteBox(c)}>
          <p style={{ margin: 0, fontFamily: "'Spectral', Georgia, serif", fontSize: 21, lineHeight: 1.42, color: c.text }}>{c.L(flow.quote.text)}</p>
          <div style={{ marginTop: 8, fontSize: 13, color: c.textMuted, fontStyle: 'italic' }}>— {flow.quote.source}</div>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginTop: 14 }}>
          {modes.map(([id, label]) => (
            <button key={id} onClick={() => setPractice(id)} style={{
              border: 'none', borderRadius: 14, cursor: 'pointer', padding: '11px 9px',
              background: practice === id ? c.accent : c.surfaceAlt,
              color: practice === id ? c.accentInk : c.text,
              fontFamily: 'inherit', fontSize: 13, fontWeight: 800,
            }}>{label}</button>
          ))}
        </div>
        <div style={{ marginTop: 12, borderRadius: 18, background: c.surfaceAlt, padding: 14 }}>
          <div style={childMiniLabel(c)}>{modes.find(x => x[0] === practice)?.[1]}</div>
          <p style={{ margin: 0, fontSize: 15, lineHeight: 1.45, color: c.text, fontWeight: 700 }}>{modes.find(x => x[0] === practice)?.[2]}</p>
        </div>
        <div style={{ marginTop: 14, display: 'grid', gap: 8 }}>
          {flow.quote.words.map(w => (
            <div key={c.L(w.term)} style={{ borderRadius: 14, background: c.surface, padding: 12, boxShadow: `inset 0 0 0 1px ${c.border}` }}>
              <div style={{ fontSize: 14, fontWeight: 900, color: c.accent }}>{c.L(w.term)}</div>
              <div style={{ marginTop: 3, fontSize: 13.5, lineHeight: 1.35, color: c.textMuted, fontWeight: 700 }}>{c.L(w.meaning)}</div>
            </div>
          ))}
        </div>
      </ChildTabLayout>
    );
  }

  if (tab === 'story') {
    const item = flow.story.beats[beat] || flow.story.beats[0];
    return (
      <ChildTabLayout icon="book" title={c.L(flow.story.title)}>
        <div style={{ borderRadius: 20, overflow: 'hidden', background: c.surfaceAlt, border: `1px solid ${c.border}`, marginBottom: 14 }}>
          <StoryBeatArt beat={beat} />
          <div style={{ padding: 14 }}>
            <div style={childMiniLabel(c)}>Story beat {beat + 1} of {flow.story.beats.length}</div>
            <h3 style={{ margin: '3px 0 6px', fontFamily: c.headingStack, fontSize: 20, color: c.text }}>{c.L(item.title)}</h3>
            <p style={{ margin: 0, fontSize: 15, lineHeight: 1.45, color: c.text, fontWeight: 700 }}>{c.L(item.body)}</p>
            <div style={{ display: 'flex', gap: 8, marginTop: 12 }}>
              <button onClick={() => setBeat(Math.max(0, beat - 1))} style={childSecondaryBtn(c)}>Back</button>
              <button onClick={() => setBeat(Math.min(flow.story.beats.length - 1, beat + 1))} style={{ ...childSecondaryBtn(c), flex: 1 }}>Next beat</button>
            </div>
          </div>
        </div>
        <div style={childQuoteBox(c)}>
          <div style={childMiniLabel(c)}>Full teacher story</div>
          <p style={{ margin: 0, whiteSpace: 'pre-line', fontFamily: "'Spectral', Georgia, serif", fontSize: 18, lineHeight: 1.52, color: c.text }}>{c.L(flow.story.full)}</p>
        </div>
      </ChildTabLayout>
    );
  }

  if (tab === 'game') return (
    <ChildTabLayout icon="user" title={c.L(flow.game.title)}>
      <p style={childP(c)}>{c.L(flow.game.body)}</p>
      <div style={{
        marginTop: 18, height: 180, borderRadius: 22, background: 'linear-gradient(180deg,#bfe5dc,#eef2df)',
        position: 'relative', overflow: 'hidden', border: `1px solid ${c.border}`,
      }}>
        <div style={{ position: 'absolute', left: 36, right: 36, bottom: 26, height: 74, borderRadius: '50%', border: `14px solid ${c.accent}`, background: c.surface, opacity: 0.9 }} />
        {[70, 118, 166, 214, 262].map((x, i) => <MiniChild key={i} x={x} y={i % 2 ? 95 : 108} color={['#68b06a','#e0b65a','#5f8dcc','#d45a35','#8a78c8'][i]} />)}
      </div>
    </ChildTabLayout>
  );

  if (tab === 'coloring') return (
    <ChildTabLayout icon="edit" title={c.L(flow.coloring.title)}>
      <p style={childP(c)}>{c.L(flow.coloring.note)}</p>
      <div style={{ display: 'flex', gap: 8, margin: '12px 0' }}>
        <button onClick={openDrawing} style={{ ...childSecondaryBtn(c), flex: 1 }}>Open</button>
        <button onClick={printDrawing} style={{ ...childSecondaryBtn(c), flex: 1 }}>Print</button>
      </div>
      <img src={flow.coloring.image} alt={c.L(flow.coloring.title)} style={{ width: '100%', display: 'block', borderRadius: 16, background: '#fff', border: `1px solid ${c.border}` }} />
    </ChildTabLayout>
  );

  return (
    <ChildTabLayout icon="spark" title="Closing prayers">
      <p style={childP(c)}>{c.L(flow.closing)}</p>
      <div style={childQuoteBox(c)}>
        <div style={childMiniLabel(c)}>Simple closing rhythm</div>
        <p style={{ margin: 0, fontSize: 16, lineHeight: 1.45, color: c.text, fontWeight: 750 }}>1. Invite two or three children.<br />2. Let them recite a prayer or the quotation.<br />3. Teacher offers the final prayer.</p>
      </div>
    </ChildTabLayout>
  );
}

function ChildTabLayout({ icon, title, children }) {
  const c = useTheme();
  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 14 }}>
        <div style={{ width: 42, height: 42, borderRadius: 16, display: 'flex', alignItems: 'center', justifyContent: 'center', background: c.accentSoft, color: c.accent, flexShrink: 0 }}>
          <Icon name={icon} size={22} />
        </div>
        <h3 style={{ margin: 0, fontFamily: c.headingStack, fontSize: 24, lineHeight: 1.08, color: c.text }}>{title}</h3>
      </div>
      {children}
    </div>
  );
}

function StoryBeatArt({ beat }) {
  const stage = [
    ['#d45a35', '#5f8dcc', 54],
    ['#d45a35', '#5f8dcc', 98],
    ['#d45a35', '#5f8dcc', 146],
    ['#d45a35', '#5f8dcc', 198],
  ][beat] || ['#d45a35', '#5f8dcc', 54];
  return (
    <div style={{ height: 148, position: 'relative', overflow: 'hidden', background: 'linear-gradient(180deg,#ead6a8,#f8f2df)' }}>
      <div style={{ position: 'absolute', left: 44, right: 44, bottom: 24, height: 38, borderRadius: '50%', background: '#b58258', transform: 'skewX(-14deg)', boxShadow: '0 10px 16px rgba(95,55,28,0.2)' }} />
      <div style={{ position: 'absolute', left: stage[2], bottom: 62, width: 18, height: 32, border: '2px solid #75a8c4', borderRadius: '4px 4px 7px 7px', background: beat === 2 ? 'rgba(157,219,212,0.05)' : 'rgba(157,219,212,0.45)' }} />
      <MiniChild x={88} y={106} color={stage[0]} />
      <MiniChild x={244} y={108} color={stage[1]} />
      {beat === 3 && <div style={{ position: 'absolute', left: 164, bottom: 96, width: 54, height: 24, borderRadius: 999, background: 'rgba(255,255,255,0.88)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#2e7a47', fontWeight: 900 }}>joy</div>}
    </div>
  );
}

function MiniChild({ x, y, color }) {
  return (
    <div style={{ position: 'absolute', left: x, top: y, width: 32, height: 52, transform: 'translate(-50%, -100%)' }}>
      <div style={{ position: 'absolute', left: 7, top: 0, width: 19, height: 18, borderRadius: '8px 8px 6px 6px', background: '#a76647', boxShadow: 'inset 0 3px #2c1c18' }} />
      <div style={{ position: 'absolute', left: 4, top: 18, width: 25, height: 29, borderRadius: '9px 9px 5px 5px', background: color, boxShadow: 'inset 0 0 0 2px rgba(44,32,23,.16)' }} />
    </div>
  );
}

function childP(c) {
  return { margin: 0, fontSize: 16, lineHeight: 1.5, color: c.text, fontWeight: 700 };
}
function childMiniLabel(c) {
  return { fontSize: 11.5, fontWeight: 900, letterSpacing: '0.12em', textTransform: 'uppercase', color: c.accent, marginBottom: 7 };
}
function childQuoteBox(c) {
  return { marginTop: 14, borderRadius: 18, background: c.surfaceAlt, padding: 15, border: `1px solid ${c.border}` };
}
function childSecondaryBtn(c) {
  return {
    border: `1px solid ${c.border}`, borderRadius: 13, background: c.surface, color: c.text,
    padding: '11px 13px', fontFamily: 'inherit', fontSize: 14, fontWeight: 900, cursor: 'pointer',
  };
}
function childPrimaryLink(c) {
  return {
    display: 'inline-flex', textDecoration: 'none', borderRadius: 999, background: c.text, color: c.bg,
    padding: '11px 14px', fontSize: 14, fontWeight: 900, marginTop: 13,
  };
}

function buildLessonSteps(c, lesson) {
  const steps = [];
  for (let i = 0; i < lesson.sections.length; i++) {
    const s = lesson.sections[i];
    if (s.kind === 'text') {
      const raw = s.label ? c.L(s.label) : '';
      const parts = raw.split('·').map(x => x.trim()).filter(Boolean);
      steps.push({ id: `story-${i}`, tab: parts[0] || 'Story', title: parts[1] || c.L(lesson.title), indexes: [i] });
    } else if (s.kind === 'prayer') {
      const next = lesson.sections[i + 1];
      const nextLabel = next?.label ? c.L(next.label) : '';
      const indexes = next && (next.kind === 'fillbank' || (next.kind === 'questions' && /memorize/i.test(nextLabel))) ? [i, i + 1] : [i];
      steps.push({ id: `prayer-${i}`, tab: 'Prayer', title: 'Memorize', indexes });
      if (indexes.length > 1) i++;
    } else {
      const raw = s.label ? c.L(s.label) : '';
      const parts = raw.split('·').map(x => x.trim()).filter(Boolean);
      const tab = parts[0] || `Activity ${steps.length}`;
      const title = parts[1] || (
        s.kind === 'questions' ? 'Discuss' :
        s.kind === 'choice' ? 'Choose' :
        s.kind === 'wordbank' ? 'Vocabulary' :
        s.kind === 'fillbank' ? 'Complete' :
        s.kind === 'attention' ? 'Practice' :
        s.kind === 'phrasepairs' ? 'Words' :
        s.kind === 'writing' ? 'Write' :
        s.kind === 'bookpage' ? 'Book page' : tab
      );
      steps.push({ id: `${s.kind}-${i}`, tab, title, indexes: [i] });
    }
  }
  return steps;
}

function LessonPager({ book, lesson, isDone, setReadDone, back }) {
  const c = useTheme();
  const steps = React.useMemo(() => buildLessonSteps(c, lesson), [c.lang, lesson.id]);
  const [step, setStep] = React.useState(0);
  const [done, setDone] = useLocal(`asf_steps_${book.id}_${lesson.id}`, {});
  const contentRef = React.useRef(null);
  const cur = steps[step];
  React.useEffect(() => {
    const resetScroll = () => {
      if (contentRef.current) contentRef.current.scrollTop = 0;
      document.querySelectorAll('.asf-scroll').forEach(el => { el.scrollTop = 0; });
      window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
    };
    resetScroll();
    const t = window.setTimeout(resetScroll, 50);
    return () => window.clearTimeout(t);
  }, [step]);
  const completeCurrent = () => setDone(d => ({ ...d, [cur.id]: true }));
  const goNext = () => {
    completeCurrent();
    if (step < steps.length - 1) setStep(step + 1);
    else setReadDone(book.id, lesson.id, true);
  };
  const goBack = () => step > 0 ? setStep(step - 1) : back();
  const title = cur.title;
  const statusText = step === steps.length - 1 && isDone ? 'Complete' : `Step ${step + 1} of ${steps.length}`;

  return (
    <div style={{ minHeight: 874, background: c.bg, display: 'flex', flexDirection: 'column', padding: '0 14px 28px' }}>
      <TopBar back={back} label={c.L(book.title)} />
      <div style={{ padding: '0 6px 10px' }}>
        <div style={{ fontSize: 11.5, fontWeight: 800, letterSpacing: '0.12em', textTransform: 'uppercase', color: c.accent }}>{c.L(book.strand)}</div>
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 12, marginTop: 7 }}>
          <h1 style={{ margin: 0, fontFamily: c.headingStack, fontWeight: 800, fontSize: 28, lineHeight: 1.04, color: c.text, letterSpacing: 0 }}>{c.L(lesson.title)}</h1>
          <div style={{
            whiteSpace: 'nowrap', borderRadius: 999, padding: '8px 11px', background: c.chip || c.surface,
            color: c.text, fontSize: 12, fontWeight: 800,
            boxShadow: c.dark ? 'inset 0 1px 0 rgba(255,255,255,0.12)' : '0 5px 12px rgba(40,70,30,0.14), inset 0 1px 0 rgba(255,255,255,0.8)',
          }}>{statusText}</div>
        </div>
        <div style={{
          display: 'inline-flex', alignItems: 'center', gap: 7, marginTop: 12,
          padding: '8px 12px', borderRadius: 999, background: c.chip || c.surface,
          color: c.text, fontSize: 12.5, fontWeight: 800,
          boxShadow: c.dark ? 'inset 0 1px 0 rgba(255,255,255,0.12)' : '0 5px 12px rgba(40,70,30,0.14), inset 0 1px 0 rgba(255,255,255,0.8)',
        }}>
          <Icon name="cal" size={14} />
          <span>Monday 5:30 PM · Ashford Santa Fe Apartments</span>
        </div>
      </div>

      <div className="asf-scroll" style={{ display: 'flex', gap: 8, overflowX: 'auto', padding: '2px 2px 12px', flexShrink: 0 }}>
        {steps.map((s, i) => {
          const active = i === step;
          const stepDone = !!done[s.id] || (isDone && i < steps.length - 1);
          return (
            <button key={s.id} onClick={() => setStep(i)} style={{
              flex: '0 0 auto', border: 'none', cursor: 'pointer', borderRadius: 999, padding: '9px 13px',
              background: active ? (c.btn || c.accent) : c.surface,
              color: active ? (c.btnInk || c.accentInk) : c.textMuted,
              fontFamily: 'inherit', fontSize: 13, fontWeight: 800,
              boxShadow: active ? (c.btnShadow || 'none') : (c.dark ? `inset 0 0 0 1px ${c.border}` : '0 5px 12px rgba(40,70,30,0.10), inset 0 1px 0 rgba(255,255,255,0.9)'),
            }}>{stepDone ? '✓ ' : ''}{s.tab}</button>
          );
        })}
      </div>

      <div style={{ ...asfCard(c), flex: '1 1 auto', minHeight: 0, padding: 0, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
        <div style={{ padding: '17px 18px 13px', borderBottom: `1px solid ${c.border}` }}>
          <div style={{ fontSize: 11.5, fontWeight: 800, letterSpacing: '0.12em', textTransform: 'uppercase', color: c.accent }}>{cur.tab}</div>
          <h2 style={{ margin: '4px 0 0', fontFamily: c.headingStack, fontWeight: 800, fontSize: 22, color: c.text, lineHeight: 1.1 }}>{title}</h2>
          <div style={{ marginTop: 12, display: 'flex', gap: 7 }}>
            {steps.map((s, i) => {
              const active = i === step;
              const stepDone = !!done[s.id] || (isDone && i < steps.length - 1);
              return (
                <button key={s.id} onClick={() => setStep(i)} aria-label={s.tab} style={{
                  width: 22, height: 22, borderRadius: 999, border: 'none', cursor: 'pointer',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  background: active ? c.accent : (stepDone ? (c.soft2 || c.accentSoft) : c.surfaceAlt),
                  color: active ? c.accentInk : (stepDone ? (c.accent2 || c.accent) : c.textMuted),
                  fontFamily: c.headingStack, fontSize: 12, fontWeight: 800,
                }}>{stepDone ? '✓' : i + 1}</button>
              );
            })}
          </div>
        </div>

        <div key={cur.id} ref={contentRef} className="asf-scroll" style={{ overflow: 'auto', padding: 18, minHeight: 0, flex: 1 }}>
          {cur.indexes.map(i => <LessonSection key={i} s={lesson.sections[i]} base={`a_${book.id}_${lesson.id}_${i}`} />)}
        </div>

        <div style={{ display: 'flex', gap: 10, padding: 12, borderTop: `1px solid ${c.border}`, background: c.footer || c.surfaceAlt }}>
          <button onClick={goBack} style={{
            border: 'none', cursor: 'pointer', borderRadius: 999, padding: '12px 16px',
            background: c.dark ? 'rgba(255,255,255,0.08)' : 'rgba(33,51,26,0.08)',
            color: c.textMuted, fontFamily: 'inherit', fontWeight: 800, fontSize: 14,
          }}>{step === 0 ? 'Close' : 'Back'}</button>
          <button onClick={goNext} style={{ ...asfPrimaryBtn(c), flex: 1, padding: '12px 16px' }}>
            {step < steps.length - 1 ? `Next: ${steps[step + 1].tab}` : (isDone ? 'Completed' : 'Mark lesson complete')}
          </button>
        </div>
      </div>
    </div>
  );
}

function LessonSection({ s, base }) {
  const c = useTheme();
  if (s.kind === 'text' && s.readingSupport === 'habits-hom1') return <SupportedReadingBlock s={s} base={base} />;
  if (s.kind === 'text') return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      {c.L(s.body).split('\n\n').map((para, j) => (
        <p key={j} style={{ margin: 0, fontFamily: "'Spectral', Georgia, serif", fontSize: 18.5, lineHeight: 1.55, color: c.text }}>{para}</p>
      ))}
    </div>
  );
  if (s.kind === 'activity') return (
    <div>
      <div style={{ fontSize: 11.5, fontWeight: 800, letterSpacing: '0.10em', textTransform: 'uppercase', color: c.accent, marginBottom: 10 }}>{c.L(s.label)}</div>
      <p style={{ margin: 0, fontFamily: "'Spectral', Georgia, serif", fontSize: 19, lineHeight: 1.5, color: c.text }}>{c.L(s.body)}</p>
      <div style={{ marginTop: 18, borderRadius: 18, background: c.surfaceAlt, padding: 14 }}>
        <div style={{ fontSize: 13, fontWeight: 800, color: c.accent, marginBottom: 6 }}>Prompt</div>
        <p style={{ margin: 0, fontSize: 15, color: c.text, lineHeight: 1.45 }}>After reading, choose one sentence that felt important and share why.</p>
        <AnswerField base={`${base}_note`} />
      </div>
    </div>
  );
  if (s.kind === 'questions') return <QuestionsBlock s={s} base={base} />;
  if (s.kind === 'choice') return <ChoiceBlock s={s} base={base} />;
  if (s.kind === 'wordbank') return <WordbankBlock s={s} base={base} />;
  if (s.kind === 'fillbank') return <FillbankBlock s={s} base={base} />;
  if (s.kind === 'attention') return <AttentionBlock s={s} base={base} />;
  if (s.kind === 'phrasepairs') return <PhrasePairsBlock s={s} base={base} />;
  if (s.kind === 'writing') return <WritingBlock s={s} base={base} />;
  if (s.kind === 'bookpage') return <BookPageBlock s={s} />;
  if (s.kind === 'prayer') return (
    <div style={{ textAlign: 'center', padding: '6px 2px 18px' }}>
      <p style={{ margin: 0, fontFamily: "'Spectral', Georgia, serif", fontSize: 19, lineHeight: 1.58, color: c.text }}>{c.L(s.body)}</p>
      <div style={{ marginTop: 10, fontSize: 13, color: c.textMuted, fontStyle: 'italic' }}>— {s.source}</div>
    </div>
  );
  return null;
}

const HABITS_HOM1_WORDS = {
  intan: { pron: 'IN-tahn', es: 'Intan', def: 'nombre de una de las amigas.', example: 'Intan es la mayor del grupo.' },
  dian: { pron: 'DEE-ahn', es: 'Dian', def: 'nombre de uno de los amigos.', example: 'Dian hace una pregunta importante.' },
  rayzal: { pron: 'RAY-zahl', es: 'Rayzal', def: 'nombre de uno de los amigos.', example: 'Rayzal se preocupa por Shan.' },
  meiling: { pron: 'MAY-ling', es: 'Meiling', def: 'nombre de la hermana mayor de Shan.', example: 'Meiling quiere ayudar a Shan.' },
  shan: { pron: 'shahn', es: 'Shan', def: 'nombre del joven que llega tarde.', example: 'Shan perdió la noción del tiempo.' },
  friends: { pron: 'frendz', es: 'amigos', def: 'personas que se quieren y pasan tiempo juntas.', example: 'Los amigos se reúnen en el parque.' },
  remember: { pron: 'ree-MEM-ber', es: 'recordar', def: 'tener algo en la memoria.', example: 'Ellos se conocen desde que pueden recordar.' },
  imagine: { pron: 'ih-MA-jin', es: 'imaginar', def: 'formar una idea en la mente.', example: 'Es difícil imaginar amigos más unidos.' },
  closer: { pron: 'KLOH-zer', es: 'más unido', def: 'con una relación más fuerte o cercana.', example: 'Es un grupo de amigos muy unido.' },
  set: { pron: 'set', es: 'grupo', def: 'conjunto de personas o cosas.', example: 'Ellos forman un grupo cercano.' },
  usually: { pron: 'YOO-zhoo-uh-lee', es: 'normalmente', def: 'casi siempre.', example: 'Normalmente los otros cuatro están cerca.' },
  parents: { pron: 'PAIR-ents', es: 'padres', def: 'mamá y papá.', example: 'Sus padres se conocieron en la universidad.' },
  major: { pron: 'MAY-jer', es: 'principales', def: 'más importantes o más grandes.', example: 'Había tres grupos principales.' },
  ethnic: { pron: 'ETH-nik', es: 'étnico', def: 'relacionado con la cultura o el origen de un grupo.', example: 'Sus familias vienen de grupos étnicos diferentes.' },
  groups: { pron: 'groops', es: 'grupos', def: 'personas que pertenecen juntas por alguna razón.', example: 'Los tres grupos viven en el mismo país.' },
  country: { pron: 'KUN-tree', es: 'país', def: 'nación o lugar donde vive una población.', example: 'Ellos viven en el mismo país.' },
  university: { pron: 'yoo-nih-VER-sih-tee', es: 'universidad', def: 'escuela después de la preparatoria.', example: 'Sus padres se conocieron en la universidad.' },
  later: { pron: 'LAY-ter', es: 'después', def: 'en un momento posterior.', example: 'Después decidieron vivir cerca.' },
  decided: { pron: 'dih-SY-ded', es: 'decidieron', def: 'escogieron hacer algo.', example: 'Decidieron vivir en el mismo vecindario.' },
  neighborhood: { pron: 'NAY-ber-hood', es: 'vecindario', def: 'área donde viven varias familias cerca unas de otras.', example: 'Viven en el mismo vecindario.' },
  strongly: { pron: 'STRONG-lee', es: 'firmemente', def: 'con mucha fuerza o convicción.', example: 'Creen firmemente en la unidad.' },
  believe: { pron: 'bih-LEEV', es: 'creer', def: 'pensar que algo es verdad.', example: 'Ellos creen en la unidad de la humanidad.' },
  oneness: { pron: 'WUN-ness', es: 'unidad', def: 'la idea de que todos somos una sola familia humana.', example: 'Creen en la unidad de la humanidad.' },
  humankind: { pron: 'HYOO-men-kynd', es: 'humanidad', def: 'todas las personas del mundo.', example: 'La humanidad es una sola familia.' },
  raise: { pron: 'rayz', es: 'criar', def: 'cuidar y educar a los hijos mientras crecen.', example: 'Quieren criar a sus hijos con ese espíritu.' },
  children: { pron: 'CHIL-dren', es: 'hijos / niños', def: 'personas jóvenes o hijos de una familia.', example: 'Quieren criar a sus hijos en unidad.' },
  spirit: { pron: 'SPIR-it', es: 'espíritu', def: 'actitud o manera de vivir una idea.', example: 'Los crían con espíritu de unidad.' },
  often: { pron: 'AW-fen', es: 'a menudo', def: 'muchas veces.', example: 'A menudo pasan tiempo juntos.' },
  spend: { pron: 'spend', es: 'pasar', def: 'usar tiempo haciendo algo.', example: 'Pasan tiempo juntos en el parque.' },
  park: { pron: 'park', es: 'parque', def: 'lugar abierto para jugar o caminar.', example: 'Se reúnen en el parque.' },
  distance: { pron: 'DIS-tens', es: 'distancia', def: 'espacio entre dos lugares.', example: 'El parque está a poca distancia.' },
  homes: { pron: 'hohmz', es: 'casas', def: 'lugares donde viven.', example: 'El parque está cerca de sus casas.' },
  planned: { pron: 'pland', es: 'planearon', def: 'decidieron hacer algo antes de hacerlo.', example: 'Planearon reunirse en el parque.' },
  meet: { pron: 'meet', es: 'reunirse', def: 'estar juntos en un lugar.', example: 'Van a reunirse en el parque.' },
  dinner: { pron: 'DIN-er', es: 'cena', def: 'comida de la tarde o noche.', example: 'Después irán a cenar.' },
  arrives: { pron: 'uh-RYVZ', es: 'llega', def: 'viene a un lugar.', example: 'Shan llega tarde.' },
  late: { pron: 'layt', es: 'tarde', def: 'después de la hora esperada.', example: 'Shan llega media hora tarde.' },
  sorry: { pron: 'SAW-ree', es: 'lo siento', def: 'palabras para disculparse.', example: 'Shan dice: lo siento.' },
  lost: { pron: 'lawst', es: 'perdí', def: 'dejé de saber o tener algo.', example: 'Perdí la noción del tiempo.' },
  track: { pron: 'trak', es: 'seguimiento', def: 'atención a cómo cambia algo.', example: 'No siguió el paso del tiempo.' },
  time: { pron: 'tym', es: 'tiempo', def: 'minutos, horas o momentos.', example: 'No se dio cuenta del tiempo.' },
  exclaims: { pron: 'eks-KLAYMZ', es: 'exclama', def: 'dice algo con emoción o fuerza.', example: 'Shan exclama que lo siente.' },
  again: { pron: 'uh-GEN', es: 'otra vez', def: 'una vez más.', example: 'Meiling dice: otra vez no.' },
  older: { pron: 'OHL-der', es: 'mayor', def: 'de más edad.', example: 'Meiling es la hermana mayor.' },
  sister: { pron: 'SIS-ter', es: 'hermana', def: 'hija de los mismos padres.', example: 'Meiling es la hermana de Shan.' },
  classmate: { pron: 'KLAS-mayt', es: 'compañero de clase', def: 'persona que estudia en la misma clase.', example: 'Rayzal es compañero de clase de Shan.' },
  school: { pron: 'skool', es: 'escuela', def: 'lugar donde se estudia.', example: 'Van a la escuela juntos.' },
  beginning: { pron: 'bih-GIN-ing', es: 'empezando', def: 'iniciando algo.', example: 'Rayzal está empezando a preocuparse.' },
  worry: { pron: 'WER-ee', es: 'preocuparse', def: 'sentir inquietud por algo.', example: 'Rayzal se preocupa por Shan.' },
  distracted: { pron: 'dih-STRAK-ted', es: 'distraído', def: 'sin prestar atención a lo importante.', example: 'Shan está distraído últimamente.' },
  yesterday: { pron: 'YES-ter-day', es: 'ayer', def: 'el día antes de hoy.', example: 'Ayer no encontró su libro.' },
  lend: { pron: 'lend', es: 'prestar', def: 'dar algo por un tiempo para que otra persona lo use.', example: 'Rayzal tuvo que prestarle su libro.' },
  textbook: { pron: 'TEKST-book', es: 'libro de texto', def: 'libro usado para estudiar una materia.', example: 'No podía encontrar su libro de texto.' },
  find: { pron: 'fynd', es: 'encontrar', def: 'descubrir dónde está algo.', example: 'Shan no pudo encontrar su libro.' },
  know: { pron: 'noh', es: 'saber', def: 'entender o tener información.', example: 'Shan dice que sabe lo que pasa.' },
  replies: { pron: 'rih-PLYZ', es: 'responde', def: 'contesta a alguien.', example: 'Shan responde a Rayzal.' },
  forgetting: { pron: 'fer-GET-ing', es: 'olvidando', def: 'dejando de recordar.', example: 'Shan sigue olvidando cosas.' },
  problem: { pron: 'PRAH-blem', es: 'problema', def: 'dificultad que necesita atención.', example: 'No tenía este problema antes.' },
  couple: { pron: 'KUH-pul', es: 'un par', def: 'dos o unos pocos.', example: 'Hace un par de meses era diferente.' },
  months: { pron: 'munths', es: 'meses', def: 'partes del año.', example: 'No tenía este problema hace unos meses.' },
  true: { pron: 'troo', es: 'verdad', def: 'correcto o real.', example: 'Meiling dice que eso es verdad.' },
  father: { pron: 'FAH-ther', es: 'padre', def: 'papá.', example: 'Su padre hablaba de una mente ordenada.' },
  used: { pron: 'yoozd', es: 'solía', def: 'hacía algo en el pasado.', example: 'Su padre solía decir eso.' },
  orderly: { pron: 'OR-der-lee', es: 'ordenado', def: 'que está organizado y en buen orden.', example: 'Una mente ordenada puede enfocarse mejor.' },
  mind: { pron: 'mynd', es: 'mente', def: 'parte de la persona con la que piensa y recuerda.', example: 'Una mente ordenada ayuda a pensar con claridad.' },
  asks: { pron: 'asks', es: 'pregunta', def: 'dice algo para buscar una respuesta.', example: 'Dian pregunta qué significa eso.' },
  sure: { pron: 'shoor', es: 'seguro', def: 'tener confianza en algo.', example: 'Intan no está segura.' },
  house: { pron: 'hows', es: 'casa', def: 'lugar donde vive una familia.', example: 'Nuestra casa está ordenada.' },
  library: { pron: 'LY-brer-ee', es: 'biblioteca', def: 'lugar con libros organizados.', example: 'La escuela tiene una biblioteca ordenada.' },
  laughs: { pron: 'lafs', es: 'se ríe', def: 'muestra alegría con risa.', example: 'Rayzal se ríe al hablar de la biblioteca.' },
  seriously: { pron: 'SEER-ee-us-lee', es: 'en serio', def: 'de manera seria, no jugando.', example: 'Dian pregunta en serio.' },
  mean: { pron: 'meen', es: 'significar', def: 'querer decir.', example: '¿Qué significa una mente ordenada?' },
  silence: { pron: 'SY-lens', es: 'silencio', def: 'momento sin hablar o sin ruido.', example: 'Hay silencio por un momento.' },
  moment: { pron: 'MOH-ment', es: 'momento', def: 'poco tiempo.', example: 'Se quedan en silencio por un momento.' },
  responds: { pron: 'rih-SPONDZ', es: 'responde', def: 'contesta.', example: 'Meiling responde después.' },
  entirely: { pron: 'en-TY-er-lee', es: 'completamente', def: 'totalmente.', example: 'No está completamente segura.' },
};

const READING_COMMON_WORDS = {
  a: ['un / una', 'artículo para hablar de una cosa.', 'Veo una cosa importante.'],
  about: ['sobre / acerca de', 'indica el tema de lo que se habla.', 'Hablamos sobre la historia.'],
  ago: ['hace', 'indica tiempo pasado.', 'Eso pasó hace poco.'],
  am: ['soy / estoy', 'forma del verbo ser o estar con “I”.', 'Estoy listo para leer.'],
  an: ['un / una', 'artículo usado antes de algunas palabras.', 'Tengo una idea.'],
  and: ['y', 'une dos palabras o ideas.', 'Intan y Dian son amigos.'],
  are: ['son / están', 'forma del verbo ser o estar.', 'Ellos están juntos.'],
  as: ['como / mientras', 'se usa para comparar o conectar ideas.', 'Lo hizo como buen amigo.'],
  ask: ['preguntar', 'decir algo para buscar una respuesta.', 'Voy a preguntar con respeto.'],
  at: ['en / a', 'indica lugar, momento o dirección.', 'Estamos en el parque.'],
  because: ['porque', 'introduce la razón de algo.', 'Llegó tarde porque perdió la noción del tiempo.'],
  been: ['sido / estado', 'forma del verbo ser o estar.', 'Han sido amigos por mucho tiempo.'],
  but: ['pero', 'une ideas diferentes u opuestas.', 'Quería llegar, pero llegó tarde.'],
  by: ['por / cerca de', 'puede indicar quién hace algo o dónde está.', 'El libro está cerca de la mesa.'],
  can: ['puede / pueden', 'indica habilidad o posibilidad.', 'Ellos pueden ayudarse.'],
  catch: ['alcanzar / ponerse al día', 'en esta historia forma parte de “catch up”.', 'Van a ponerse al día.'],
  could: ['podía / pudieron', 'forma pasada de poder.', 'No podía encontrar su libro.'],
  did: ['hizo / usó para preguntar o negar', 'ayuda a formar preguntas o frases en pasado.', 'No hizo eso ayer.'],
  do: ['hacer', 'verbo usado para acciones o preguntas.', '¿Qué debemos hacer?'],
  each: ['cada', 'habla de una persona o cosa individual.', 'Cada amigo escucha.'],
  five: ['cinco', 'número 5.', 'Hay cinco amigos.'],
  for: ['para / por', 'indica propósito, tiempo o razón.', 'Es para el grupo.'],
  four: ['cuatro', 'número 4.', 'Los otros cuatro están cerca.'],
  fourteen: ['catorce', 'número 14.', 'Intan tiene catorce años.'],
  from: ['de / desde', 'indica origen o punto de partida.', 'Vienen de diferentes familias.'],
  go: ['ir', 'moverse a otro lugar.', 'Van a ir a cenar.'],
  group: ['grupo', 'conjunto de personas.', 'El grupo se reúne los lunes.'],
  had: ['tenía / tuvo', 'forma pasada de tener.', 'Tuvo que prestar su libro.'],
  half: ['media / mitad', 'una de dos partes iguales.', 'Llegó media hora tarde.'],
  hard: ['difícil', 'que cuesta trabajo.', 'Es difícil imaginar amigos más unidos.'],
  has: ['tiene / ha', 'forma del verbo tener o haber.', 'La escuela tiene una biblioteca.'],
  have: ['tener / haber', 'poseer algo o formar algunos tiempos verbales.', 'Ellos tienen una amistad fuerte.'],
  he: ['él', 'pronombre para un hombre o niño.', 'Él llegó tarde.'],
  home: ['casa / hogar', 'lugar donde vive alguien.', 'Van a la casa de Meiling.'],
  how: ['cómo', 'pregunta por la manera de algo.', '¿Cómo pasó eso?'],
  hour: ['hora', 'periodo de 60 minutos.', 'Shan llega media hora tarde.'],
  i: ['yo', 'pronombre para hablar de uno mismo.', 'Yo quiero entender.'],
  in: ['en', 'indica lugar, tiempo o situación.', 'Viven en el mismo vecindario.'],
  is: ['es / está', 'forma del verbo ser o estar.', 'Shan está distraído.'],
  it: ['eso / ello', 'pronombre para una cosa o idea.', 'Eso es importante.'],
  just: ['solo / simplemente', 'indica algo exacto o limitado.', 'Así soy yo simplemente.'],
  keep: ['seguir / continuar', 'hacer algo repetidamente o sin parar.', 'Sigo olvidando cosas.'],
  let: ['dejar / vamos a', 'permite algo o invita a hacerlo.', 'Vamos a preguntarle a mi padre.'],
  live: ['vivir', 'tener una casa o residencia.', 'Viven cerca unos de otros.'],
  long: ['largo / mucho tiempo', 'indica duración o tamaño.', 'Se conocen desde hace mucho tiempo.'],
  many: ['muchos / muchas', 'gran cantidad.', 'Se conocieron hace muchos años.'],
  met: ['conocieron / se encontraron', 'pasado de conocer o reunirse.', 'Sus padres se conocieron en la universidad.'],
  my: ['mi', 'indica que algo pertenece a quien habla.', 'Mi libro está aquí.'],
  near: ['cerca de', 'a poca distancia.', 'Viven cerca unos de otros.'],
  not: ['no', 'niega una idea.', 'Eso no es correcto.'],
  of: ['de', 'conecta posesión, parte o relación.', 'La unidad de la humanidad.'],
  oldest: ['mayor', 'la persona con más edad.', 'Intan es la mayor del grupo.'],
  on: ['en / sobre', 'indica lugar o tema.', 'El libro está sobre la mesa.'],
  one: ['uno / una', 'número 1 o una persona/cosa.', 'Ves a uno de los amigos.'],
  other: ['otro / otra', 'diferente o restante.', 'Los otros amigos están cerca.'],
  our: ['nuestro / nuestra', 'pertenece a nosotros.', 'Nuestra casa está ordenada.'],
  same: ['mismo / misma', 'igual, no diferente.', 'Viven en el mismo vecindario.'],
  say: ['decir', 'expresar palabras.', 'Van a decir lo que piensan.'],
  says: ['dice', 'expresa palabras.', 'Meiling dice algo a Shan.'],
  see: ['ver', 'mirar o notar algo.', 'Puedes ver a los amigos juntos.'],
  she: ['ella', 'pronombre para una mujer o niña.', 'Ella es la mayor.'],
  short: ['corto / poca', 'de poca longitud o duración.', 'El parque está a poca distancia.'],
  that: ['eso / que', 'señala una idea o conecta partes de una oración.', 'Eso es verdad.'],
  the: ['el / la / los / las', 'artículo para hablar de algo específico.', 'El grupo está en el parque.'],
  their: ['su / sus', 'pertenece a ellos.', 'Sus padres se conocieron antes.'],
  then: ['entonces / luego', 'después de eso.', 'Luego van a cenar.'],
  there: ['hay / allí', 'indica existencia o lugar.', 'Hay silencio por un momento.'],
  they: ['ellos / ellas', 'pronombre para un grupo.', 'Ellos son amigos.'],
  things: ['cosas', 'objetos, ideas o asuntos.', 'Hablan de muchas cosas.'],
  this: ['esto / este / esta', 'señala algo cercano o actual.', 'Esto es importante.'],
  three: ['tres', 'número 3.', 'Hay tres grupos principales.'],
  to: ['a / para', 'indica dirección, propósito o relación.', 'Van a la casa de Meiling.'],
  today: ['hoy', 'este día.', 'Hoy se reúnen en el parque.'],
  together: ['juntos', 'con otras personas.', 'Pasan tiempo juntos.'],
  up: ['arriba / al día', 'en esta historia forma parte de “catch up”.', 'Se ponen al día.'],
  us: ['nos / nosotros', 'pronombre para hablar del grupo que incluye a quien habla.', 'Vamos a preguntarle nosotros.'],
  want: ['querer', 'desear hacer o tener algo.', 'Quieren criar a sus hijos en unidad.'],
  we: ['nosotros', 'pronombre para un grupo que incluye a quien habla.', 'Nosotros aprendemos juntos.'],
  what: ['qué', 'pregunta por una cosa o idea.', '¿Qué significa eso?'],
  where: ['donde / dónde', 'pregunta o indica lugar.', '¿Dónde está el libro?'],
  who: ['quien / quién', 'pregunta o indica una persona.', '¿Quién llegó tarde?'],
  years: ['años', 'periodos de doce meses.', 'Se conocieron hace muchos años.'],
  you: ['tú / ustedes', 'pronombre para la persona a quien se habla.', 'Tú puedes leer esta oración.'],
  yours: ['el tuyo / la tuya', 'algo que pertenece a ti.', 'No encontró el suyo.'],
};

const HABITS_HOM1_PHRASES = {
  'as long as they can remember': { es: 'desde que tienen memoria', explanation: 'Significa desde hace muchísimo tiempo, desde que eran pequeños.' },
  'a closer set of friends': { es: 'un grupo de amigos más unido', explanation: 'Un grupo con una amistad muy fuerte.' },
  'where you see one': { es: 'donde ves a uno', explanation: 'Si encuentras a uno de ellos, normalmente los demás están cerca.' },
  'the other four': { es: 'los otros cuatro', explanation: 'Los cuatro amigos restantes.' },
  'major ethnic groups': { es: 'principales grupos étnicos', explanation: 'Grupos grandes de personas con cultura u origen compartido.' },
  'many years ago': { es: 'hace muchos años', explanation: 'En el pasado, hace bastante tiempo.' },
  'same neighborhood': { es: 'el mismo vecindario', explanation: 'Viven cerca unos de otros.' },
  'oneness of humankind': { es: 'unidad de la humanidad', explanation: 'La idea de que todas las personas pertenecen a una sola familia humana.' },
  'in that spirit': { es: 'con ese espíritu', explanation: 'Con esa actitud o manera de pensar.' },
  'spend time together': { es: 'pasar tiempo juntos', explanation: 'Estar juntos haciendo algo.' },
  'a short distance': { es: 'a poca distancia', explanation: 'Cerca, no muy lejos.' },
  'catch up on things': { es: 'ponerse al día', explanation: 'Hablar de lo que ha pasado recientemente.' },
  'lost track of time': { es: 'perdí la noción del tiempo', explanation: 'No se dio cuenta de cuánto tiempo había pasado.' },
  'not again': { es: 'otra vez no', explanation: 'Se dice cuando algo ha pasado antes y molesta un poco.' },
  'older sister': { es: 'hermana mayor', explanation: 'Una hermana que tiene más edad.' },
  'classmate at school': { es: 'compañero de clase en la escuela', explanation: 'Alguien que estudia en la misma clase.' },
  'beginning to worry': { es: 'empezando a preocuparme', explanation: 'Está empezando a sentir inquietud por Shan.' },
  'had to lend you my textbook': { es: 'tuve que prestarte mi libro de texto', explanation: 'Rayzal le dio su libro a Shan por un tiempo.' },
  'could not find yours': { es: 'no podías encontrar el tuyo', explanation: 'Shan no sabía dónde estaba su propio libro.' },
  'that is just how i am': { es: 'así soy yo', explanation: 'Shan piensa que olvidar cosas es parte de su manera de ser.' },
  'keep forgetting things': { es: 'sigo olvidando cosas', explanation: 'Continúa olvidando cosas una y otra vez.' },
  'a couple of months ago': { es: 'hace un par de meses', explanation: 'Hace unos dos meses.' },
  'orderly mind': { es: 'mente ordenada', explanation: 'Una mente organizada, atenta y clara.' },
  'there is silence for a moment': { es: 'hay silencio por un momento', explanation: 'Nadie habla durante un poco de tiempo.' },
  'not entirely sure': { es: 'no estoy completamente segura', explanation: 'No sabe la respuesta con total certeza.' },
};

function SupportedReadingBlock({ s, base }) {
  const c = useTheme();
  const text = c.L(s.body);
  const paragraphs = React.useMemo(() => buildSupportedParagraphs(text), [text]);
  const [popup, setPopup] = React.useState(null);
  const [saved, setSaved] = useLocal(`${base}_saved_words`, {});
  const longPress = React.useRef(null);
  const drag = React.useRef(null);
  const clearLongPress = () => {
    if (longPress.current?.timer) window.clearTimeout(longPress.current.timer);
    longPress.current = null;
  };

  const saveWord = (word, entry) => {
    if (!word || !entry) return;
    const key = cleanReadingWord(word);
    setSaved(prev => ({ ...prev, [key]: { word, es: entry.es, at: Date.now() } }));
  };

  const showWord = (word, target) => {
    const entry = lookupReadingWord(word);
    speakReadingEnglish(word);
    saveWord(word, entry);
    setPopup({
      type: 'word',
      word,
      entry,
      saved: !!entry,
      pos: readingPopupPos(target),
    });
  };

  const showPhrase = (indexes, target) => {
    const ordered = [...indexes].sort((a, b) => a - b);
    const phrase = ordered.map(i => document.querySelector(`[data-rs-word="${i}"]`)?.textContent || '').join(' ').replace(/\s+/g, ' ').trim();
    if (!phrase) return;
    const entry = lookupReadingPhrase(phrase);
    if (entry) setSaved(prev => ({ ...prev, [`phrase_${cleanReadingPhrase(phrase)}`]: { word: phrase, es: entry.es, at: Date.now(), phrase: true } }));
    setPopup({
      type: 'phrase',
      phrase,
      entry,
      pos: readingPopupPos(target),
    });
  };

  const startLongPress = (idx, target) => {
    drag.current = { active: true, start: idx, end: idx, target };
    setPopup(null);
    setSelectedRange(idx, idx);
  };

  const setSelectedRange = (start, end) => {
    document.querySelectorAll('[data-rs-word]').forEach(el => {
      const i = Number(el.getAttribute('data-rs-word'));
      const on = i >= Math.min(start, end) && i <= Math.max(start, end);
      el.setAttribute('data-rs-selected', on ? 'true' : 'false');
    });
  };

  const clearSelection = () => {
    document.querySelectorAll('[data-rs-word][data-rs-selected="true"]').forEach(el => el.setAttribute('data-rs-selected', 'false'));
  };

  const handlePointerMove = (event) => {
    if (!drag.current?.active && longPress.current) {
      const pending = longPress.current;
      const dx = event.clientX - pending.x;
      const dy = event.clientY - pending.y;
      if (Math.abs(dx) > 14 && Math.abs(dx) > Math.abs(dy)) {
        clearLongPress();
        startLongPress(pending.idx, pending.target);
      } else if (Math.abs(dy) > 18 && Math.abs(dy) > Math.abs(dx)) {
        clearLongPress();
      }
    }
    if (!drag.current?.active) return;
    event.preventDefault();
    const el = document.elementFromPoint(event.clientX, event.clientY)?.closest?.('[data-rs-word]');
    if (!el) return;
    const idx = Number(el.getAttribute('data-rs-word'));
    drag.current.end = idx;
    drag.current.target = el;
    setSelectedRange(drag.current.start, idx);
  };

  const handlePointerUp = (event) => {
    const pending = longPress.current;
    clearLongPress();
    if (drag.current?.active) {
      event.preventDefault();
      const start = drag.current.start;
      const end = drag.current.end;
      const target = drag.current.target || event.target;
      drag.current = null;
      const indexes = [];
      for (let i = Math.min(start, end); i <= Math.max(start, end); i++) indexes.push(i);
      window.setTimeout(clearSelection, 450);
      showPhrase(indexes, target);
      return;
    }
    const word = event.target?.getAttribute?.('data-rs-token');
    if (word && pending?.word === word) showWord(word, event.target);
  };

  const handlePointerDown = (event, token) => {
    clearLongPress();
    const idx = Number(event.currentTarget.getAttribute('data-rs-word'));
    longPress.current = {
      word: token,
      idx,
      target: event.currentTarget,
      x: event.clientX,
      y: event.clientY,
      timer: window.setTimeout(() => startLongPress(idx, event.currentTarget), 420),
    };
  };

  return (
    <div onPointerMove={handlePointerMove} onPointerUp={handlePointerUp} onPointerCancel={() => { clearLongPress(); clearSelection(); drag.current = null; }} style={{ position: 'relative' }}>
      <div style={{
        display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap', marginBottom: 13,
        padding: '10px 12px', borderRadius: 14, background: c.surfaceAlt, color: c.textMuted, fontSize: 13, lineHeight: 1.35,
      }}>
        <Icon name="spark" size={15} />
        <span>Tap a word for Spanish help. Hold and drag across words for phrase help. Audio may use an AI voice.</span>
        <span style={{ marginLeft: 'auto', fontWeight: 800, color: c.accent }}>{Object.keys(saved || {}).length} saved</span>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
        {paragraphs.map((para, pIdx) => (
          <p key={pIdx} style={{
            margin: 0, fontFamily: "'Spectral', Georgia, serif", fontSize: 18.5, lineHeight: 1.62, color: c.text,
          }}>
            {para.sentences.map((sentence, sIdx) => (
              <span key={sentence.id} style={{ display: 'inline' }}>
                <button onClick={() => speakReadingEnglish(sentence.text)} aria-label={`Hear sentence ${sIdx + 1}`} style={{
                  width: 19, height: 19, border: 'none', borderRadius: 999, margin: '0 5px 0 0',
                  background: c.accent, color: c.accentInk, cursor: 'pointer', verticalAlign: '1px',
                  display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                  opacity: 0.9,
                }}><Icon name="volume" size={10} /></button>
                {sentence.tokens.map((token, tIdx) => token.kind === 'word' ? (
                  <span key={tIdx}
                    data-rs-word={token.wordIndex}
                    data-rs-token={token.text}
                    onPointerDown={(event) => handlePointerDown(event, token.text)}
                    onClick={(event) => event.preventDefault()}
                    style={{
                      borderRadius: 7, padding: '0 2px', cursor: 'pointer', touchAction: 'manipulation',
                      transition: 'background .12s ease, box-shadow .12s ease',
                    }}
                  >{token.text}</span>
                ) : <React.Fragment key={tIdx}>{token.text}</React.Fragment>)}
              </span>
            ))}
          </p>
        ))}
      </div>
      {popup && <ReadingSupportPopup c={c} popup={popup} close={() => setPopup(null)} />}
      <style>{`
        [data-rs-word][data-rs-selected="true"] {
          background: rgba(224, 98, 60, 0.18);
          box-shadow: inset 0 -2px 0 rgba(224, 98, 60, 0.5);
        }
      `}</style>
    </div>
  );
}

function ReadingSupportPopup({ c, popup, close }) {
  const pos = popup.pos || { top: 130, left: 190, below: false };
  const entry = popup.entry;
  const isWord = popup.type === 'word';
  return (
    <React.Fragment>
    <div onPointerDown={close} style={{ position: 'fixed', inset: 0, zIndex: 9998, background: 'transparent' }} />
    <div style={{
      position: 'fixed', zIndex: 9999, left: pos.left, top: pos.top,
      transform: pos.below ? 'translateX(-50%)' : 'translate(-50%, -100%)',
      width: 'min(330px, calc(100vw - 24px))',
      borderRadius: 18, background: c.surface, color: c.text, padding: 14,
      border: `1px solid ${c.border}`,
      boxShadow: c.dark ? '0 18px 36px rgba(0,0,0,0.5)' : '0 18px 38px rgba(36,54,31,0.24)',
    }} onPointerDown={e => e.stopPropagation()}>
      <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>
        {isWord && <button onClick={() => speakReadingEnglish(popup.word)} style={{
          width: 34, height: 34, borderRadius: 999, border: 'none', background: c.accent,
          color: c.accentInk, display: 'grid', placeItems: 'center', flexShrink: 0,
        }}><Icon name="volume" size={16} /></button>}
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{ fontFamily: c.headingStack, fontWeight: 900, fontSize: 18, lineHeight: 1.15 }}>{isWord ? popup.word : popup.phrase}</div>
          {isWord && entry?.pron && <div style={{ marginTop: 3, fontSize: 12.5, fontWeight: 800, color: c.textMuted }}>{entry.pron}</div>}
        </div>
        <button onClick={close} aria-label="Close" style={{
          border: 'none', background: 'transparent', color: c.textMuted, cursor: 'pointer',
          fontSize: 18, fontWeight: 900, lineHeight: 1,
        }}>×</button>
      </div>
      {entry ? (
        <div style={{ marginTop: 12, display: 'flex', flexDirection: 'column', gap: 8 }}>
          <SupportLine c={c} label={isWord ? 'Spanish' : 'Traducción'} text={entry.es} />
          {isWord && <SupportLine c={c} label="Definición" text={entry.def} />}
          {entry.explanation && <SupportLine c={c} label="Explicación" text={entry.explanation} />}
          {isWord && <SupportLine c={c} label="Ejemplo" text={entry.example} />}
          {isWord && <div style={{ fontSize: 12.5, fontWeight: 800, color: c.accent }}>Saved automatically</div>}
        </div>
      ) : (
        <div style={{ marginTop: 12, color: c.textMuted, fontSize: 14, lineHeight: 1.4 }}>
          Spanish support is not available yet for this selection.
        </div>
      )}
    </div>
    </React.Fragment>
  );
}

function SupportLine({ c, label, text }) {
  return (
    <div style={{ display: 'grid', gridTemplateColumns: '86px 1fr', gap: 8, alignItems: 'baseline' }}>
      <div style={{ fontSize: 12, fontWeight: 900, color: c.accent, textTransform: 'uppercase', letterSpacing: '0.07em' }}>{label}</div>
      <div style={{ fontSize: 14.5, color: c.text, lineHeight: 1.38 }}>{text}</div>
    </div>
  );
}

function buildSupportedParagraphs(text) {
  let wordIndex = 0;
  return text.split('\n\n').map((para, pIdx) => {
    const sentenceTexts = splitReadingSentences(para);
    return {
      id: pIdx,
      sentences: sentenceTexts.map((sentence, sIdx) => ({
        id: `${pIdx}_${sIdx}`,
        text: sentence,
        tokens: tokenizeReadingSentence(sentence).map(token => {
          if (token.kind !== 'word') return token;
          return { ...token, wordIndex: wordIndex++ };
        }),
      })),
    };
  });
}

function splitReadingSentences(para) {
  const matches = para.match(/[^.!?]+[.!?]+[”"]?|[^.!?]+$/g);
  const pieces = (matches || [para]).map(s => s.trimStart()).filter(Boolean);
  const merged = [];
  for (let i = 0; i < pieces.length; i++) {
    const current = pieces[i];
    const next = pieces[i + 1];
    if (next && /^(asks|says|replies|exclaims|laughs|responds)\b/i.test(next.trim())) {
      merged.push(`${current} ${next}`);
      i++;
    } else {
      merged.push(current);
    }
  }
  return merged.map((s, i, arr) => i < arr.length - 1 && !/\s$/.test(s) ? `${s} ` : s);
}

function tokenizeReadingSentence(sentence) {
  const parts = sentence.match(/[A-Za-z]+(?:[’'][A-Za-z]+)?|[0-9]+|\s+|./g) || [sentence];
  return parts.map(part => /[A-Za-z]/.test(part) ? { kind: 'word', text: part } : { kind: 'text', text: part });
}

function cleanReadingWord(word) {
  return String(word || '').toLowerCase().replace(/[“”".,!?;:()]/g, '').replace(/[’']s$/, '');
}

function cleanReadingPhrase(phrase) {
  return String(phrase || '').toLowerCase().replace(/[“”".,!?;:()]/g, '').replace(/\s+/g, ' ').trim();
}

function lookupReadingWord(word) {
  const key = cleanReadingWord(word);
  const entry = HABITS_HOM1_WORDS[key] || HABITS_HOM1_WORDS[key.replace(/s$/, '')] || HABITS_HOM1_WORDS[key.replace(/ed$/, '')];
  if (entry) return entry;
  const common = READING_COMMON_WORDS[key] || READING_COMMON_WORDS[key.replace(/s$/, '')] || READING_COMMON_WORDS[key.replace(/ed$/, '')];
  if (common) {
    return {
      es: common[0],
      def: common[1],
      example: common[2],
    };
  }
  return {
    es: key,
    def: 'palabra del texto. Podemos mejorar esta traducción después.',
    example: `La palabra "${key}" aparece en esta parte de la historia.`,
  };
}

function lookupReadingPhrase(phrase) {
  const key = cleanReadingPhrase(phrase);
  if (HABITS_HOM1_PHRASES[key]) return HABITS_HOM1_PHRASES[key];
  const words = key.split(' ').filter(Boolean).map(w => lookupReadingWord(w));
  return { es: words.map(w => w.es.split('/')[0].trim()).join(' ') };
}

function readingPopupPos(target) {
  const rect = target?.getBoundingClientRect?.();
  if (!rect) return { left: 190, top: 140, below: false };
  const left = Math.max(170, Math.min(window.innerWidth - 170, rect.left + rect.width / 2));
  const below = rect.top < 155;
  return {
    left,
    top: below ? Math.min(window.innerHeight - 180, rect.bottom + 8) : Math.max(12, rect.top - 8),
    below,
  };
}

const readingAudioCache = new Map();
let readingAudioPlayer = null;

async function speakReadingEnglish(text) {
  const phrase = String(text || '').replace(/\s+/g, ' ').trim();
  if (!phrase) return;
  try {
    if (readingAudioPlayer) {
      readingAudioPlayer.pause();
      readingAudioPlayer.currentTime = 0;
    }
  } catch {}
  try { window.speechSynthesis.cancel(); } catch {}

  if (/^https?:$/.test(window.location.protocol)) {
    try {
      const url = await getReadingTtsUrl(phrase);
      if (url) {
        readingAudioPlayer = readingAudioPlayer || new Audio();
        readingAudioPlayer.src = url;
        readingAudioPlayer.currentTime = 0;
        await readingAudioPlayer.play();
        return;
      }
    } catch {
      // The Cloudflare function may be missing locally, offline, or waiting for an API key.
    }
  }

  speakReadingBrowserVoice(phrase);
}

async function getReadingTtsUrl(text) {
  if (readingAudioCache.has(text)) return readingAudioCache.get(text);
  const response = await fetch('/api/tts', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ text, voice: 'marin' }),
  });
  if (!response.ok) return null;
  const blob = await response.blob();
  if (!blob.type.startsWith('audio/')) return null;
  const url = URL.createObjectURL(blob);
  readingAudioCache.set(text, url);
  return url;
}

function speakReadingBrowserVoice(text) {
  if (!('speechSynthesis' in window) || typeof SpeechSynthesisUtterance === 'undefined') return;
  try { window.speechSynthesis.cancel(); } catch {}
  const utterance = new SpeechSynthesisUtterance(text);
  utterance.lang = 'en-US';
  utterance.rate = 0.95;
  utterance.pitch = 1;
  const voices = window.speechSynthesis.getVoices ? window.speechSynthesis.getVoices() : [];
  const preferred = voices.find(v => /samantha|ava|allison|aria|jenny|natural|google us english/i.test(v.name))
    || voices.find(v => /^en-US/i.test(v.lang))
    || voices.find(v => /^en/i.test(v.lang));
  if (preferred) utterance.voice = preferred;
  window.speechSynthesis.speak(utterance);
}

function BookPageBlock({ s }) {
  const c = useTheme();
  return (
    <div style={{ display: 'flex', justifyContent: 'center' }}>
      <img src={s.image} alt={c.L(s.alt) || c.L(s.label)} style={{
        width: '100%', maxWidth: 780, height: 'auto', display: 'block',
        borderRadius: 10, background: '#fff', border: `1px solid ${c.border}`,
        boxShadow: c.dark ? '0 14px 28px rgba(0,0,0,0.28)' : '0 14px 30px rgba(30,45,24,0.12)',
      }} />
    </div>
  );
}

function TopBar({ back, label }) {
  const c = useTheme();
  return (
    <div style={{ position: 'sticky', top: 0, zIndex: 30, paddingTop: 50, paddingBottom: 8,
      background: `linear-gradient(${c.bg}, ${c.bg} 70%, transparent)` }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '0 16px' }}>
        <IconBtn name="left" size={38} onClick={back} />
        {label && <span style={{ fontSize: 15, fontWeight: 700, color: c.text, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{label}</span>}
      </div>
    </div>
  );
}

Object.assign(window, { LibraryScreen, BookScreen, ReaderScreen, LessonPager, Lesson8Pager: LessonPager, TopBar, frac });
