import React, { useEffect, useRef, useState } from 'react'
import { DndContext, closestCenter, PointerSensor, useSensor, useSensors } from '@dnd-kit/core'
import { arrayMove, SortableContext, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
import type { ChangeEvent } from 'react'
import { Link } from 'react-router-dom'
import type { SiteContent, CustomLink, CustomBlock, ColossiansStudySection, StudyProgram, RedirectRule, PodcastFeaturedLink, SeoSettings, LegalSettings } from './content'
import { DEFAULTS } from './content'
import { AnalyticsPanel } from './components/AnalyticsPanel'
import { AdminCollapsibleCard } from './components/AdminCollapsibleCard'
import { useAutosave } from './hooks/useAutosave'
import QRCode from 'qrcode'

interface SortableLessonSectionProps {
  section: ColossiansStudySection
  study: StudyProgram
  updateStudySection: (studyId: string, sectionId: string, field: keyof ColossiansStudySection, value: string | string[] | number) => void
  removeStudySection: (studyId: string, sectionId: string) => void
  rssEpisodes: Array<{ title: string; audioUrl: string; duration: string; episode: string }>
}

function SortableLessonSection({ section, study, updateStudySection, removeStudySection, rssEpisodes }: SortableLessonSectionProps) {
  const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: section.id })
  const [bsbFetching, setBsbFetching] = useState(false)
  const [bsbMsg, setBsbMsg] = useState('')

  async function fetchBsbPassage() {
    const bookKey = (study.bibleBook?.trim() || study.slug).toLowerCase()
    const reference = section.reference?.trim()
    if (!bookKey || !reference) { setBsbMsg('Set the passage reference first.'); return }
    setBsbFetching(true)
    setBsbMsg('')
    try {
      const params = new URLSearchParams({ book: bookKey, reference })
      const res = await fetch(`/api/bible-passage?${params}`)
      const data = await res.json() as { text?: string; message?: string }
      if (!res.ok || !data.text) { setBsbMsg(data.message ?? 'Passage not found.'); return }
      updateStudySection(study.id, section.id, 'passageText', data.text)
      setBsbMsg('Passage filled from BSB.')
    } catch {
      setBsbMsg('Could not reach the BSB API.')
    } finally {
      setBsbFetching(false)
    }
  }
  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    opacity: isDragging ? 0.5 : 1,
    background: isDragging ? '#f8f7f2' : undefined,
    border: isDragging ? '2px solid #e0c070' : undefined,
    marginBottom: '0.5rem',
  }

  return (
    <details ref={setNodeRef} style={style} className="admin-collapsible-card" open={false}>
      <summary className="admin-collapsible-summary" {...attributes} {...listeners} style={{ cursor: 'grab', userSelect: 'none' }}>
        <div>
          <strong>Lesson {section.reference || section.id}</strong>
          <p>{section.title || 'Untitled section'}</p>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
          <a href={`/study/${study.slug}/${section.id}`} target="_blank" rel="noopener noreferrer" className="btn-admin-secondary" onClick={e => e.stopPropagation()}>Preview</a>
          <span className="admin-collapsible-hint">Expand to edit lesson</span>
        </div>
      </summary>
      <div className="admin-collapsible-body">
        <div className="admin-array-row">
          <div className="admin-array-fields">
            <div className="admin-field">
              <label htmlFor={`study-section-chapter-${study.id}-${section.id}`}>Chapter</label>
              <select id={`study-section-chapter-${study.id}-${section.id}`} value={section.chapter} onChange={e => updateStudySection(study.id, section.id, 'chapter', Number(e.target.value) || 1)}>
                {Array.from({ length: Math.max(study.numberOfChapters, section.chapter, 1) }, (_, index) => index + 1).map(chapterNumber => (
                  <option key={chapterNumber} value={chapterNumber}>Chapter {chapterNumber}</option>
                ))}
              </select>
            </div>
            <div className="admin-field">
              <label htmlFor={`study-section-reference-${study.id}-${section.id}`}>Passage Reference</label>
              <input id={`study-section-reference-${study.id}-${section.id}`} type="text" value={section.reference} placeholder="1:1-2" onChange={e => updateStudySection(study.id, section.id, 'reference', e.target.value)} />
            </div>
            <div className="admin-field">
              <label htmlFor={`study-section-title-${study.id}-${section.id}`}>Section Title</label>
              <input id={`study-section-title-${study.id}-${section.id}`} type="text" value={section.title} placeholder="Paul's Greeting" onChange={e => updateStudySection(study.id, section.id, 'title', e.target.value)} />
            </div>
            <div className="admin-field">
              <label htmlFor={`study-section-audio-${study.id}-${section.id}`}>Episode Audio (optional)</label>
              <select id={`study-section-audio-${study.id}-${section.id}`} value={section.audioEmbedUrl ?? ''} onChange={e => updateStudySection(study.id, section.id, 'audioEmbedUrl', e.target.value)}>
                <option value="">— No audio —</option>
                {rssEpisodes.map(ep => (
                  <option key={ep.audioUrl} value={ep.audioUrl}>{ep.title}</option>
                ))}
                {section.audioEmbedUrl && !rssEpisodes.some(ep => ep.audioUrl === section.audioEmbedUrl) && (
                  <option value={section.audioEmbedUrl}>{section.audioEmbedUrl}</option>
                )}
              </select>
            </div>
            <div className="admin-field">
              <label htmlFor={`study-section-released-${study.id}-${section.id}`}>Release Date (optional)</label>
              <input id={`study-section-released-${study.id}-${section.id}`} type="datetime-local" value={section.releasedAt ? new Date(section.releasedAt).toISOString().slice(0, 16) : ''} onChange={e => {
                const val = e.target.value;
                if (val) {
                  const date = new Date(val + ':00Z');
                  updateStudySection(study.id, section.id, 'releasedAt', date.toISOString());
                } else {
                  updateStudySection(study.id, section.id, 'releasedAt', '');
                }
              }} />
            </div>
            <div className="admin-field">
              <label htmlFor={`study-section-summary-${study.id}-${section.id}`}>Short Summary</label>
              <textarea id={`study-section-summary-${study.id}-${section.id}`} rows={3} value={section.summary} placeholder="One to two sentences that summarize the section." onChange={e => updateStudySection(study.id, section.id, 'summary', e.target.value)} />
            </div>
            <div className="admin-field">
              <label htmlFor={`study-section-announcement-${study.id}-${section.id}`}>Lesson Announcement</label>
              <textarea id={`study-section-announcement-${study.id}-${section.id}`} rows={3} value={section.announcement ?? ''} placeholder="A short instructor announcement for this lesson." onChange={e => updateStudySection(study.id, section.id, 'announcement', e.target.value)} />
            </div>
            <div className="admin-field">
              <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', flexWrap: 'wrap', marginBottom: '0.35rem' }}>
                <label htmlFor={`study-section-passage-${study.id}-${section.id}`} style={{ margin: 0 }}>Passage Text</label>
                <button type="button" className="btn-admin-secondary" disabled={bsbFetching} onClick={fetchBsbPassage} style={{ fontSize: '0.8rem', padding: '0.2rem 0.75rem' }}>
                  {bsbFetching ? 'Fetching…' : 'Fetch from BSB'}
                </button>
                {bsbMsg && <span style={{ fontSize: '0.8rem', color: bsbMsg.includes('filled') ? '#7abf7a' : '#c97070' }}>{bsbMsg}</span>}
              </div>
              <textarea id={`study-section-passage-${study.id}-${section.id}`} rows={4} value={section.passageText} placeholder="Paste the passage text here, or use Fetch from BSB." onChange={e => updateStudySection(study.id, section.id, 'passageText', e.target.value)} />
            </div>
            <div className="admin-field">
              <label htmlFor={`study-section-commentary-${study.id}-${section.id}`}>Commentary</label>
              <textarea id={`study-section-commentary-${study.id}-${section.id}`} rows={10} value={section.commentary} placeholder="Add your teaching notes and explanation here. Press Enter twice to start a new paragraph." onChange={e => updateStudySection(study.id, section.id, 'commentary', e.target.value)} />
            </div>
            <div className="admin-field">
              <label htmlFor={`study-section-greek-${study.id}-${section.id}`}>Greek Words</label>
              <textarea id={`study-section-greek-${study.id}-${section.id}`} rows={4} value={(section.greekNotes ?? []).join('\n')} placeholder="One note per line" onChange={e => updateStudySection(study.id, section.id, 'greekNotes', e.target.value.split('\n').map(line => line.trim()).filter(Boolean))} />
            </div>
            <div className="admin-field">
              <label htmlFor={`study-section-focus-${study.id}-${section.id}`}>Focus Question</label>
              <input id={`study-section-focus-${study.id}-${section.id}`} type="text" value={section.focusQuestion ?? ''} placeholder="Shown on the lesson card (defaults to first study question)" onChange={e => updateStudySection(study.id, section.id, 'focusQuestion', e.target.value)} />
            </div>
            <div className="admin-field">
              <label htmlFor={`study-section-questions-${study.id}-${section.id}`}>Study Questions</label>
              <textarea id={`study-section-questions-${study.id}-${section.id}`} rows={5} value={(section.studyQuestions ?? []).join('\n')} placeholder="One question per line" onChange={e => updateStudySection(study.id, section.id, 'studyQuestions', e.target.value.split('\n').map(line => line.trim()).filter(Boolean))} />
            </div>
            <div className="admin-field">
              <label htmlFor={`study-section-checkpoint-prompt-${study.id}-${section.id}`}>Checkpoint Prompt</label>
              <input id={`study-section-checkpoint-prompt-${study.id}-${section.id}`} type="text" value={section.checkpointPrompt ?? ''} placeholder="A short prompt for the checkpoint" onChange={e => updateStudySection(study.id, section.id, 'checkpointPrompt', e.target.value)} />
            </div>
            <div className="admin-field">
              <label htmlFor={`study-section-checkpoint-questions-${study.id}-${section.id}`}>Checkpoint Questions</label>
              <textarea id={`study-section-checkpoint-questions-${study.id}-${section.id}`} rows={5} value={(section.checkpointQuestions ?? []).join('\n')} placeholder="One checkpoint question per line" onChange={e => updateStudySection(study.id, section.id, 'checkpointQuestions', e.target.value.split('\n').map(line => line.trim()).filter(Boolean))} />
              <p className="admin-field-help">Optional reflection questions that learners answer before completing the lesson.</p>
            </div>
          </div>
          <button type="button" className="btn-admin-remove" onClick={() => removeStudySection(study.id, section.id)}>Remove Lesson</button>
        </div>
      </div>
    </details>
  )
}

interface StudyUserRecord {
  id: string
  username: string
  displayName: string
  createdAt: string | null
  lastLoginAt: string | null
  lastStudiedDate: string | null
  currentStreak: number
  longestStreak: number
  enrolledStudies: Array<{ slug: string; title: string; completedLessons?: number; totalLessons?: number }>
  noteCount: number
  subscribeNewsletter: boolean
  studyRemindersEnabled: boolean
}

function StudyUsersPanel({ studies }: { studies: StudyProgram[] }) {
  const [users, setUsers] = useState<StudyUserRecord[]>([])
  const [loading, setLoading] = useState(true)
  const [search, setSearch] = useState('')
  const [expandedId, setExpandedId] = useState<string | null>(null)
  const [editDisplayName, setEditDisplayName] = useState<Record<string, string>>({})
  const [newPassword, setNewPassword] = useState<Record<string, string>>({})
  const [msg, setMsg] = useState<Record<string, string>>({})
  const [confirmDeleteId, setConfirmDeleteId] = useState<string | null>(null)

  function flashMsg(id: string, text: string) {
    setMsg(prev => ({ ...prev, [id]: text }))
    setTimeout(() => setMsg(prev => { const n = { ...prev }; delete n[id]; return n }), 4000)
  }

  useEffect(() => {
    fetch('/api/admin/study-users')
      .then(r => r.json())
      .then((d: { users: StudyUserRecord[] }) => { setUsers(d.users ?? []); setLoading(false) })
      .catch(() => setLoading(false))
  }, [])

  async function patchUser(id: string, body: Record<string, string>) {
    const res = await fetch(`/api/admin/study-users/${id}`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(body),
    })
    const data = await res.json()
    if (!res.ok) throw new Error(data.message ?? 'Error')
    return data
  }

  async function handleSaveDisplayName(user: StudyUserRecord) {
    try {
      const name = editDisplayName[user.id] ?? user.displayName
      await patchUser(user.id, { displayName: name })
      setUsers(prev => prev.map(u => u.id === user.id ? { ...u, displayName: name } : u))
      flashMsg(user.id, 'Display name updated.')
    } catch (e) { flashMsg(user.id, e instanceof Error ? e.message : 'Error') }
  }

  async function handleResetPassword(user: StudyUserRecord) {
    const pw = newPassword[user.id] ?? ''
    if (pw.length < 8) { flashMsg(user.id, 'Password must be at least 8 characters.'); return }
    try {
      await patchUser(user.id, { newPassword: pw })
      setNewPassword(prev => { const n = { ...prev }; delete n[user.id]; return n })
      flashMsg(user.id, 'Password reset. Their active sessions have been signed out.')
    } catch (e) { flashMsg(user.id, e instanceof Error ? e.message : 'Error') }
  }

  async function handleEnroll(user: StudyUserRecord, slug: string) {
    try {
      const data = await patchUser(user.id, { addEnrollment: slug })
      setUsers(prev => prev.map(u => {
        if (u.id !== user.id) return u
        const study = studies.find(s => s.slug === slug)
        const already = u.enrolledStudies.find(e => e.slug === slug)
        return { ...u, enrolledStudies: already ? u.enrolledStudies : [...u.enrolledStudies, { slug, title: study?.title ?? slug }] }
      }))
      flashMsg(user.id, `Enrolled in ${slug}.`)
      return data
    } catch (e) { flashMsg(user.id, e instanceof Error ? e.message : 'Error') }
  }

  async function handleUnenroll(user: StudyUserRecord, slug: string) {
    try {
      await patchUser(user.id, { removeEnrollment: slug })
      setUsers(prev => prev.map(u => u.id === user.id ? { ...u, enrolledStudies: u.enrolledStudies.filter(e => e.slug !== slug) } : u))
      flashMsg(user.id, `Removed from ${slug}.`)
    } catch (e) { flashMsg(user.id, e instanceof Error ? e.message : 'Error') }
  }

  async function handleDelete(id: string) {
    try {
      await fetch(`/api/admin/study-users/${id}`, { method: 'DELETE' })
      setUsers(prev => prev.filter(u => u.id !== id))
      setConfirmDeleteId(null)
      setExpandedId(null)
    } catch { /* ignore */ }
  }

  const filtered = users.filter(u =>
    u.username.toLowerCase().includes(search.toLowerCase()) ||
    u.displayName.toLowerCase().includes(search.toLowerCase())
  )

  if (loading) return <p className="admin-stats-note">Loading study users…</p>

  return (
    <section className="admin-panel-section" aria-label="Study Users">
      <div className="admin-panel-head">
        <h2>Study Users</h2>
        <p>Manage student accounts — reset passwords, update display names, adjust enrollments, or delete accounts.</p>
      </div>

      <div className="admin-field" style={{ maxWidth: '360px', marginBottom: '1.25rem' }}>
        <input
          type="search"
          placeholder="Search by email or display name…"
          value={search}
          onChange={e => setSearch(e.target.value)}
        />
      </div>

      {filtered.length === 0 && <p className="admin-stats-note">{users.length === 0 ? 'No study users yet.' : 'No users match your search.'}</p>}

      <div className="admin-study-users-list">
        {filtered.map(user => {
          const isExpanded = expandedId === user.id
          const unenrolledStudies = studies.filter(s => !user.enrolledStudies.find(e => e.slug === s.slug))
          return (
            <div key={user.id} className={`admin-study-user-card${isExpanded ? ' admin-study-user-card--open' : ''}`}>
              <button
                type="button"
                className="admin-study-user-header"
                onClick={() => {
                  setExpandedId(isExpanded ? null : user.id)
                  setEditDisplayName(prev => ({ ...prev, [user.id]: user.displayName }))
                }}
              >
                <div className="admin-study-user-header-left">
                  <strong>{user.displayName || user.username}</strong>
                  {user.displayName && <span className="admin-study-user-email">{user.username}</span>}
                </div>
                <div className="admin-study-user-header-right">
                  <span className="admin-study-user-pill">{user.enrolledStudies.length} enrolled</span>
                  <span className="admin-study-user-pill">{user.noteCount} notes</span>
                  {(user.currentStreak ?? 0) > 0 && <span className="admin-study-user-pill">{user.currentStreak} 🔥</span>}
                  <span style={{ color: '#5a5440', fontSize: '0.8rem' }}>{isExpanded ? '▲' : '▼'}</span>
                </div>
              </button>

              {isExpanded && (
                <div className="admin-study-user-body">
                  <div className="admin-study-user-meta">
                    <span>Joined: {user.createdAt ? new Date(user.createdAt).toLocaleDateString() : '—'}</span>
                    <span>Last login: {user.lastLoginAt ? new Date(user.lastLoginAt).toLocaleDateString() : '—'}</span>
                    <span>Last studied: {user.lastStudiedDate ?? '—'}</span>
                    <span>Streak: {user.currentStreak ?? 0} 🔥 (best: {user.longestStreak ?? 0})</span>
                    <span>Newsletter: {user.subscribeNewsletter ? 'Yes' : 'No'}</span>
                    <span>Reminders: {user.studyRemindersEnabled ? 'On' : 'Off'}</span>
                  </div>

                  {/* Display Name */}
                  <div className="admin-study-user-section">
                    <h4>Display Name</h4>
                    <div className="admin-study-user-row">
                      <input
                        type="text"
                        value={editDisplayName[user.id] ?? user.displayName}
                        onChange={e => setEditDisplayName(prev => ({ ...prev, [user.id]: e.target.value }))}
                        placeholder="Display name"
                        maxLength={80}
                      />
                      <button type="button" className="btn-admin-save" onClick={() => handleSaveDisplayName(user)}>Save</button>
                    </div>
                  </div>

                  {/* Password Reset */}
                  <div className="admin-study-user-section">
                    <h4>Reset Password</h4>
                    <div className="admin-study-user-row">
                      <input
                        type="text"
                        value={newPassword[user.id] ?? ''}
                        onChange={e => setNewPassword(prev => ({ ...prev, [user.id]: e.target.value }))}
                        placeholder="New password (min 8 chars)"
                        autoComplete="off"
                      />
                      <button type="button" className="btn-admin-save" onClick={() => handleResetPassword(user)}>Reset</button>
                    </div>
                    <p className="admin-stats-note" style={{ marginTop: '0.25rem' }}>This immediately signs them out of all active sessions.</p>
                  </div>

                  {/* Enrollments */}
                  <div className="admin-study-user-section">
                    <h4>Enrollments</h4>
                    {user.enrolledStudies.length === 0
                      ? <p className="admin-stats-note">Not enrolled in any studies.</p>
                      : (
                        <ul className="admin-study-user-enrollments">
                          {user.enrolledStudies.map(e => (
                            <li key={e.slug}>
                              <span>
                                {e.title}
                                {e.totalLessons != null && (
                                  <span style={{ color: '#8a7a3a', fontSize: '0.8em', marginLeft: '0.5rem' }}>
                                    {e.completedLessons ?? 0}/{e.totalLessons} lessons
                                  </span>
                                )}
                              </span>
                              <button type="button" className="btn-admin-remove" style={{ fontSize: '0.78rem', padding: '0.2rem 0.6rem' }} onClick={() => handleUnenroll(user, e.slug)}>Remove</button>
                            </li>
                          ))}
                        </ul>
                      )
                    }
                    {unenrolledStudies.length > 0 && (
                      <div className="admin-study-user-row" style={{ marginTop: '0.5rem' }}>
                        <select
                          defaultValue=""
                          onChange={e => { if (e.target.value) { handleEnroll(user, e.target.value); e.target.value = '' } }}
                        >
                          <option value="">Enroll in a study…</option>
                          {unenrolledStudies.map(s => <option key={s.slug} value={s.slug}>{s.title}</option>)}
                        </select>
                      </div>
                    )}
                  </div>

                  {msg[user.id] && <p className="admin-study-user-msg">{msg[user.id]}</p>}

                  {/* Delete */}
                  <div className="admin-study-user-section" style={{ borderTop: '1px solid rgba(201,168,76,0.1)', paddingTop: '0.75rem', marginTop: '0.5rem' }}>
                    {confirmDeleteId === user.id ? (
                      <div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
                        <span style={{ fontSize: '0.85rem', color: '#e05c5c' }}>Delete this account permanently?</span>
                        <button type="button" className="btn-admin-remove" onClick={() => handleDelete(user.id)}>Yes, Delete</button>
                        <button type="button" className="btn-admin-reset" onClick={() => setConfirmDeleteId(null)}>Cancel</button>
                      </div>
                    ) : (
                      <button type="button" className="btn-admin-remove" style={{ fontSize: '0.82rem' }} onClick={() => setConfirmDeleteId(user.id)}>Delete Account</button>
                    )}
                  </div>
                </div>
              )}
            </div>
          )
        })}
      </div>
    </section>
  )
}

function EmailTemplatesPanel({
  form,
  handleChange,
  renderSaveStatus,
}: {
  form: SiteContent
  handleChange: (key: StringField, value: string) => void
  renderSaveStatus: () => React.ReactNode
}) {
  const [expandedEmail, setExpandedEmail] = useState<string | null>(null)

  function EmailTemplateCard({
    type, icon, title, trigger, autoVars, fieldKeys, openKey,
  }: {
    type: 'newsletter' | 'study' | 'transactional'
    icon: string
    title: string
    trigger: string
    autoVars?: string
    fieldKeys: string[]
    openKey: string
  }) {
    const isOpen = expandedEmail === openKey
    return (
      <div className={`admin-et-card admin-et-card--${type}${isOpen ? ' admin-et-card--open' : ''}`}>
        <button
          type="button"
          className="admin-et-card-header"
          onClick={() => setExpandedEmail(isOpen ? null : openKey)}
        >
          <span className="admin-et-card-icon" aria-hidden="true">{icon}</span>
          <div className="admin-et-card-meta">
            <div className="admin-et-card-title-row">
              <span className={`admin-et-badge admin-et-badge--${type}`}>{type}</span>
              <strong className="admin-et-card-title">{title}</strong>
            </div>
            <p className="admin-et-card-trigger">⚡ {trigger}</p>
          </div>
          <span className="admin-et-card-chevron">{isOpen ? '▲' : '▼'}</span>
        </button>
        {isOpen && (
          <div className="admin-et-card-body">
            {autoVars && (
              <p className="admin-et-card-vars">
                <span className="admin-et-card-vars-label">Auto-inserted:</span> {autoVars}
              </p>
            )}
            {fieldKeys.map(key => {
              const field = FIELDS.find(f => f.key === key && f.section === 'email-templates')
              if (!field) return null
              return (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-et-${key}`}>{field.label}</label>
                  {field.multiline
                    ? <textarea id={`field-et-${key}`} rows={3} value={form[key as StringField] as string} onChange={e => handleChange(key as StringField, e.target.value)} />
                    : <input id={`field-et-${key}`} type="text" value={form[key as StringField] as string} onChange={e => handleChange(key as StringField, e.target.value)} />
                  }
                </div>
              )
            })}
          </div>
        )}
      </div>
    )
  }

  return (
    <section className="admin-panel-section" aria-label="Email Templates">
      <div className="admin-panel-head">
        <h2>Email Templates</h2>
        <p>Every automated email the site sends — edit subject lines, body copy, and sign-offs. Publish to apply changes.</p>
      </div>

      <EmailTemplateCard type="newsletter" icon="📬" title="Newsletter Welcome"
        trigger="Someone subscribes via the homepage form or contact page"
        autoVars="Subscriber's first name, Spotify / Apple / Amazon links"
        fieldKeys={['welcomeEmailSubject','welcomeEmailGreetingPrefix','welcomeEmailIntro','welcomeEmailCurrentSeries','welcomeEmailStartHereTitle','welcomeEmailStartHereSummary','welcomeEmailStartHereUrl','welcomeEmailStartHereLinkLabel','welcomeEmailWhatToExpect1','welcomeEmailWhatToExpect2','welcomeEmailWhatToExpect3','welcomeEmailScripture','welcomeEmailScriptureRef','welcomeEmailSignoff','welcomeEmailSpotifyUrl','welcomeEmailSpotifyBtnLabel','welcomeEmailAppleUrl','welcomeEmailAppleBtnLabel','welcomeEmailAmazonUrl']}
        openKey="newsletter-welcome" />

      <EmailTemplateCard type="study" icon="📖" title="Study Account — New Account"
        trigger="A student creates a study account"
        autoVars="Student's display name, link to study hub, link to account page"
        fieldKeys={['studyWelcomeEmailSubject','studyWelcomeEmailBody','studyWelcomeEmailCtaLabel','studyWelcomeEmailCtaPath','studyWelcomeEmailSignoff']}
        openKey="study-welcome" />

      <EmailTemplateCard type="study" icon="🗑️" title="Study Account — Account Deleted"
        trigger="A student (or admin) deletes a study account"
        autoVars="Student's display name, link to create a new account"
        fieldKeys={['studyDeletedEmailSubject','studyDeletedEmailBody','studyDeletedEmailCtaLabel','studyDeletedEmailCtaPath','studyDeletedEmailSignoff']}
        openKey="study-deleted" />

      <EmailTemplateCard type="study" icon="🔔" title="Study Account — New Lesson Unlocked"
        trigger="A lesson releases for a student who has reminders turned on"
        autoVars="Student's name, lesson title, scripture reference, study track name — button links directly to the lesson (auto-generated, not editable)"
        fieldKeys={['studyReminderEmailSubjectPrefix','studyReminderEmailBody','studyReminderEmailCtaLabel','studyReminderEmailSignoff']}
        openKey="study-reminder" />

      <EmailTemplateCard type="study" icon="💤" title="Study Account — Re-engagement Reminders"
        trigger="A student with reminders enabled hasn't logged in for the configured number of days (fires up to 3 tiers, one per check)"
        autoVars="Student's name, study title, study URL, and a one-click unsubscribe link are auto-inserted"
        fieldKeys={['studyReengagementDays','studyReengagementT1Subject','studyReengagementT1Body','studyReengagementT1Cta','studyReengagementT2Subject','studyReengagementT2Body','studyReengagementT2Cta','studyReengagementT3Subject','studyReengagementT3Body','studyReengagementT3Cta']}
        openKey="study-reengagement" />

      <EmailTemplateCard type="transactional" icon="🔐" title="Account Security — Email Change Confirmation"
        trigger="A student requests to change their account email address"
        autoVars="Button links to a one-time verification URL (auto-generated per request, not editable)"
        fieldKeys={['emailChangeSubject','emailChangeBody','emailChangeCtaLabel']}
        openKey="email-change" />

      <EmailTemplateCard type="transactional" icon="📟" title="Two-Factor Auth — Email Sign-In Code"
        trigger="A student with email-based 2FA signs in, or requests a resend during login"
        autoVars="The 6-digit code is auto-generated and inserted between the body text and expiry note — not editable"
        fieldKeys={['twoFaOtpEmailSubject','twoFaOtpEmailBody','twoFaOtpEmailExpiry']}
        openKey="2fa-otp" />

      <EmailTemplateCard type="transactional" icon="💬" title="Q&amp;A — Question Answered Notification"
        trigger={'An admin approves a Q&A question submitted with "Email me when answered" checked'}
        autoVars={'Submitter\'s first name, their original question text, the answer (truncated to 600 chars), a direct link to the answer'}
        fieldKeys={['qaAnsweredEmailSubject','qaAnsweredEmailBody','qaAnsweredEmailCtaLabel','qaAnsweredEmailSignoff']}
        openKey="qa-answered" />

      {renderSaveStatus()}
    </section>
  )
}

interface Props {
  content: SiteContent
  onSave: (c: SiteContent) => void
  onLogout: () => void | Promise<void>
}

interface BackupPreview {
  filename: string
  sizeBytes: number
  createdAt: string | null
  reason: string
  adminUpdatedAt: string | null
  totalHits: number
  totalVisits: number
}

export interface AdminStats {
  totalHits: number
  realHits: number
  botHits: number
  firstHitAt: string | null
  lastHitAt: string | null
  topPaths: Array<{ path: string; hits: number }>
  topPathsReal: Array<{ path: string; hits: number }>
  topPathsBot: Array<{ path: string; hits: number }>
  last7Days: Array<{ day: string; hits: number }>
  last7DaysReal: Array<{ day: string; hits: number }>
  last7DaysBot: Array<{ day: string; hits: number }>
  last30DaysTotal: number
  last30DaysRealTotal: number
  last30DaysBotTotal: number
  botReasons: Array<{ reason: string; count: number }>
  visitors: {
    totalVisits: number
    uniqueVisitors: number
    returningVisits: number
    firstVisitAt: string | null
    lastVisitAt: string | null
    topCountries: Array<{ name: string; hits: number }>
    topStates: Array<{ name: string; hits: number }>
    topCounties: Array<{ name: string; hits: number }>
    topCities: Array<{ name: string; hits: number }>
    deviceBreakdown: { mobile: number; desktop: number; tablet: number; unknown: number }
    topReferrers: Array<{ referrer: string; count: number }>
    last30DaysReal: Array<{ day: string; hits: number }>
    recentVisits: Array<{
      at: string
      visitorId: string
      ip: string
      path: string
      referrer?: string
      device?: string
      country: string
      state: string
      county: string
      city: string
      returningVisitor: boolean
      visitCount: number
      pageHistory?: Array<{ at: string; path: string; referrer?: string }>
      studyUser?: { userId: string; username: string; displayName: string }
    }>
  }
  writeStatus: {
    hitStats: { ok: boolean; at: string | null; error: string | null }
    visitorStats: { ok: boolean; at: string | null; error: string | null }
    backups: { ok: boolean; at: string | null; error: string | null; file: string | null }
  }
  contactTotals: {
    totalSubmissions: number
    totalQuestions: number
  }
  studyEnrollment: {
    totalUsers: number
    enrolledUsers: number
    totalEnrollments: number
    enrollmentsByStudy: Array<{ slug: string; title: string; count: number }>
    users: Array<{
      id: string
      username: string
      displayName: string
      enrolledStudies: Array<{ slug: string; title: string }>
    }>
    funnel?: { signups: number; firstVisit: number; firstCompletion: number }
  }
  episodePlays?: Array<{
    title: string
    total: number
    byDay: Record<string, number>
    last30Days: Array<{ day: string; plays: number }>
  }>
  engagement?: {
    scrollDepth: Array<{ path: string; 25?: number; 50?: number; 75?: number; 90?: number }>
    timeOnPage: Array<{ path: string; avgSeconds: number; count: number }>
    topOutboundClicks: Array<{ url: string; count: number }>
    topLinkClicks: Array<{ url: string; count: number; internal: boolean }>
    topUTMSources: Array<{ source: string; count: number }>
    topSearchQueries: Array<{ query: string; count: number }>
    top404s: Array<{ path: string; count: number }>
    audioEvents: Array<{ title: string; pauses: number; completions: number; totalListenSeconds: number }>
  }
}

interface AdminAsset {
  filename: string
  url: string
  sizeBytes: number
  updatedAt: string
  tags?: string[]
}

interface PublishState {
  draftUpdatedAt: string | null
  publishedAt: string | null
}

interface OpsStatus {
  buildCommit: string | null
  buildNumber: string | null
  deployedAt: string | null
  cachePurge: { ok: boolean; at: string | null; error: string | null }
  deployHook: { ok: boolean; at: string | null; error: string | null }
}

interface Question {
  id: string
  submittedAt: string
  firstName: string
  email: string
  question: string
  answer: string
  answeredAt: string | null
  isApproved: boolean
  approvedAt: string | null
  upvotes?: number
  pinned?: boolean
}

interface ContactSubmission {
  id: string
  submittedAt: string
  name: string
  email: string
  message: string
  messageType: 'question' | 'testimony' | 'topic' | 'general'
  subscribe: boolean
  archived?: boolean
  source?: 'contact-form' | 'download' | 'inbound-email'
  inboundTo?: string
  htmlBody?: string | null
}

interface Subscriber {
  name: string
  email: string
  subscribedAt: string
  source: 'contact-form' | 'download'
}


type StringField = Exclude<keyof SiteContent, 'customLinks' | 'customBlocks' | 'redirects' | 'podcastFeaturedLinks' | 'finishedBooks' | 'testimonials' | 'seo' | 'legal' | 'whereToNextCards' | 'colossiansStudySections' | 'studies'>

type AdminView =
  | 'dashboard' | 'homepage' | 'start-here' | 'about' | 'contact'
  | 'podcast' | 'current-series' | 'episode-highlights'
  | 'downloads' | 'custom-links' | 'content-blocks' | 'books' | 'recommended-resources'
  | 'questions' | 'study-comments' | 'analytics' | 'assets' | 'colossians-study' | 'qr-codes'
  | 'subscribers' | 'study-users' | 'email-templates'
  | 'seo' | 'legal' | 'security' | 'brand' | 'global'

interface AdminSectionLink {
  id: string
  label: string
}

const ADMIN_VIEW_OPTIONS: Array<{ group: string; options: Array<{ value: AdminView; label: string }> }> = [
  {
    group: 'Overview',
    options: [{ value: 'dashboard', label: 'Dashboard' }],
  },
  {
    group: 'Site',
    options: [
      { value: 'homepage', label: 'Homepage' },
      { value: 'start-here', label: 'Start Here' },
      { value: 'about', label: 'About' },
      { value: 'contact', label: 'Contact' },
    ],
  },
  {
    group: 'Content',
    options: [
      { value: 'podcast', label: 'Podcast Hub' },
      { value: 'colossians-study', label: 'Studies' },
      { value: 'downloads', label: 'Downloads' },
      { value: 'custom-links', label: 'Custom Links' },
      { value: 'content-blocks', label: 'Content Blocks' },
      { value: 'books', label: 'Books' },
      { value: 'recommended-resources', label: 'Recommended Resources' },
    ],
  },
  {
    group: 'Audience',
    options: [
      { value: 'questions', label: 'Questions' },
      { value: 'study-comments', label: 'Study Comments' },
      { value: 'subscribers', label: 'Subscribers' },
      { value: 'study-users', label: 'Study Users' },
    ],
  },
  {
    group: 'Insights',
    options: [
      { value: 'analytics', label: 'Analytics' },
    ],
  },
  {
    group: 'Tools',
    options: [
      { value: 'assets', label: 'Asset Manager' },
      { value: 'qr-codes', label: 'QR Codes' },
    ],
  },
  {
    group: 'Configure',
    options: [
      { value: 'global', label: 'Footer & Global' },
      { value: 'seo', label: 'SEO & Redirects' },
      { value: 'email-templates', label: 'Email Templates' },
      { value: 'legal', label: 'Legal Pages' },
      { value: 'security', label: 'Security' },
      { value: 'brand', label: 'Brand Kit' },
    ],
  },
]

const ADMIN_SECTION_LINKS: Partial<Record<AdminView, AdminSectionLink[]>> = {
  homepage: [
    { id: 'homepage-hero', label: 'Hero' },
    { id: 'homepage-share', label: 'Share Section' },
    { id: 'homepage-prism', label: 'PRISM Block' },
    { id: 'homepage-where-to-next-labels', label: 'Where to Next Labels' },
    { id: 'homepage-where-to-next-cards', label: 'Where to Next Cards' },
  ],
  'start-here': [
    { id: 'start-here-intro', label: 'Intro' },
    { id: 'start-here-step-1', label: 'Step 1' },
    { id: 'start-here-step-2', label: 'Step 2' },
    { id: 'start-here-step-3', label: 'Step 3' },
  ],
  about: [
    { id: 'about-show-copy', label: 'Show Copy' },
    { id: 'about-nate-bio', label: 'Nate Bio' },
    { id: 'about-images', label: 'Images' },
    { id: 'about-testimonials', label: 'Testimonials' },
  ],
  contact: [
    { id: 'contact-profile', label: 'Profile' },
    { id: 'contact-intro', label: 'Intro Copy' },
    { id: 'contact-scripture', label: 'Scripture' },
  ],
  downloads: [
    { id: 'downloads-study-guide', label: 'Study Guide' },
    { id: 'downloads-library', label: 'Download Library' },
    { id: 'downloads-previous-studies', label: 'Previous Studies' },
  ],
  global: [
    { id: 'global-footer', label: 'Footer' },
    { id: 'global-header', label: 'Header' },
    { id: 'global-platforms', label: 'Platform URLs' },
    { id: 'global-banner', label: 'Analytics Banner' },
    { id: 'global-welcome-email', label: 'Welcome Email' },
  ],
  seo: [
    { id: 'seo-metadata', label: 'Metadata' },
    { id: 'seo-redirects', label: 'Redirects' },
  ],
  legal: [
    { id: 'legal-privacy', label: 'Privacy' },
    { id: 'legal-terms', label: 'Terms' },
  ],
}

type PodcastTab = 'current-series' | 'episode-highlights' | 'finished-books' | 'episode-scripts' | 'rss-feed'

type MainContentSection = 'hero' | 'start-here' | 'about' | 'contact' | 'series' | 'share' | 'prism' | 'global' | 'email-templates'

const BRAND_KIT_SWATCHES = [
  { name: 'Rich Black', hex: '#0a0a08', role: 'Primary background' },
  { name: 'Soft Black', hex: '#0f0f0c', role: 'Secondary surfaces' },
  { name: 'Panel Black', hex: '#1a1a15', role: 'Cards and panels' },
  { name: 'Deep Brown', hex: '#2a2518', role: 'Borders and dividers' },
  { name: 'Antique Gold', hex: '#c9a84c', role: 'Primary accent' },
  { name: 'Light Gold', hex: '#e0c070', role: 'Hover and highlight' },
  { name: 'Warm White', hex: '#f0ead8', role: 'Primary text' },
  { name: 'Warm Gray', hex: '#7a7060', role: 'Secondary text' },
] as const

const BRAND_KIT_TYPE = [
  {
    label: 'Display / Heading Font',
    spec: 'Cormorant Garamond · 300/400/600/700 · italic supported · for H1, H2, display text',
    sample: 'Verse by Verse with Nate',
  },
  {
    label: 'Body Font',
    spec: 'Lora · 400/500/600 · italic supported · for paragraphs, labels, nav, and UI text',
    sample: 'Verse by verse. Nugget by nugget.',
  },
  {
    label: 'Subtitle / Small Caps',
    spec: 'Lora uppercase with tracking for eyebrows, labels, and support text',
    sample: 'A Journey Through Scripture',
  },
] as const

const BRAND_KIT_VERIFICATION = [
  'Headings use Cormorant Garamond (300–700, italic) for display elegance.',
  'Body text uses Lora (400–600, italic) for screen readability.',
  'Shared brand variables define black, gold, border, warm white, and muted text colors.',
  'Live site CSS references brand variables for core text and accent styling.',
  'Admin surfaces inherit the same typography and palette tokens used by the public site.',
] as const

const FIELDS: Array<{ key: StringField; label: string; multiline?: boolean; section: MainContentSection }> = [
  { key: 'podcastArtUrl', label: 'Hero — Podcast Art Image URL', section: 'hero' },
  { key: 'eyebrow', label: 'Hero Eyebrow Text', section: 'hero' },
  { key: 'heroTagline', label: 'Hero Tagline', section: 'hero' },
  { key: 'heroBtnSpotify', label: 'Hero — Spotify Button Label', section: 'hero' },
  { key: 'heroBtnEpisodes', label: 'Hero — Episodes Button Label', section: 'hero' },
  { key: 'heroBtnStartHere', label: 'Hero — Start Here Button Label', section: 'hero' },
  { key: 'startHereHeading', label: 'Start Here — Heading', section: 'start-here' },
  { key: 'startHereIntro', label: 'Start Here — Intro', multiline: true, section: 'start-here' },
  { key: 'startHereStep1Title', label: 'Start Here — Step 1 Title', section: 'start-here' },
  { key: 'startHereStep1Body', label: 'Start Here — Step 1 Body', multiline: true, section: 'start-here' },
  { key: 'startHereStep1Cta', label: 'Start Here — Step 1 Button Text', section: 'start-here' },
  { key: 'startHereStep2Title', label: 'Start Here — Step 2 Title', section: 'start-here' },
  { key: 'startHereStep2Body', label: 'Start Here — Step 2 Body', multiline: true, section: 'start-here' },
  { key: 'startHereStep2Cta', label: 'Start Here — Step 2 Button Text', section: 'start-here' },
  { key: 'startHereStep3Title', label: 'Start Here — Step 3 Title', section: 'start-here' },
  { key: 'startHereStep3Body', label: 'Start Here — Step 3 Body', multiline: true, section: 'start-here' },
  { key: 'startHereStep3Cta', label: 'Start Here — Step 3 Button Text', section: 'start-here' },
  { key: 'aboutShowHeading', label: 'About Show — Heading', section: 'about' },
  { key: 'aboutShowP1', label: 'About Show — Paragraph 1', multiline: true, section: 'about' },
  { key: 'aboutShowP2', label: 'About Show — Paragraph 2', multiline: true, section: 'about' },
  { key: 'aboutNate', label: 'About Nate', multiline: true, section: 'about' },
  { key: 'aboutPhotoUrl', label: 'About — Nate Portrait Image URL', section: 'about' },
  { key: 'aboutVerseArtUrl', label: 'About — Scripture Artwork Image URL', section: 'about' },
  { key: 'aboutEyebrow', label: 'About — "About Nate" Eyebrow', section: 'about' },
  { key: 'aboutListenBtnLabel', label: 'About — Listen Button Label', section: 'about' },
  { key: 'aboutShowEyebrow', label: 'About — "About the Show" Eyebrow', section: 'about' },
  { key: 'contactPhotoUrl', label: 'Contact — Profile Photo URL', section: 'contact' },
  { key: 'contactEyebrow', label: 'Contact — Section Eyebrow', section: 'contact' },
  { key: 'contactHeading', label: 'Contact — Heading', section: 'contact' },
  { key: 'contactName', label: 'Contact — Profile Name', section: 'contact' },
  { key: 'contactRole', label: 'Contact — Profile Role', section: 'contact' },
  { key: 'contactQuote', label: 'Contact — Quote', multiline: true, section: 'contact' },
  { key: 'contactIntro', label: 'Contact — Intro Text', multiline: true, section: 'contact' },
  { key: 'contactPoint1', label: 'Contact — Point 1', section: 'contact' },
  { key: 'contactPoint2', label: 'Contact — Point 2', section: 'contact' },
  { key: 'contactVerse', label: 'Contact — Scripture Text', section: 'contact' },
  { key: 'contactVerseRef', label: 'Contact — Scripture Reference', section: 'contact' },
  { key: 'episodesSeoIntro', label: 'Episodes Page — SEO Description (hidden from visitors, indexed by search engines)', multiline: true, section: 'series' },
  { key: 'seriesLabel', label: 'Series Label (e.g. "Now Playing")', section: 'series' },
  { key: 'seriesTitle', label: 'Series Title', section: 'series' },
  { key: 'seriesDescription', label: 'Series Description', multiline: true, section: 'series' },
  { key: 'seriesImageUrl', label: 'Series Cover Image URL', section: 'series' },
  { key: 'seriesListenUrl', label: 'Series Listen URL', section: 'series' },
  { key: 'seriesListenBtnLabel', label: 'Series — Listen Button Label', section: 'series' },
  { key: 'seriesDownloadsBtnLabel', label: 'Series — Downloads Button Label', section: 'series' },
  { key: 'studyGuideTitle', label: 'Study Guide Title', section: 'series' },
  { key: 'studyGuideDescription', label: 'Study Guide Description', multiline: true, section: 'series' },
  { key: 'studyGuideUrl', label: 'Study Guide URL (Amazon link)', section: 'series' },
  { key: 'shareHeading', label: 'Share Section — Heading', section: 'share' },
  { key: 'shareP', label: 'Share Section — Paragraph', multiline: true, section: 'share' },
  { key: 'shareQrImageUrl', label: 'Share Section — QR Code Image', section: 'share' },
  { key: 'prismVideoUrl', label: 'PRISM — Video URL', section: 'prism' },
  { key: 'prismEyebrow', label: 'PRISM — Eyebrow', section: 'prism' },
  { key: 'prismHeading', label: 'PRISM — Heading', section: 'prism' },
  { key: 'prismIntro', label: 'PRISM — Intro Text', multiline: true, section: 'prism' },
  { key: 'prismPatternIntro', label: 'PRISM — Pattern Intro', multiline: true, section: 'prism' },
  { key: 'prismStep1', label: 'PRISM — Step 1', section: 'prism' },
  { key: 'prismStep2', label: 'PRISM — Step 2', section: 'prism' },
  { key: 'prismStep3', label: 'PRISM — Step 3', section: 'prism' },
  { key: 'prismStep4', label: 'PRISM — Step 4', section: 'prism' },
  { key: 'prismStep5', label: 'PRISM — Step 5', section: 'prism' },
  { key: 'prismOutro', label: 'PRISM — Outro Text', multiline: true, section: 'prism' },
  { key: 'prismClosing', label: 'PRISM — Closing Line', multiline: true, section: 'prism' },
  // Global / Footer / Platform
  { key: 'footerTitle', label: 'Footer — Brand Title', section: 'global' },
  { key: 'footerSubtitle', label: 'Footer — Subtitle', section: 'global' },
  { key: 'footerEmail', label: 'Footer — Contact Email', section: 'global' },
  { key: 'footerCopyright', label: 'Footer — Copyright Line', section: 'global' },
  { key: 'footerPrivacyNote', label: 'Footer — Privacy Note', multiline: true, section: 'global' },
  { key: 'headerFollowLabel', label: 'Header — Follow Button Label', section: 'global' },
  { key: 'cookieBannerText', label: 'Analytics Cookie Banner Text', multiline: true, section: 'global' },
  { key: 'platformSpotifyUrl', label: 'Platform — Spotify URL', section: 'global' },
  { key: 'platformAppleUrl', label: 'Platform — Apple Podcasts URL', section: 'global' },
  { key: 'platformYoutubeUrl', label: 'Platform — YouTube URL', section: 'global' },
  { key: 'platformAmazonUrl', label: 'Platform — Amazon Music URL', section: 'global' },
  { key: 'platformFacebookUrl', label: 'Platform — Facebook URL', section: 'global' },
  { key: 'platformCreatorUrl', label: 'Platform — Creator Profile URL', section: 'global' },
  { key: 'welcomeEmailSubject', label: 'Welcome Email — Subject', section: 'global' },
  { key: 'welcomeEmailGreetingPrefix', label: 'Welcome Email — Greeting Prefix', section: 'global' },
  { key: 'welcomeEmailIntro', label: 'Welcome Email — Intro Paragraph', multiline: true, section: 'global' },
  { key: 'welcomeEmailCurrentSeries', label: 'Welcome Email — Current Series Paragraph', multiline: true, section: 'global' },
  { key: 'welcomeEmailStartHereTitle', label: 'Welcome Email — Start Here Title', section: 'global' },
  { key: 'welcomeEmailStartHereSummary', label: 'Welcome Email — Start Here Summary', multiline: true, section: 'global' },
  { key: 'welcomeEmailStartHereUrl', label: 'Welcome Email — Start Here URL', section: 'global' },
  { key: 'welcomeEmailSpotifyUrl', label: 'Welcome Email — Spotify URL', section: 'global' },
  { key: 'welcomeEmailAppleUrl', label: 'Welcome Email — Apple URL', section: 'global' },
  { key: 'welcomeEmailAmazonUrl', label: 'Welcome Email — Amazon URL', section: 'global' },
  { key: 'welcomeEmailWebsiteUrl', label: 'Welcome Email — Website URL', section: 'global' },
  { key: 'welcomeEmailImageUrl', label: 'Welcome Email — Image URL', section: 'global' },
  { key: 'welcomeEmailWhatToExpect1', label: 'Welcome Email — What to Expect 1', multiline: true, section: 'global' },
  { key: 'welcomeEmailWhatToExpect2', label: 'Welcome Email — What to Expect 2', multiline: true, section: 'global' },
  { key: 'welcomeEmailWhatToExpect3', label: 'Welcome Email — What to Expect 3', multiline: true, section: 'global' },
  { key: 'welcomeEmailScripture', label: 'Welcome Email — Scripture Text', multiline: true, section: 'global' },
  { key: 'welcomeEmailScriptureRef', label: 'Welcome Email — Scripture Reference', section: 'global' },
  { key: 'welcomeEmailSignoff', label: 'Signoff', multiline: true, section: 'global' },
  // ── Email Templates section ──
  { key: 'welcomeEmailSubject', label: 'Subject Line', section: 'email-templates' },
  { key: 'welcomeEmailGreetingPrefix', label: 'Greeting Prefix (before subscriber name)', section: 'email-templates' },
  { key: 'welcomeEmailIntro', label: 'Intro Paragraph', multiline: true, section: 'email-templates' },
  { key: 'welcomeEmailCurrentSeries', label: 'Current Series Blurb', multiline: true, section: 'email-templates' },
  { key: 'welcomeEmailStartHereTitle', label: 'Start Here Episode Title', section: 'email-templates' },
  { key: 'welcomeEmailStartHereSummary', label: 'Start Here Episode Summary', multiline: true, section: 'email-templates' },
  { key: 'welcomeEmailStartHereUrl', label: 'Start Here Episode URL', section: 'email-templates' },
  { key: 'welcomeEmailWhatToExpect1', label: 'What to Expect — Line 1', multiline: true, section: 'email-templates' },
  { key: 'welcomeEmailWhatToExpect2', label: 'What to Expect — Line 2', multiline: true, section: 'email-templates' },
  { key: 'welcomeEmailWhatToExpect3', label: 'What to Expect — Line 3', multiline: true, section: 'email-templates' },
  { key: 'welcomeEmailScripture', label: 'Closing Scripture Text', multiline: true, section: 'email-templates' },
  { key: 'welcomeEmailScriptureRef', label: 'Closing Scripture Reference', section: 'email-templates' },
  { key: 'welcomeEmailSignoff', label: 'Signoff', multiline: true, section: 'email-templates' },
  { key: 'welcomeEmailSpotifyUrl', label: 'Spotify URL', section: 'email-templates' },
  { key: 'welcomeEmailSpotifyBtnLabel', label: '"Listen on Spotify" button label', section: 'email-templates' },
  { key: 'welcomeEmailAppleUrl', label: 'Apple Podcasts URL', section: 'email-templates' },
  { key: 'welcomeEmailAppleBtnLabel', label: '"Apple Podcasts" button label', section: 'email-templates' },
  { key: 'welcomeEmailAmazonUrl', label: 'Amazon Music URL', section: 'email-templates' },
  { key: 'welcomeEmailStartHereLinkLabel', label: '"Start Here" link label', section: 'email-templates' },
  { key: 'studyWelcomeEmailSubject', label: 'Subject Line', section: 'email-templates' },
  { key: 'studyWelcomeEmailBody', label: 'Body Paragraph', multiline: true, section: 'email-templates' },
  { key: 'studyWelcomeEmailCtaLabel', label: 'Button Label', section: 'email-templates' },
  { key: 'studyWelcomeEmailCtaPath', label: 'Button Link (path or full URL)', section: 'email-templates' },
  { key: 'studyWelcomeEmailSignoff', label: 'Signoff', multiline: true, section: 'email-templates' },
  { key: 'studyDeletedEmailSubject', label: 'Subject Line', section: 'email-templates' },
  { key: 'studyDeletedEmailBody', label: 'Body Paragraph', multiline: true, section: 'email-templates' },
  { key: 'studyDeletedEmailCtaLabel', label: 'Button Label', section: 'email-templates' },
  { key: 'studyDeletedEmailCtaPath', label: 'Button Link (path or full URL)', section: 'email-templates' },
  { key: 'studyDeletedEmailSignoff', label: 'Signoff', multiline: true, section: 'email-templates' },
  { key: 'studyReminderEmailSubjectPrefix', label: 'Subject Prefix (lesson title auto-appended)', section: 'email-templates' },
  { key: 'studyReminderEmailBody', label: 'Body Paragraph', multiline: true, section: 'email-templates' },
  { key: 'studyReminderEmailCtaLabel', label: 'Button Label', section: 'email-templates' },
  { key: 'studyReminderEmailSignoff', label: 'Signoff', multiline: true, section: 'email-templates' },
  { key: 'studyReengagementDays', label: 'Inactivity thresholds — comma-separated days for each tier (e.g. "7,14,30")', section: 'email-templates' },
  { key: 'studyReengagementT1Subject', label: 'Tier 1 — Subject Line', section: 'email-templates' },
  { key: 'studyReengagementT1Body', label: 'Tier 1 — Body', multiline: true, section: 'email-templates' },
  { key: 'studyReengagementT1Cta', label: 'Tier 1 — Button Label', section: 'email-templates' },
  { key: 'studyReengagementT2Subject', label: 'Tier 2 — Subject Line', section: 'email-templates' },
  { key: 'studyReengagementT2Body', label: 'Tier 2 — Body', multiline: true, section: 'email-templates' },
  { key: 'studyReengagementT2Cta', label: 'Tier 2 — Button Label', section: 'email-templates' },
  { key: 'studyReengagementT3Subject', label: 'Tier 3 — Subject Line', section: 'email-templates' },
  { key: 'studyReengagementT3Body', label: 'Tier 3 — Body', multiline: true, section: 'email-templates' },
  { key: 'studyReengagementT3Cta', label: 'Tier 3 — Button Label', section: 'email-templates' },
  { key: 'emailChangeSubject', label: 'Subject Line', section: 'email-templates' },
  { key: 'emailChangeBody', label: 'Body Text', multiline: true, section: 'email-templates' },
  { key: 'emailChangeCtaLabel', label: 'Button Label', section: 'email-templates' },
  { key: 'twoFaOtpEmailSubject', label: 'Subject Line', section: 'email-templates' },
  { key: 'twoFaOtpEmailBody', label: 'Body Text (shown above the code)', multiline: true, section: 'email-templates' },
  { key: 'twoFaOtpEmailExpiry', label: 'Expiry / Security Note (shown below the code)', multiline: true, section: 'email-templates' },
  { key: 'qaAnsweredEmailSubject', label: 'Subject Line', section: 'email-templates' },
  { key: 'qaAnsweredEmailBody', label: 'Body Text (paragraph shown below the greeting)', multiline: true, section: 'email-templates' },
  { key: 'qaAnsweredEmailCtaLabel', label: 'Button Label', section: 'email-templates' },
  { key: 'qaAnsweredEmailSignoff', label: 'Signoff', multiline: true, section: 'email-templates' },
]

function normalizeStudies(siteContent: SiteContent): StudyProgram[] {
  if (Array.isArray(siteContent.studies) && siteContent.studies.length > 0) {
    return siteContent.studies.map(study => ({
      ...study,
      homepageEyebrow: typeof study.homepageEyebrow === 'string' ? study.homepageEyebrow : '',
      showOnHomepage: study.showOnHomepage === true,
      showNewTag: study.showNewTag === true,
      newTagLabel: typeof study.newTagLabel === 'string' ? study.newTagLabel : 'NEW',
      numberOfChapters: Number.isInteger(study.numberOfChapters) && study.numberOfChapters >= 1 && study.numberOfChapters <= 999 ? study.numberOfChapters : 1,
    }))
  }

  const fallbackSections = siteContent.colossiansStudySections?.length
    ? siteContent.colossiansStudySections
    : DEFAULTS.colossiansStudySections

  return [
    {
      id: 'study-colossians',
      slug: 'colossians',
      title: 'Colossians: Rooted in Christ',
      description: 'Walk through Colossians in guided lessons with commentary, Greek notes, and discussion prompts.',
      homepageEyebrow: 'New Study',
      showOnHomepage: true,
      showNewTag: true,
      newTagLabel: 'NEW',
      status: 'active',
      difficulty: 'intermediate',
      estimatedHours: 12,
      completionBadge: 'Colossians Completion',
      numberOfChapters: 4,
      sections: fallbackSections,
    },
  ]
}

function normalizeSiteContentForAdmin(siteContent: SiteContent): SiteContent {
  const studies = normalizeStudies(siteContent)
  const colossians = studies.find(study => study.slug === 'colossians')

  return {
    ...siteContent,
    studies,
    colossiansStudySections: colossians?.sections?.length
      ? colossians.sections
      : (siteContent.colossiansStudySections?.length ? siteContent.colossiansStudySections : DEFAULTS.colossiansStudySections),
  }
}

function buildSiteContentForSave(siteContent: SiteContent): SiteContent {
  const normalized = normalizeSiteContentForAdmin(siteContent)
  const colossians = normalized.studies.find(study => study.slug === 'colossians')
  return {
    ...normalized,
    colossiansStudySections: colossians?.sections ?? normalized.colossiansStudySections,
  }
}

function QrCodeImage({ url, label }: { url: string; label: string }) {
  const canvasRef = React.useRef<HTMLCanvasElement>(null)
  const [dataUrl, setDataUrl] = React.useState<string>('')

  React.useEffect(() => {
    const canvas = canvasRef.current
    if (!canvas) return
    QRCode.toCanvas(canvas, url, { width: 256, margin: 2, color: { dark: '#000000', light: '#ffffff' } }, (err: Error | null | undefined) => {
      if (err) { console.error('[QR]', err); return }
      setDataUrl(canvas.toDataURL('image/png'))
    })
  }, [url])

  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 6, marginTop: 10 }}>
      <canvas ref={canvasRef} style={{ width: 128, height: 128, border: '1px solid var(--admin-border, #ddd)', borderRadius: 4 }} />
      {dataUrl && (
        <a
          href={dataUrl}
          download={`qr-${label.toLowerCase().replace(/[^a-z0-9]+/g, '-')}.png`}
          className="btn-admin-reset"
          style={{ fontSize: '0.8em' }}
        >
          Download PNG
        </a>
      )}
    </div>
  )
}

export default function AdminPage({ content, onSave, onLogout }: Props) {
  const normalizedContent = normalizeSiteContentForAdmin(content)
  const [form, setForm] = useState<SiteContent>(normalizedContent)
  const [lastSavedSnapshot, setLastSavedSnapshot] = useState(() => JSON.stringify(normalizedContent))
  const [status, setStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle')
  const [errorMsg, setErrorMsg] = useState('')
  const [adminView, setAdminView] = useState<AdminView>('dashboard')
  const [podcastTab, setPodcastTab] = useState<PodcastTab>('current-series')
  const [stats, setStats] = useState<AdminStats | null>(null)
  const [statsStatus, setStatsStatus] = useState<'loading' | 'ready' | 'error'>('loading')
  const [statsRange, setStatsRange] = useState<'7d' | '30d' | '90d'>('30d')
  const [maintenanceMsg, setMaintenanceMsg] = useState('')
  const [opsMsg, setOpsMsg] = useState('')
  const [backupFiles, setBackupFiles] = useState<BackupPreview[]>([])
  const [selectedBackup, setSelectedBackup] = useState('')
  const [selectedBackupPreview, setSelectedBackupPreview] = useState<BackupPreview | null>(null)
  const [fullBackupBusy, setFullBackupBusy] = useState(false)

  // TOTP management state
  const [totpEnabled, setTotpEnabled] = useState<boolean | null>(null)
  const [appVersion, setAppVersion] = useState<string>('')
  const [totpSetupQr, setTotpSetupQr] = useState<string | null>(null)
  const [totpSetupSecret, setTotpSetupSecret] = useState<string | null>(null)
  const [totpConfirmCode, setTotpConfirmCode] = useState('')
  const [totpMsg, setTotpMsg] = useState('')
  const [totpRecoveryCodes, setTotpRecoveryCodes] = useState<string[] | null>(null)
  const [publishState, setPublishState] = useState<PublishState>({ draftUpdatedAt: null, publishedAt: null })
  const [publishConfirmOpen, setPublishConfirmOpen] = useState(false)
  const [assets, setAssets] = useState<AdminAsset[]>([])
  const [assetTagEdits, setAssetTagEdits] = useState<Record<string, string>>({})
  const [opsStatus, setOpsStatus] = useState<OpsStatus | null>(null)
  const [assetUploadPending, setAssetUploadPending] = useState(false)

  const [endSeriesOpen, setEndSeriesOpen] = useState(false)
  const [endSeriesNewTitle, setEndSeriesNewTitle] = useState('')
  const [endSeriesNewImage, setEndSeriesNewImage] = useState('')
  const [endSeriesNewSeason, setEndSeriesNewSeason] = useState('')

  const [questions, setQuestions] = useState<Question[]>([])
  const [contactSubmissions, setContactSubmissions] = useState<ContactSubmission[]>([])
  const [answeredQuestions, setAnsweredQuestions] = useState<{ [key: string]: string }>({})
  const [editingQuestionId, setEditingQuestionId] = useState<string | null>(null)
  const [questionSearch, setQuestionSearch] = useState('')
  const [questionFilter, setQuestionFilter] = useState<'all' | 'pending' | 'approved' | 'answered' | 'unanswered'>('all')
  const [questionPage, setQuestionPage] = useState(0)
  const [selectedQuestionIds, setSelectedQuestionIds] = useState<Set<string>>(new Set())
  const [mobileNavOpen, setMobileNavOpen] = useState(false)
  const [navSearch, setNavSearch] = useState('')
  const [subscribers, setSubscribers] = useState<Subscriber[]>([])
  const [subscriberSearch, setSubscriberSearch] = useState('')
  const [downloadStats, setDownloadStats] = useState<Record<string, number>>({})
  // Study comments moderation state
  interface AdminComment { id: string; studySlug: string; sectionId: string; displayName: string; text: string; createdAt: string; isApproved: boolean; approvedAt: string | null }
  const [studyComments, setStudyComments] = useState<AdminComment[]>([])
  const [commentsLoading, setCommentsLoading] = useState(false)
  const [commentsLoaded, setCommentsLoaded] = useState(false)
  const [commentFilter, setCommentFilter] = useState<'all' | 'pending' | 'approved'>('all')
  const [commentSearch, setCommentSearch] = useState('')

  // Episode scripts state
  const [scriptsList, setScriptsList] = useState<Array<{ episodeNumber: string; title: string; filename: string; uploadedAt: string | null; wordCount: number }>>([])
  const [scriptsLoading, setScriptsLoading] = useState(false)
  const [scriptUploadEpisode, setScriptUploadEpisode] = useState('')
  const [scriptUploadTitle, setScriptUploadTitle] = useState('')
  const [scriptUploadFile, setScriptUploadFile] = useState<File | null>(null)
  const [scriptUploadBusy, setScriptUploadBusy] = useState(false)
  const [scriptUploadMsg, setScriptUploadMsg] = useState('')

  // QR codes state
  interface QrCode { id: string; slug: string; label: string; destination: string; active: boolean; createdAt: string; scanCount?: number }
  interface QrScan { id: string; qrId: string; slug: string; scannedAt: string; ip: string; userAgent: string }
  const [qrCodes, setQrCodes] = useState<QrCode[]>([])
  const [qrScans, setQrScans] = useState<QrScan[]>([])
  const [qrLoading, setQrLoading] = useState(false)
  const [qrLoaded, setQrLoaded] = useState(false)
  const [qrMsg, setQrMsg] = useState('')
  const [qrNewLabel, setQrNewLabel] = useState('')
  const [qrNewSlug, setQrNewSlug] = useState('')
  const [qrNewDest, setQrNewDest] = useState('')
  const [qrEditId, setQrEditId] = useState<string | null>(null)
  const [qrEditLabel, setQrEditLabel] = useState('')
  const [qrEditDest, setQrEditDest] = useState('')

  const [rssEpisodes, setRssEpisodes] = useState<Array<{ title: string; audioUrl: string; duration: string; episode: string }>>([])

const [dashboardNow, setDashboardNow] = useState(() => new Date())
  const [manualQuestion, setManualQuestion] = useState({
    firstName: '',
    email: '',
    question: '',
    answer: '',
    approve: false,
  })
  const [manualQuestionStatus, setManualQuestionStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle')
  const [manualQuestionMsg, setManualQuestionMsg] = useState('')
  const [previewOpen, setPreviewOpen] = useState(false)
  const previewIframeRef = useRef<HTMLIFrameElement>(null)
  const dragSensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 5 } }))
  const saveStatusTimeoutRef = useRef<number | null>(null)
  const autosaveRestoreCheckedRef = useRef(false)

  const isDirty = JSON.stringify(form) !== lastSavedSnapshot
  const unreadEmailCount = contactSubmissions.filter(s => !s.archived).length
  const unansweredCount = questions.filter(q => !q.answer?.trim()).length
  const navSearchTerm = navSearch.trim().toLowerCase()
  const filteredAdminViewOptions = ADMIN_VIEW_OPTIONS
    .map(group => ({
      ...group,
      options: group.options.filter(option => {
        if (!navSearchTerm) return true
        return `${group.group} ${option.label} ${option.value}`.toLowerCase().includes(navSearchTerm)
      }),
    }))
    .filter(group => group.options.length > 0)

  // Silently saves draft every 30s when there are unsaved changes
  useAutosave(form, isDirty, handleSaveDraft)

  function clearAutosaveDraft() {
    try {
      sessionStorage.removeItem('admin-autosave-draft')
    } catch {
      // Ignore storage failures; the server draft is still the source of truth.
    }
  }

  // Broadcast live form state + active view into the preview iframe whenever they change
  useEffect(() => {
    if (!previewOpen) return
    const timer = setTimeout(() => {
      previewIframeRef.current?.contentWindow?.postMessage(
        { type: 'admin-preview-content', content: form, view: adminView },
        window.location.origin
      )
    }, 150)
    return () => clearTimeout(timer)
  }, [form, adminView, previewOpen])

  useEffect(() => {
    const normalized = normalizeSiteContentForAdmin(content)
    const nextSnapshot = JSON.stringify(normalized)
    setForm(normalized)
    setLastSavedSnapshot(nextSnapshot)
    autosaveRestoreCheckedRef.current = false
  }, [content])

  useEffect(() => {
    if (autosaveRestoreCheckedRef.current) return
    autosaveRestoreCheckedRef.current = true

    try {
      const rawDraft = sessionStorage.getItem('admin-autosave-draft')
      if (!rawDraft) return

      const parsedDraft = JSON.parse(rawDraft) as SiteContent
      const restoredDraft = normalizeSiteContentForAdmin(parsedDraft)
      if (JSON.stringify(restoredDraft) === lastSavedSnapshot) {
        clearAutosaveDraft()
        return
      }

      if (confirm('Restore an unsaved draft from this browser session?')) {
        setForm(restoredDraft)
      }
    } catch {
      clearAutosaveDraft()
    }
  }, [lastSavedSnapshot])

  useEffect(() => {
    return () => {
      if (saveStatusTimeoutRef.current !== null) {
        window.clearTimeout(saveStatusTimeoutRef.current)
      }
    }
  }, [])

  useEffect(() => {
    if (!mobileNavOpen) return

    const previousOverflow = document.body.style.overflow
    document.body.style.overflow = 'hidden'

    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === 'Escape') {
        setMobileNavOpen(false)
      }
    }

    window.addEventListener('keydown', handleKeyDown)

    return () => {
      window.removeEventListener('keydown', handleKeyDown)
      document.body.style.overflow = previousOverflow
    }
  }, [mobileNavOpen])

  useEffect(() => {
    const intervalId = window.setInterval(() => {
      setDashboardNow(new Date())
    }, 1000)

    return () => window.clearInterval(intervalId)
  }, [])

  useEffect(() => {
    const handleBeforeUnload = (event: BeforeUnloadEvent) => {
      if (!isDirty) return
      event.preventDefault()
      event.returnValue = ''
    }

    window.addEventListener('beforeunload', handleBeforeUnload)
    return () => window.removeEventListener('beforeunload', handleBeforeUnload)
  }, [isDirty])

  useEffect(() => {
    fetch('/api/episode-audio')
      .then(r => r.ok ? r.json() : Promise.reject())
      .then((data: { episodes: Array<{ title: string; audioUrl: string; duration: string; episode: string }> }) => {
        setRssEpisodes(data.episodes ?? [])
      })
      .catch(() => {})
  }, [])

  useEffect(() => {
    fetch('/api/admin-auth/status')
      .then(r => r.ok ? r.json() : Promise.reject())
      .then((data: { totpEnabled?: boolean; version?: string; commit?: string }) => {
        setTotpEnabled(!!data.totpEnabled)
        if (data.version) setAppVersion(data.commit ? `v${data.version} (${data.commit})` : `v${data.version}`)
      })
      .catch(() => {})
  }, [])

  useEffect(() => {
    fetch(`/api/admin-stats?range=${statsRange}`)
      .then(r => (r.ok ? r.json() : Promise.reject(new Error('Failed to load stats'))))
      .then(data => {
        setStats(data as AdminStats)
        setStatsStatus('ready')
      })
      .catch(() => {
        setStatsStatus('error')
      })

    fetch('/api/admin-questions')
      .then(r => (r.ok ? r.json() : Promise.reject(new Error('Failed to load questions'))))
      .then(data => {
        setQuestions((data as { questions: Question[] }).questions ?? [])
      })
      .catch(() => {})

    fetch('/api/admin-contact-submissions')
      .then(r => (r.ok ? r.json() : Promise.reject(new Error('Failed to load contact submissions'))))
      .then(data => {
        setContactSubmissions((data as { submissions: ContactSubmission[] }).submissions ?? [])
      })
      .catch(() => {})

    fetch('/api/admin-stats/backups')
      .then(r => (r.ok ? r.json() : Promise.reject(new Error('Failed to load backups'))))
      .then(data => {
        const files = Array.isArray((data as { backups?: unknown }).backups) ? (data as { backups: BackupPreview[] }).backups : []
        setBackupFiles(files)
        if (files.length > 0) {
          setSelectedBackup(files[0].filename)
        }
      })
      .catch(() => {})

    fetch('/api/admin-content-state')
      .then(r => (r.ok ? r.json() : Promise.reject(new Error('Failed to load publish state'))))
      .then(data => {
        const state = (data as { publishState?: PublishState }).publishState
        if (state) {
          setPublishState({
            draftUpdatedAt: state.draftUpdatedAt ?? null,
            publishedAt: state.publishedAt ?? null,
          })
        }
      })
      .catch(() => {})

    void reloadAssets()

    fetch('/api/admin-ops/status')
      .then(r => (r.ok ? r.json() : Promise.reject(new Error('Failed to load operations status'))))
      .then(data => {
        setOpsStatus(data as OpsStatus)
      })
      .catch(() => {})

    fetch('/api/admin-subscribers')
      .then(r => (r.ok ? r.json() : Promise.reject()))
      .then(data => setSubscribers((data as { subscribers: Subscriber[] }).subscribers ?? []))
      .catch(() => {})

    fetch('/api/admin-download-stats')
      .then(r => (r.ok ? r.json() : Promise.reject()))
      .then(data => setDownloadStats((data as { counts: Record<string, number> }).counts ?? {}))
      .catch(() => {})
  }, [])

  useEffect(() => {
    if (!selectedBackup) {
      setSelectedBackupPreview(null)
      return
    }

    fetch('/api/admin-stats/backup-preview', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ filename: selectedBackup }),
    })
      .then(r => (r.ok ? r.json() : Promise.reject(new Error('Preview failed'))))
      .then(data => {
        const preview = (data as { preview?: BackupPreview }).preview ?? null
        setSelectedBackupPreview(preview)
      })
      .catch(() => {
        setSelectedBackupPreview(null)
      })
  }, [selectedBackup])

  async function reloadStats(range?: '7d' | '30d' | '90d') {
    const r = await fetch(`/api/admin-stats?range=${range ?? statsRange}`)
    if (!r.ok) throw new Error('Could not refresh stats')
    const data = await r.json()
    setStats(data as AdminStats)
    setStatsStatus('ready')
  }


  useEffect(() => {
    setQuestionPage(0)
  }, [questionSearch, questionFilter])

  async function reloadBackups() {
    const r = await fetch('/api/admin-stats/backups')
    if (!r.ok) throw new Error('Could not refresh backups')
    const data = await r.json() as { backups?: BackupPreview[] }
    const files = Array.isArray(data.backups) ? data.backups : []
    setBackupFiles(files)
    const names = files.map(f => f.filename)
    if (files.length > 0 && !names.includes(selectedBackup)) {
      setSelectedBackup(files[0].filename)
    }
  }

  async function reloadContentFromServer() {
    const draftRes = await fetch('/api/admin-content?source=draft')
    if (draftRes.ok) {
      const draftData = await draftRes.json() as { siteContent?: Partial<SiteContent> }
      if (draftData?.siteContent && typeof draftData.siteContent === 'object') {
        const next = normalizeSiteContentForAdmin({ ...DEFAULTS, ...draftData.siteContent })
        setForm(next)
        onSave(next)
        return
      }
    }

    const publishedRes = await fetch('/api/admin-content')
    if (!publishedRes.ok) return
    const publishedData = await publishedRes.json() as { siteContent?: Partial<SiteContent> }
    if (publishedData?.siteContent && typeof publishedData.siteContent === 'object') {
      const next = normalizeSiteContentForAdmin({ ...DEFAULTS, ...publishedData.siteContent })
      setForm(next)
      onSave(next)
    }
  }

  async function reloadAssets() {
    const r = await fetch('/api/admin-assets')
    if (!r.ok) throw new Error('Could not refresh assets')
    const data = await r.json() as { assets?: AdminAsset[] }
    const incomingAssets = Array.isArray(data.assets) ? data.assets : []
    setAssets(incomingAssets)
    setAssetTagEdits(incomingAssets.reduce<Record<string, string>>((memo, asset) => {
      memo[asset.filename] = (asset.tags ?? []).join(', ')
      return memo
    }, {}))
  }

  async function reloadOpsStatus() {
    const r = await fetch('/api/admin-ops/status')
    if (!r.ok) throw new Error('Could not refresh ops status')
    const data = await r.json() as OpsStatus
    setOpsStatus(data)
  }

  async function handleSaveDraft() {
    const payload = buildSiteContentForSave(form)
    const formSnapshot = JSON.stringify(form)
    setStatus('saving')
    setErrorMsg('')
    try {
      const res = await fetch('/api/admin-content-draft', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ siteContent: payload }),
      })
      if (!res.ok) {
        const data = await res.json().catch(() => ({}))
        throw new Error((data as { message?: string }).message ?? 'Draft save failed')
      }
      const data = await res.json() as { updatedAt?: string }
      setPublishState(prev => ({ ...prev, draftUpdatedAt: data.updatedAt ?? new Date().toISOString() }))
      setLastSavedSnapshot(formSnapshot)
      clearAutosaveDraft()
      setStatus('saved')
      if (saveStatusTimeoutRef.current !== null) {
        window.clearTimeout(saveStatusTimeoutRef.current)
      }
      saveStatusTimeoutRef.current = window.setTimeout(() => setStatus('idle'), 3500)
    } catch (err) {
      setErrorMsg(err instanceof Error ? err.message : 'Unknown error')
      setStatus('error')
    }
  }

  async function handlePublishDraft() {
    const payload = buildSiteContentForSave(form)
    setStatus('saving')
    setErrorMsg('')
    try {
      // Always save the current form to draft first so publish never fails from a missing draft
      const draftRes = await fetch('/api/admin-content-draft', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ siteContent: payload }),
      })
      if (!draftRes.ok) {
        const data = await draftRes.json().catch(() => ({}))
        throw new Error((data as { message?: string }).message ?? 'Draft save failed')
      }
      const draftData = await draftRes.json() as { updatedAt?: string }
      setPublishState(prev => ({ ...prev, draftUpdatedAt: draftData.updatedAt ?? new Date().toISOString() }))

      const publishRes = await fetch('/api/admin-content/publish', { method: 'POST' })
      if (!publishRes.ok) {
        const data = await publishRes.json().catch(() => ({}))
        throw new Error((data as { message?: string }).message ?? 'Publish failed')
      }

      const published = await publishRes.json() as { publishedAt?: string }
      const latestRes = await fetch('/api/admin-content?source=draft')
      if (!latestRes.ok) throw new Error('Failed to refresh published content')
      const latest = await latestRes.json() as { siteContent?: Partial<SiteContent> }
      if (latest?.siteContent) {
        const next = normalizeSiteContentForAdmin({ ...DEFAULTS, ...latest.siteContent })
        setForm(next)
        onSave(next)
        setLastSavedSnapshot(JSON.stringify(next))
      }

      setPublishState(prev => ({ ...prev, publishedAt: published.publishedAt ?? new Date().toISOString() }))
      setPublishConfirmOpen(false)
      clearAutosaveDraft()
      await reloadStats()
      setStatus('saved')
      if (saveStatusTimeoutRef.current !== null) {
        window.clearTimeout(saveStatusTimeoutRef.current)
      }
      saveStatusTimeoutRef.current = window.setTimeout(() => setStatus('idle'), 3500)
    } catch (err) {
      setPublishConfirmOpen(false)
      setErrorMsg(err instanceof Error ? err.message : 'Unknown error')
      setStatus('error')
    }
  }

  function updateSeoField(field: keyof SeoSettings, value: string | string[]) {
    setForm(f => ({
      ...f,
      seo: {
        ...f.seo,
        [field]: value,
      },
    }))
  }

  function updateLegalField(field: keyof LegalSettings, value: string | string[]) {
    setForm(f => ({
      ...f,
      legal: {
        ...f.legal,
        [field]: value,
      },
    }))
  }

  async function handleTotpSetupInit() {
    setTotpMsg('')
    setTotpRecoveryCodes(null)
    const res = await fetch('/api/admin-auth/totp-setup-init', { method: 'POST' })
    const data = await res.json().catch(() => ({})) as { qrDataUrl?: string; secret?: string; message?: string }
    if (!res.ok) { setTotpMsg(data.message ?? 'Setup failed.'); return }
    setTotpSetupQr(data.qrDataUrl ?? null)
    setTotpSetupSecret(data.secret ?? null)
    setTotpConfirmCode('')
  }

  async function handleTotpSetupConfirm(e: React.FormEvent) {
    e.preventDefault()
    setTotpMsg('')
    const res = await fetch('/api/admin-auth/totp-setup-confirm', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ code: totpConfirmCode }),
    })
    const data = await res.json().catch(() => ({})) as { ok?: boolean; recoveryCodes?: string[]; message?: string }
    if (!res.ok) { setTotpMsg(data.message ?? 'Confirmation failed.'); return }
    setTotpEnabled(true)
    setTotpSetupQr(null)
    setTotpSetupSecret(null)
    setTotpConfirmCode('')
    setTotpRecoveryCodes(data.recoveryCodes ?? null)
    setTotpMsg('Two-factor authentication enabled.')
  }

  async function handleTotpDisable() {
    if (!confirm('Disable two-factor authentication? This will make your admin less secure.')) return
    setTotpMsg('')
    const res = await fetch('/api/admin-auth/totp-disable', { method: 'POST' })
    if (res.ok) { setTotpEnabled(false); setTotpRecoveryCodes(null); setTotpMsg('Two-factor authentication disabled.') }
    else { const d = await res.json().catch(() => ({})) as { message?: string }; setTotpMsg(d.message ?? 'Failed to disable TOTP.') }
  }

  async function handleTotpRegenRecovery() {
    if (!confirm('Regenerate recovery codes? Your old codes will stop working immediately.')) return
    setTotpMsg('')
    const res = await fetch('/api/admin-auth/totp-regen-recovery', { method: 'POST' })
    const data = await res.json().catch(() => ({})) as { ok?: boolean; recoveryCodes?: string[]; message?: string }
    if (!res.ok) { setTotpMsg(data.message ?? 'Failed.'); return }
    setTotpRecoveryCodes(data.recoveryCodes ?? null)
    setTotpMsg('New recovery codes generated. Save these now.')
  }

  function addRedirectRule() {
    setForm(f => ({
      ...f,
      redirects: [
        ...(f.redirects ?? []),
        {
          id: Date.now().toString(36),
          path: '/new-short-link',
          target: 'https://',
          statusCode: 301,
        },
      ],
    }))
  }

  function updateRedirectRule(id: string, field: keyof RedirectRule, value: string | number) {
    setForm(f => ({
      ...f,
      redirects: (f.redirects ?? []).map(rule => rule.id === id
        ? {
            ...rule,
            [field]: field === 'statusCode' ? (Number(value) === 302 ? 302 : 301) : value,
          }
        : rule),
    }))
  }

  function removeRedirectRule(id: string) {
    setForm(f => ({ ...f, redirects: (f.redirects ?? []).filter(rule => rule.id !== id) }))
  }

  function addPodcastLink() {
    setForm(f => ({
      ...f,
      podcastFeaturedLinks: [
        ...(f.podcastFeaturedLinks ?? []),
        {
          id: Date.now().toString(36),
          title: '',
          episodeNumber: '',
          summary: '',
          url: '',
          embedUrl: '',
          showNotes: '',
          discussionQuestions: [],
        },
      ],
    }))
  }

  function updatePodcastLink(id: string, field: keyof PodcastFeaturedLink, value: string) {
    setForm(f => ({
      ...f,
      podcastFeaturedLinks: (f.podcastFeaturedLinks ?? []).map(link => link.id === id ? { ...link, [field]: value } : link),
    }))
  }

  function updatePodcastLinkQuestions(id: string, raw: string) {
    const questions = raw.split('\n').map(q => q.trim()).filter(Boolean)
    setForm(f => ({
      ...f,
      podcastFeaturedLinks: (f.podcastFeaturedLinks ?? []).map(link =>
        link.id === id ? { ...link, discussionQuestions: questions } : link
      ),
    }))
  }

  function removePodcastLink(id: string) {
    setForm(f => ({ ...f, podcastFeaturedLinks: (f.podcastFeaturedLinks ?? []).filter(link => link.id !== id) }))
  }

async function handleAssetUpload(event: ChangeEvent<HTMLInputElement>) {
    const file = event.target.files?.[0]
    if (!file) return

    setAssetUploadPending(true)
    setOpsMsg('')
    try {
      const dataUrl = await new Promise<string>((resolve, reject) => {
        const reader = new FileReader()
        reader.onload = () => resolve(String(reader.result ?? ''))
        reader.onerror = () => reject(new Error('Failed to read file'))
        reader.readAsDataURL(file)
      })

      const res = await fetch('/api/admin-assets', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ filename: file.name, dataUrl }),
      })
      if (!res.ok) {
        const data = await res.json().catch(() => ({}))
        throw new Error((data as { message?: string }).message ?? 'Upload failed')
      }
      await reloadAssets()
      setOpsMsg('Asset uploaded successfully.')
    } catch (err) {
      setOpsMsg(err instanceof Error ? err.message : 'Upload failed.')
    } finally {
      setAssetUploadPending(false)
      event.target.value = ''
    }
  }

  async function handleDeleteAsset(filename: string) {
    if (!confirm(`Delete asset ${filename}?`)) return
    try {
      const res = await fetch(`/api/admin-assets/${encodeURIComponent(filename)}`, { method: 'DELETE' })
      if (!res.ok) throw new Error('Delete failed')
      await reloadAssets()
      setOpsMsg('Asset deleted.')
    } catch {
      setOpsMsg('Asset delete failed.')
    }
  }

  async function handleSaveAssetTags(filename: string) {
    const tagsText = assetTagEdits[filename] ?? ''
    const tags = tagsText.split(',').map(tag => tag.trim()).filter(Boolean)
    try {
      const res = await fetch(`/api/admin-assets/${encodeURIComponent(filename)}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ tags }),
      })
      if (!res.ok) {
        const data = await res.json().catch(() => ({}))
        throw new Error((data as { message?: string }).message ?? 'Save failed')
      }
      await reloadAssets()
      setOpsMsg('Asset tags saved.')
    } catch (err) {
      setOpsMsg(err instanceof Error ? err.message : 'Save failed.')
    }
  }

  async function handlePurgeCache() {
    try {
      const res = await fetch('/api/admin-ops/purge-cache', { method: 'POST' })
      if (!res.ok) {
        const data = await res.json().catch(() => ({}))
        throw new Error((data as { message?: string }).message ?? 'Cache purge failed')
      }
      await reloadOpsStatus()
      setOpsMsg('Cache purge triggered.')
    } catch (err) {
      setOpsMsg(err instanceof Error ? err.message : 'Cache purge failed.')
    }
  }

  async function handleDeployHook() {
    try {
      const res = await fetch('/api/admin-ops/deploy', { method: 'POST' })
      if (!res.ok) {
        const data = await res.json().catch(() => ({}))
        throw new Error((data as { message?: string }).message ?? 'Deploy hook failed')
      }
      await reloadOpsStatus()
      setOpsMsg('Deploy hook triggered.')
    } catch (err) {
      setOpsMsg(err instanceof Error ? err.message : 'Deploy hook failed.')
    }
  }

  function maskIp(ip: string) {
    if (!ip || ip === 'unknown') return 'unknown'
    if (ip.includes('.')) {
      const parts = ip.split('.')
      if (parts.length === 4) return `${parts[0]}.${parts[1]}.x.x`
    }
    if (ip.includes(':')) {
      const parts = ip.split(':')
      return `${parts.slice(0, 3).join(':')}:x:x`
    }
    return ip
  }

  async function handleExport() {
    try {
      const r = await fetch('/api/admin-stats/export')
      if (!r.ok) throw new Error('Export failed')
      const data = await r.json()
      const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
      const url = URL.createObjectURL(blob)
      const a = document.createElement('a')
      a.href = url
      a.download = `siteforge-admin-export-${new Date().toISOString().slice(0, 10)}.json`
      a.click()
      URL.revokeObjectURL(url)
      setMaintenanceMsg('Export downloaded.')
    } catch {
      setMaintenanceMsg('Export failed.')
    }
  }

  async function handlePrune() {
    const input = prompt('Keep how many days of analytics data?', '180')
    if (input === null) return
    const days = Number(input)
    if (!Number.isFinite(days) || days <= 0) {
      setMaintenanceMsg('Invalid retention days.')
      return
    }
    try {
      const r = await fetch('/api/admin-stats/prune', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ days }),
      })
      if (!r.ok) throw new Error('Prune failed')
      await reloadStats()
      setMaintenanceMsg(`Pruned analytics to ${Math.floor(days)} days.`)
    } catch {
      setMaintenanceMsg('Prune failed.')
    }
  }

  async function handleClear() {
    if (!confirm('Clear ALL analytics data now? This cannot be undone.')) return
    try {
      const r = await fetch('/api/admin-stats/clear', { method: 'POST' })
      if (!r.ok) throw new Error('Clear failed')
      await reloadStats()
      setMaintenanceMsg('All analytics data cleared.')
    } catch {
      setMaintenanceMsg('Clear failed.')
    }
  }

  async function handleBackupNow() {
    try {
      const r = await fetch('/api/admin-stats/backup', { method: 'POST' })
      if (!r.ok) throw new Error('Backup failed')
      await reloadStats()
      await reloadBackups()
      setMaintenanceMsg('Backup snapshot created.')
    } catch {
      setMaintenanceMsg('Backup failed.')
    }
  }

  async function handleDownloadFullBackup() {
    setFullBackupBusy(true)
    setMaintenanceMsg('Preparing full backup archive…')
    try {
      const r = await fetch('/api/admin-backup/export')
      if (!r.ok) throw new Error('Export failed')
      const blob = await r.blob()
      const disposition = r.headers.get('Content-Disposition') ?? ''
      const filename = /filename="([^"]+)"/.exec(disposition)?.[1] ?? 'siteforge-data-backup.tar.gz'
      const url = URL.createObjectURL(blob)
      const link = document.createElement('a')
      link.href = url
      link.download = filename
      link.click()
      URL.revokeObjectURL(url)
      setMaintenanceMsg(`Full backup downloaded (${filename}). Keep it somewhere safe — it contains all site data.`)
    } catch {
      setMaintenanceMsg('Full backup download failed.')
    } finally {
      setFullBackupBusy(false)
    }
  }

  async function handleImportFullBackup(file: File) {
    if (!confirm(`Restore ALL site data from ${file.name}?\n\nThis replaces content, uploads, study accounts, notes, questions, and analytics with the archive contents. A pre-import snapshot is saved first, and study users will need to sign in again.`)) return
    setFullBackupBusy(true)
    setMaintenanceMsg('Restoring full backup — do not close this page…')
    try {
      const r = await fetch('/api/admin-backup/import', {
        method: 'POST',
        headers: { 'Content-Type': 'application/gzip' },
        body: file,
      })
      const data = await r.json().catch(() => null)
      if (!r.ok) throw new Error(data?.message ?? 'Restore failed')
      await reloadContentFromServer()
      await reloadStats()
      await reloadBackups()
      setMaintenanceMsg(`Full data restore complete — ${data?.restoredEntries ?? 'all'} items restored from ${file.name}.`)
    } catch (err) {
      setMaintenanceMsg(err instanceof Error ? err.message : 'Full data restore failed.')
    } finally {
      setFullBackupBusy(false)
    }
  }

  async function handleRestoreBackup() {
    if (!selectedBackup) {
      setMaintenanceMsg('Select a backup first.')
      return
    }
    if (!confirm(`Restore backup ${selectedBackup}? This will overwrite current admin data and analytics.`)) return
    try {
      const r = await fetch('/api/admin-stats/restore', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ filename: selectedBackup }),
      })
      if (!r.ok) throw new Error('Restore failed')
      await reloadContentFromServer()
      await reloadStats()
      await reloadBackups()
      setMaintenanceMsg(`Restored from ${selectedBackup}. Content fields were refreshed from backup.`)
    } catch {
      setMaintenanceMsg('Restore failed.')
    }
  }

  function formatDate(value: string | null) {
    if (!value) return 'Not available yet'
    const d = new Date(value)
    return Number.isNaN(d.getTime()) ? 'Not available yet' : d.toLocaleString()
  }

  function handleChange(key: StringField, value: string) {
    setForm(f => ({ ...f, [key]: value }))
  }

  function renderImageAssetSelector(value: string | null | undefined, onChange: (value: string) => void, fieldId: string) {
    const assetOptions = [...assets.map(asset => ({ label: asset.filename, value: asset.url }))]
    const currentValue = value?.trim() ?? ''

    if (currentValue && !assetOptions.some(item => item.value === currentValue)) {
      assetOptions.unshift({ label: `Current image (${currentValue})`, value: currentValue })
    }

    if (assetOptions.length === 0) return null

    return (
      <div className="admin-field-asset-picker">
        <label htmlFor={fieldId}>Choose an existing image</label>
        <select id={fieldId} value={assetOptions.some(a => a.value === currentValue) ? currentValue : ''} onChange={e => onChange(e.target.value)}>
          <option value="">Select image</option>
          {assetOptions.map(option => (
            <option key={option.value} value={option.value}>
              {option.label}
            </option>
          ))}
        </select>
      </div>
    )
  }

  function renderImagePreview(value: string | null | undefined, alt: string) {
    const src = value?.trim() ?? ''
    if (!src) return null

    return (
      <div className="admin-field-image-preview">
        <span className="admin-field-image-preview-label">Preview</span>
        <img src={src} alt={alt} className="admin-field-image-preview-img" />
      </div>
    )
  }

  function getCustomLinkPlacementLabel(placement: CustomLink['placement']) {
    if (placement === 'platforms') return 'Homepage listen/platform links'
    if (placement === 'footer') return 'Footer navigation links'
    if (placement === 'otherSites') return 'Other site links in footer dropdown'
    if (placement === 'externalSites') return 'Homepage external site links'
    if (placement === 'books') return 'Downloads page (Reading List)'
    if (placement === 'recommended') return 'Downloads page (Recommended Resources)'
    return 'Downloads page (More Resources)'
  }

  function getCustomLinkPreviewPath(placement: CustomLink['placement']) {
    if (placement === 'resources') return '/resources'
    if (placement === 'books') return '/resources'
    if (placement === 'recommended') return '/resources'
    return '/'
  }

  function getContentBlockPageLabel(page: CustomBlock['page']) {
    if (page === 'homepage') return 'Homepage'
    if (page === 'start-here') return 'Start Here'
    if (page === 'episodes') return 'Episodes'
    if (page === 'downloads') return 'Downloads'
    if (page === 'about') return 'About'
    if (page === 'contact') return 'Contact'
    return 'Q&A'
  }

  function getContentBlockPreviewPath(page: CustomBlock['page']) {
    if (page === 'homepage') return '/'
    if (page === 'start-here') return '/start-here'
    if (page === 'episodes') return '/episodes'
    if (page === 'downloads') return '/resources'
    if (page === 'about') return '/about'
    if (page === 'contact') return '/contact'
    return '/questions'
  }

  function renderFileAssetSelector(value: string | null | undefined, onChange: (value: string) => void, fieldId: string) {
    const assetOptions = [...assets.map(asset => ({ label: asset.filename, value: asset.url }))]
    const currentValue = value?.trim() ?? ''

    if (currentValue && !assetOptions.some(item => item.value === currentValue)) {
      assetOptions.unshift({ label: `Current file (${currentValue})`, value: currentValue })
    }

    if (assetOptions.length === 0) return null

    return (
      <div className="admin-field-asset-picker">
        <label htmlFor={fieldId}>Choose an existing file</label>
        <select id={fieldId} value={assetOptions.some(a => a.value === currentValue) ? currentValue : ''} onChange={e => onChange(e.target.value)}>
          <option value="">Select file</option>
          {assetOptions.map(option => (
            <option key={option.value} value={option.value}>
              {option.label}
            </option>
          ))}
        </select>
      </div>
    )
  }

  function isImageAsset(filename: string) {
    return /\.(png|jpe?g|webp|gif)$/i.test(filename)
  }

  function confirmLeaveUnsavedChanges() {
    if (!isDirty) return true
    return confirm('You have unsaved changes. Leave this section without saving?')
  }

  function navigateTo(view: AdminView) {
    if (view === adminView) return
    if (!confirmLeaveUnsavedChanges()) return
    setAdminView(view)
    setMobileNavOpen(false)
  }

  function getAdminViewBadge(view: AdminView) {
    if (view === 'questions' && unansweredCount > 0) {
      return { count: unansweredCount, neutral: false }
    }
    if (view === 'subscribers' && subscribers.length > 0) {
      return { count: subscribers.length, neutral: true }
    }
    return null
  }

  function renderAdminNavItem(view: AdminView, label: string) {
    const badge = getAdminViewBadge(view)

    return (
      <button
        key={view}
        type="button"
        className={`admin-nav-item${adminView === view ? ' admin-nav-item--active' : ''}`}
        onClick={() => navigateTo(view)}
      >
        {label}
        {badge && (
          <span className={`admin-nav-badge${badge.neutral ? ' admin-nav-badge--neutral' : ''}`}>
            {badge.count}
          </span>
        )}
      </button>
    )
  }

  function jumpToAdminSection(sectionId: string) {
    document.getElementById(sectionId)?.scrollIntoView({ behavior: 'smooth', block: 'start' })
  }

  function renderSectionJumpNav(view: AdminView) {
    const links = ADMIN_SECTION_LINKS[view] ?? []
    if (links.length < 2) return null

    return (
      <div className="admin-section-jump-nav" aria-label="Jump to section">
        <span className="admin-section-jump-nav-label">Jump to</span>
        <div className="admin-section-jump-nav-links">
          {links.map(link => (
            <button
              key={link.id}
              type="button"
              className="admin-section-jump-nav-btn"
              onClick={() => jumpToAdminSection(link.id)}
            >
              {link.label}
            </button>
          ))}
        </div>
      </div>
    )
  }

  function addLink() {
    setForm(f => ({
      ...f,
      customLinks: [
        ...(f.customLinks ?? []),
        { id: Date.now().toString(36), label: '', url: '', imageUrl: '', placement: 'platforms' as const },
      ],
    }))
  }

  function addOtherSiteLink() {
    setForm(f => ({
      ...f,
      customLinks: [
        ...(f.customLinks ?? []),
        { id: Date.now().toString(36), label: '', url: '', imageUrl: '', placement: 'otherSites' as const },
      ],
    }))
  }

  function addExternalSiteLink() {
    setForm(f => ({
      ...f,
      customLinks: [
        ...(f.customLinks ?? []),
        { id: Date.now().toString(36), label: '', url: '', imageUrl: '', description: '', placement: 'externalSites' as const },
      ],
    }))
  }

  function addResource() {
    setForm(f => ({
      ...f,
      customLinks: [
        ...(f.customLinks ?? []),
        { id: Date.now().toString(36), label: '', url: '', imageUrl: '', description: '', placement: 'resources' as const },
      ],
    }))
  }

  function addBook() {
    setForm(f => ({
      ...f,
      customLinks: [
        ...(f.customLinks ?? []),
        { id: Date.now().toString(36), label: '', url: '', imageUrl: '', description: '', amazonUrl: '', amazonLabel: 'View on Amazon', placement: 'books' as const },
      ],
    }))
  }

  function addRecommended() {
    setForm(f => ({
      ...f,
      customLinks: [
        ...(f.customLinks ?? []),
        { id: Date.now().toString(36), label: '', url: '', imageUrl: '', description: '', placement: 'recommended' as const },
      ],
    }))
  }

  function updateLink(id: string, field: keyof CustomLink, value: string | string[]) {
    setForm(f => ({
      ...f,
      customLinks: (f.customLinks ?? []).map(l => l.id === id ? { ...l, [field]: value } : l),
    }))
  }

  function removeLink(id: string) {
    setForm(f => ({ ...f, customLinks: (f.customLinks ?? []).filter(l => l.id !== id) }))
  }

  function moveLinkToResources(id: string) {
    setForm(f => ({
      ...f,
      customLinks: (f.customLinks ?? []).map(link => (
        link.id === id ? { ...link, placement: 'resources' as const } : link
      )),
    }))
  }

  function addBlock() {
    setForm(f => ({
      ...f,
      customBlocks: [
        ...(f.customBlocks ?? []),
        { id: Date.now().toString(36), heading: '', body: '', page: 'homepage' as const },
      ],
    }))
  }

  function updateBlock(id: string, field: keyof CustomBlock, value: string) {
    setForm(f => ({
      ...f,
      customBlocks: (f.customBlocks ?? []).map(b => b.id === id ? { ...b, [field]: value } : b),
    }))
  }

  function removeBlock(id: string) {
    setForm(f => ({ ...f, customBlocks: (f.customBlocks ?? []).filter(b => b.id !== id) }))
  }

  function addStudyProgram() {
    setForm(f => ({
      ...f,
      studies: [
        ...(f.studies ?? []),
        {
          id: `study-${Date.now().toString(36)}`,
          slug: `new-study-${Date.now().toString(36)}`,
          title: 'New Study',
          description: 'Add study description',
          homepageEyebrow: 'New Study',
          showOnHomepage: false,
          showNewTag: false,
          newTagLabel: 'NEW',
          status: 'planned',
          difficulty: 'beginner',
          estimatedHours: 8,
          completionBadge: 'Study Completion',
          numberOfChapters: 4,
          sections: [],
        },
      ],
    }))
  }

  function updateStudyProgram(studyId: string, field: keyof Omit<StudyProgram, 'sections'>, value: string | number | boolean) {
    setForm(f => ({
      ...f,
      studies: (f.studies ?? []).map(study => study.id === studyId ? { ...study, [field]: value } : study),
    }))
  }

  function removeStudyProgram(studyId: string) {
    setForm(f => ({
      ...f,
      studies: (f.studies ?? []).filter(study => study.id !== studyId),
    }))
  }

  function addStudySection(studyId: string) {
    setForm(f => ({
      ...f,
      studies: (f.studies ?? []).map(study => study.id === studyId
        ? {
            ...study,
            sections: [
              ...(study.sections ?? []),
              {
                id: `${study.slug || 'section'}-${Date.now().toString(36)}`,
                chapter: 1,
                reference: '',
                title: '',
                audioEmbedUrl: '',
                passageText: '',
                summary: '',
                commentary: '',
                greekNotes: [],
                studyQuestions: [],
                checkpointPrompt: '',
                checkpointQuestions: [],
              },
            ],
          }
        : study),
    }))
  }

  function updateStudySection(studyId: string, sectionId: string, field: keyof ColossiansStudySection, value: string | number | string[]) {
    setForm(f => ({
      ...f,
      studies: (f.studies ?? []).map(study => study.id === studyId
        ? {
            ...study,
            sections: (study.sections ?? []).map(section => section.id === sectionId ? { ...section, [field]: value } : section),
          }
        : study),
    }))
  }

  function removeStudySection(studyId: string, sectionId: string) {
    setForm(f => ({
      ...f,
      studies: (f.studies ?? []).map(study => study.id === studyId
        ? { ...study, sections: (study.sections ?? []).filter(section => section.id !== sectionId) }
        : study),
    }))
  }

  async function handleAnswerQuestion(questionId: string, answer: string) {
    if (!answer.trim()) return
    try {
      const res = await fetch(`/api/admin-questions/${questionId}/answer`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ answer: answer.trim() }),
      })
      if (!res.ok) throw new Error('Failed to answer question')
      setQuestions(qs =>
        qs.map(q =>
          q.id === questionId
            ? { ...q, answer: answer.trim(), answeredAt: new Date().toISOString() }
            : q
        )
      )
      setEditingQuestionId(null)
      setAnsweredQuestions(a => ({ ...a, [questionId]: '' }))
    } catch {
      alert('Failed to save answer')
    }
  }

  async function handleApproveQuestion(questionId: string, approved: boolean) {
    try {
      const res = await fetch(`/api/admin-questions/${questionId}/approve`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ approved }),
      })
      if (!res.ok) throw new Error('Failed to update question')
      setQuestions(qs =>
        qs.map(q =>
          q.id === questionId
            ? { ...q, isApproved: approved, approvedAt: approved ? new Date().toISOString() : null }
            : q
        )
      )
    } catch {
      alert('Failed to update question')
    }
  }

  async function handlePinQuestion(questionId: string, pin: boolean) {
    try {
      const method = pin ? 'POST' : 'DELETE'
      const res = await fetch(`/api/admin-questions/${questionId}/pin`, { method })
      if (!res.ok) throw new Error('Failed to update pin')
      const data = await res.json().catch(() => ({})) as { question?: Question }
      if (data.question) {
        setQuestions(items => items.map(q => q.id === questionId ? { ...q, pinned: data.question!.pinned } : q))
      }
    } catch (err) {
      console.error(err)
    }
  }

  async function loadQrCodes() {
    setQrLoading(true)
    setQrMsg('')
    try {
      const res = await fetch('/api/admin/qr-codes')
      const data = await res.json()
      setQrCodes(Array.isArray(data.codes) ? data.codes : [])
      setQrScans(Array.isArray(data.scans) ? data.scans : [])
      setQrLoaded(true)
    } catch { setQrMsg('Failed to load QR codes.') }
    finally { setQrLoading(false) }
  }

  async function handleCreateQrCode() {
    setQrMsg('')
    try {
      const res = await fetch('/api/admin/qr-codes', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ label: qrNewLabel, slug: qrNewSlug, destination: qrNewDest }),
      })
      const data = await res.json()
      if (!res.ok) { setQrMsg(data.message || 'Failed to create QR code.'); return }
      setQrCodes(prev => [...prev, data.code])
      setQrNewLabel(''); setQrNewSlug(''); setQrNewDest('')
    } catch { setQrMsg('Failed to create QR code.') }
  }

  async function handleSaveQrEdit(id: string) {
    setQrMsg('')
    try {
      const res = await fetch(`/api/admin/qr-codes/${id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ label: qrEditLabel, destination: qrEditDest }),
      })
      const data = await res.json()
      if (!res.ok) { setQrMsg(data.message || 'Failed to save.'); return }
      setQrCodes(prev => prev.map(c => c.id === id ? { ...c, ...data.code } : c))
      setQrEditId(null)
    } catch { setQrMsg('Failed to save.') }
  }

  async function handleToggleQrActive(id: string, active: boolean) {
    try {
      await fetch(`/api/admin/qr-codes/${id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ active }),
      })
      setQrCodes(prev => prev.map(c => c.id === id ? { ...c, active } : c))
    } catch { /* ignore */ }
  }

  async function handleDeleteQrCode(id: string) {
    if (!confirm('Delete this QR code and all its scan history?')) return
    try {
      await fetch(`/api/admin/qr-codes/${id}`, { method: 'DELETE' })
      setQrCodes(prev => prev.filter(c => c.id !== id))
      setQrScans(prev => prev.filter(s => s.qrId !== id))
    } catch { /* ignore */ }
  }

  async function handleClearQrScans(id: string) {
    if (!confirm('Clear all scan history for this QR code?')) return
    try {
      await fetch(`/api/admin/qr-codes/${id}/scans`, { method: 'DELETE' })
      setQrScans(prev => prev.filter(s => s.qrId !== id))
      setQrCodes(prev => prev.map(c => c.id === id ? { ...c, scanCount: 0 } : c))
    } catch { /* ignore */ }
  }

  async function loadStudyComments() {
    setCommentsLoading(true)
    try {
      const res = await fetch('/api/admin-study-comments')
      const data = await res.json()
      setStudyComments(Array.isArray(data.comments) ? data.comments : [])
      setCommentsLoaded(true)
    } catch { /* ignore */ }
    finally { setCommentsLoading(false) }
  }

  async function handleApproveComment(commentId: string) {
    try {
      const res = await fetch(`/api/admin-study-comments/${commentId}/approve`, { method: 'POST' })
      if (!res.ok) return
      setStudyComments(prev => prev.map(c => c.id === commentId ? { ...c, isApproved: true, approvedAt: new Date().toISOString() } : c))
    } catch { /* ignore */ }
  }

  async function handleDeleteComment(commentId: string) {
    if (!confirm('Delete this comment permanently?')) return
    try {
      const res = await fetch(`/api/admin-study-comments/${commentId}`, { method: 'DELETE' })
      if (!res.ok) return
      setStudyComments(prev => prev.filter(c => c.id !== commentId))
    } catch { /* ignore */ }
  }

  async function loadScriptsList() {
    setScriptsLoading(true)
    try {
      const res = await fetch('/api/admin-episode-scripts')
      const data = await res.json()
      setScriptsList(Array.isArray(data.scripts) ? data.scripts : [])
    } catch { /* ignore */ }
    finally { setScriptsLoading(false) }
  }

  async function handleScriptUpload() {
    if (!scriptUploadEpisode.trim() || !/^\d+$/.test(scriptUploadEpisode.trim())) {
      setScriptUploadMsg('Enter a valid episode number.'); return
    }
    if (!scriptUploadFile) { setScriptUploadMsg('Select a .docx file.'); return }
    setScriptUploadBusy(true)
    setScriptUploadMsg('')
    try {
      const reader = new FileReader()
      const dataUrl = await new Promise<string>((resolve, reject) => {
        reader.onload = e => resolve(e.target?.result as string)
        reader.onerror = reject
        reader.readAsDataURL(scriptUploadFile)
      })
      const res = await fetch(`/api/admin-episode-scripts/${encodeURIComponent(scriptUploadEpisode.trim())}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ dataUrl, filename: scriptUploadFile.name, title: scriptUploadTitle.trim() || `Episode ${scriptUploadEpisode.trim()}` }),
      })
      const data = await res.json()
      if (!res.ok) { setScriptUploadMsg(data.message ?? 'Upload failed.'); return }
      setScriptUploadMsg(`✓ Episode ${scriptUploadEpisode} uploaded — ${data.wordCount?.toLocaleString()} words indexed.`)
      setScriptUploadEpisode('')
      setScriptUploadTitle('')
      setScriptUploadFile(null)
      loadScriptsList()
    } catch {
      setScriptUploadMsg('Upload failed. Please try again.')
    } finally {
      setScriptUploadBusy(false)
    }
  }

  async function handleDeleteScript(episodeNumber: string) {
    if (!confirm(`Remove script for Episode ${episodeNumber}?`)) return
    try {
      await fetch(`/api/admin-episode-scripts/${encodeURIComponent(episodeNumber)}`, { method: 'DELETE' })
      setScriptsList(prev => prev.filter(s => s.episodeNumber !== episodeNumber))
    } catch { alert('Failed to delete script.') }
  }

  async function handleDeleteQuestion(questionId: string) {
    if (!confirm('Delete this question permanently?')) return
    try {
      const res = await fetch(`/api/admin-questions/${questionId}`, { method: 'DELETE' })
      if (!res.ok) throw new Error('Failed to delete question')
      setQuestions(qs => qs.filter(q => q.id !== questionId))
      setSelectedQuestionIds(prev => { const next = new Set(prev); next.delete(questionId); return next })
    } catch {
      alert('Failed to delete question')
    }
  }

  async function handleBulkDeleteQuestions() {
    if (selectedQuestionIds.size === 0) return
    if (!confirm(`Delete ${selectedQuestionIds.size} question${selectedQuestionIds.size === 1 ? '' : 's'} permanently?`)) return
    const ids = Array.from(selectedQuestionIds)
    let deletedCount = 0
    for (const id of ids) {
      try {
        const res = await fetch(`/api/admin-questions/${id}`, { method: 'DELETE' })
        if (res.ok) {
          deletedCount++
          setQuestions(qs => qs.filter(q => q.id !== id))
        }
      } catch {
        // continue with remaining
      }
    }
    setSelectedQuestionIds(new Set())
    if (deletedCount < ids.length) alert(`Deleted ${deletedCount} of ${ids.length} questions.`)
  }

  async function handleCreateManualQuestion() {
    if (!manualQuestion.firstName.trim() || !manualQuestion.question.trim()) {
      setManualQuestionStatus('error')
      setManualQuestionMsg('First name and question are required.')
      return
    }

    setManualQuestionStatus('saving')
    setManualQuestionMsg('')

    try {
      const res = await fetch('/api/admin-questions', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          firstName: manualQuestion.firstName,
          email: manualQuestion.email,
          question: manualQuestion.question,
          answer: manualQuestion.answer,
          approve: manualQuestion.approve,
        }),
      })

      const data = await res.json().catch(() => ({})) as { question?: Question; message?: string }
      if (!res.ok || !data.question) {
        throw new Error(data.message ?? 'Failed to add question.')
      }

      setQuestions(items => [data.question as Question, ...items])
      setManualQuestion({ firstName: '', email: '', question: '', answer: '', approve: false })
      setManualQuestionStatus('saved')
      setManualQuestionMsg('Question added to draft Q&A list.')
    } catch (err) {
      setManualQuestionStatus('error')
      setManualQuestionMsg(err instanceof Error ? err.message : 'Failed to add question.')
    }
  }

  const resourceLinks = (form.customLinks ?? []).filter(link => link.placement === 'resources')

  const filteredAdminQuestions = questions.filter(question => {
    const search = questionSearch.trim().toLowerCase()
    const matchesSearch = !search
      || question.firstName.toLowerCase().includes(search)
      || question.question.toLowerCase().includes(search)
      || question.answer.toLowerCase().includes(search)

    const matchesFilter = (
      questionFilter === 'all'
      || (questionFilter === 'pending' && !question.isApproved)
      || (questionFilter === 'approved' && question.isApproved)
      || (questionFilter === 'answered' && Boolean(question.answer?.trim()))
      || (questionFilter === 'unanswered' && !question.answer?.trim())
    )

    return matchesSearch && matchesFilter
  })

  const QUESTION_PAGE_SIZE = 20
  const totalQuestionPages = Math.max(1, Math.ceil(filteredAdminQuestions.length / QUESTION_PAGE_SIZE))
  const visibleAdminQuestions = filteredAdminQuestions.slice(
    questionPage * QUESTION_PAGE_SIZE,
    (questionPage + 1) * QUESTION_PAGE_SIZE,
  )
  function renderSaveStatus() {
    return (
      <>
        {status === 'saved' && <p className="admin-status admin-status--ok">✓ Changes saved.</p>}
        {status === 'error' && <p className="admin-status admin-status--err">✗ {errorMsg}</p>}
      </>
    )
  }

  return (
    <div className="admin-shell">
      {/* ── Top bar ── */}
      <header className="admin-topbar">
        <div className="admin-topbar-left">
          <button
            type="button"
            className={`admin-mobile-menu-btn${mobileNavOpen ? ' admin-mobile-menu-btn--open' : ''}`}
            onClick={() => setMobileNavOpen(open => !open)}
            aria-expanded={mobileNavOpen}
            aria-controls="admin-sidebar-nav"
          >
            {mobileNavOpen ? 'Close' : 'Menu'}
          </button>
          <div className="admin-topbar-brand">
            <span className="admin-topbar-ornament">✦</span>
            <span className="admin-topbar-title">Site Admin</span>
            <span className="admin-topbar-sub">Verse by Verse with Nate</span>
          </div>
        </div>
        <div className="admin-topbar-state">
          {isDirty && <span className="admin-status admin-status--warn">● Unsaved</span>}
          <span className="admin-topbar-meta">Draft: {formatDate(publishState.draftUpdatedAt)}</span>
          <span className="admin-topbar-meta">Live: {formatDate(publishState.publishedAt)}</span>
        </div>
        <div className="admin-topbar-actions">
          <label className="admin-view-picker" aria-label="Jump to admin section">
            <span className="admin-view-picker-label">Jump to</span>
            <select
              className="admin-view-picker-select"
              value={adminView}
              onChange={e => navigateTo(e.target.value as AdminView)}
            >
              {ADMIN_VIEW_OPTIONS.map(group => (
                <optgroup key={group.group} label={group.group}>
                  {group.options.map(option => (
                    <option key={option.value} value={option.value}>
                      {option.label}
                    </option>
                  ))}
                </optgroup>
              ))}
            </select>
          </label>
          <button type="button" className="btn-admin-save" onClick={handleSaveDraft} disabled={status === 'saving' || !isDirty}>
            {status === 'saving' ? 'Saving…' : 'Save Draft'}
          </button>
          <button type="button" className={`btn-admin-reset${previewOpen ? ' btn-admin-reset--active' : ''}`} onClick={() => setPreviewOpen(o => !o)}>
            {previewOpen ? 'Close Preview' : 'Preview'}
          </button>
          {publishConfirmOpen ? (
            <>
              <span style={{ fontSize: '0.82rem', color: '#e0c070' }}>Publish to live site?</span>
              <button type="button" className="btn-admin-save" onClick={() => { setPublishConfirmOpen(false); handlePublishDraft() }}>Yes, Publish</button>
              <button type="button" className="btn-admin-reset" onClick={() => setPublishConfirmOpen(false)}>Cancel</button>
            </>
          ) : (
            <button type="button" className="btn-admin-reset" onClick={() => setPublishConfirmOpen(true)}>Publish</button>
          )}
        </div>
      </header>

      <div className={`admin-layout${previewOpen ? ' admin-layout--split' : ''}`}>
        {/* ── Sidebar ── */}
        <nav id="admin-sidebar-nav" className={`admin-sidebar${mobileNavOpen ? ' admin-sidebar--open' : ''}`} aria-label="Admin navigation">
          <div className="admin-sidebar-meta-links">
            <Link
              to="/"
              className="admin-meta-link"
              onClick={e => {
                if (!confirmLeaveUnsavedChanges()) e.preventDefault()
                else setMobileNavOpen(false)
              }}
            >
              ← Back to site
            </Link>
            <button
              type="button"
              className="admin-meta-link"
              onClick={() => {
                if (!confirmLeaveUnsavedChanges()) return
                setMobileNavOpen(false)
                void onLogout()
              }}
            >
              Log Out
            </button>
          </div>
          {appVersion && (
            <div className="admin-sidebar-version">{appVersion}</div>
          )}

          <div className="admin-nav-search">
            <label htmlFor="admin-nav-search-input">Find a section</label>
            <input
              id="admin-nav-search-input"
              type="search"
              value={navSearch}
              onChange={e => setNavSearch(e.target.value)}
              placeholder="Search admin..."
            />
          </div>

          {filteredAdminViewOptions.length === 0 ? (
            <p className="admin-stats-note admin-nav-empty">No admin sections match this search.</p>
          ) : (
            filteredAdminViewOptions.map(group => (
              <div key={group.group} className="admin-nav-group">
                <span className="admin-nav-label">{group.group}</span>
                {group.options.map(option => renderAdminNavItem(option.value, option.label))}
              </div>
            ))
          )}
        </nav>

        {mobileNavOpen && <button type="button" className="admin-sidebar-backdrop" aria-label="Close navigation menu" onClick={() => setMobileNavOpen(false)} />}

        {/* ── Content panel ── */}
        <main className="admin-panel">

          {/* DASHBOARD */}
          {adminView === 'dashboard' && (() => {
            const thisWeekHits = stats?.last7Days?.reduce((s, d) => s + d.hits, 0) ?? 0
            const thisWeekReal = stats?.last7DaysReal?.reduce((s, d) => s + d.hits, 0) ?? 0
            const dashboardHour = dashboardNow.getHours()
            const welcomeMessage = dashboardHour < 12
              ? 'Good morning.'
              : dashboardHour < 18
                ? 'Good afternoon.'
                : 'Good evening.'
            const dashboardDateLabel = dashboardNow.toLocaleDateString(undefined, {
              weekday: 'long',
              month: 'long',
              day: 'numeric',
            })
            const dashboardTimeLabel = dashboardNow.toLocaleTimeString(undefined, {
              hour: 'numeric',
              minute: '2-digit',
              second: '2-digit',
            })
            const recentContacts = [...contactSubmissions]
              .sort((a, b) => new Date(b.submittedAt).getTime() - new Date(a.submittedAt).getTime())
              .slice(0, 5)
            const recentQuestions = [...questions]
              .sort((a, b) => new Date(b.submittedAt ?? '').getTime() - new Date(a.submittedAt ?? '').getTime())
              .slice(0, 5)
            const approvedCount = questions.filter(q => q.isApproved).length
            const answeredCount = questions.filter(q => !!q.answer?.trim()).length
            const topEnrollment = stats?.studyEnrollment?.enrollmentsByStudy?.[0]
            return (
              <section className="admin-panel-section">
                <div className="admin-panel-head">
                  <h2>Dashboard</h2>
                  <p>Quick overview of your ministry site.</p>
                </div>

                <div className="admin-dashboard-welcome">
                  <div>
                    <h3 className="admin-dashboard-welcome-title">{welcomeMessage}</h3>
                    <p className="admin-dashboard-welcome-copy">Here&apos;s what needs your attention and how the site is performing today.</p>
                  </div>
                  <div className="admin-dashboard-clock" aria-label={`Current time ${dashboardTimeLabel} on ${dashboardDateLabel}`}>
                    <span className="admin-dashboard-clock-time">{dashboardTimeLabel}</span>
                    <span className="admin-dashboard-clock-date">{dashboardDateLabel}</span>
                  </div>
                </div>

                <div className="admin-dashboard-grid">
                  <div className={`admin-dashboard-card${unansweredCount > 0 ? ' admin-dashboard-card--alert' : ''}`}>
                    <div className="admin-dashboard-card-value">{unansweredCount}</div>
                    <div className="admin-dashboard-card-label">Unanswered Questions</div>
                    <div className="admin-dashboard-card-sub">{answeredCount} answered · {approvedCount} approved of {questions.length}</div>
                    {unansweredCount > 0 && <button type="button" className="admin-dashboard-card-action" onClick={() => { setQuestionFilter('unanswered'); navigateTo('questions') }}>Answer Now →</button>}
                  </div>
                  <div className={`admin-dashboard-card${unreadEmailCount > 0 ? ' admin-dashboard-card--alert' : ''}`}>
                    <div className="admin-dashboard-card-value">{unreadEmailCount}</div>
                    <div className="admin-dashboard-card-label">Inbox Messages</div>
                    <div className="admin-dashboard-card-sub">{contactSubmissions.filter(s => s.archived).length} archived · {contactSubmissions.length} total</div>
                    <Link to="/email" className="admin-dashboard-card-action" style={{ textDecoration: 'none' }}>Open Inbox →</Link>
                  </div>
                  <div className="admin-dashboard-card">
                    <div className="admin-dashboard-card-value">{thisWeekReal.toLocaleString()}</div>
                    <div className="admin-dashboard-card-label">Real Visits (7 Days)</div>
                    <div className="admin-dashboard-card-sub">{thisWeekHits.toLocaleString()} total · {stats?.visitors?.uniqueVisitors?.toLocaleString() ?? '—'} unique all time</div>
                    <button type="button" className="admin-dashboard-card-action" onClick={() => navigateTo('analytics')}>Full Analytics →</button>
                  </div>
                  <div className="admin-dashboard-card">
                    <div className="admin-dashboard-card-value">{subscribers.length}</div>
                    <div className="admin-dashboard-card-label">Email Subscribers</div>
                    <div className="admin-dashboard-card-sub">{contactSubmissions.length} total contact submissions</div>
                    <button type="button" className="admin-dashboard-card-action" onClick={() => navigateTo('subscribers')}>View List →</button>
                  </div>
                  <div className="admin-dashboard-card">
                    <div className="admin-dashboard-card-value">{stats?.studyEnrollment?.totalEnrollments ?? 0}</div>
                    <div className="admin-dashboard-card-label">Study Enrollments</div>
                    <div className="admin-dashboard-card-sub">
                      {stats?.studyEnrollment?.enrolledUsers ?? 0} of {stats?.studyEnrollment?.totalUsers ?? 0} students enrolled
                      {topEnrollment ? ` · Top: ${topEnrollment.title} (${topEnrollment.count})` : ''}
                    </div>
                  </div>
                  <div className="admin-dashboard-card">
                    <div className="admin-dashboard-card-value">{downloadStats['titus-study'] ?? 0}</div>
                    <div className="admin-dashboard-card-label">Titus Study Downloads</div>
                    <div className="admin-dashboard-card-sub">
                      {Object.values(downloadStats).reduce((a, b) => a + b, 0)} total resource downloads
                    </div>
                  </div>
                  {publishState?.publishedAt && (
                    <div className="admin-dashboard-card">
                      <div className="admin-dashboard-card-value" style={{ fontSize: '1rem', marginTop: '0.25rem' }}>{formatDate(publishState.publishedAt)}</div>
                      <div className="admin-dashboard-card-label">Last Published</div>
                      {publishState.draftUpdatedAt && <div className="admin-dashboard-card-sub">Draft updated {formatDate(publishState.draftUpdatedAt)}</div>}
                    </div>
                  )}
                </div>

                <div className="admin-dashboard-activity">
                  <div className="admin-dashboard-activity-col">
                    <h3 className="admin-dashboard-activity-heading">Recent Contacts</h3>
                    {recentContacts.length === 0
                      ? <p className="admin-stats-note">No contacts yet.</p>
                      : (
                        <div className="admin-dashboard-feed">
                          {recentContacts.map(c => (
                            <div key={c.id} className="admin-dashboard-feed-item">
                              <div className="admin-dashboard-feed-meta">
                                <strong>{c.name}</strong>
                                <span className="admin-dashboard-feed-date">{formatDate(c.submittedAt)}</span>
                              </div>
                              <div className="admin-dashboard-feed-email">{c.email}</div>
                              {c.message && <div className="admin-dashboard-feed-preview">{c.message.slice(0, 100)}{c.message.length > 100 ? '…' : ''}</div>}
                            </div>
                          ))}
                          <Link to="/contacts" className="admin-dashboard-card-action" style={{ marginTop: '0.5rem', display: 'inline-block' }}>View All Contacts →</Link>
                        </div>
                      )
                    }
                  </div>
                  <div className="admin-dashboard-activity-col">
                    <h3 className="admin-dashboard-activity-heading">Recent Questions</h3>
                    {recentQuestions.length === 0
                      ? <p className="admin-stats-note">No questions yet.</p>
                      : (
                        <div className="admin-dashboard-feed">
                          {recentQuestions.map(q => (
                            <div key={q.id} className="admin-dashboard-feed-item">
                              <div className="admin-dashboard-feed-meta">
                                <strong>{q.firstName}</strong>
                                <span className={`admin-badge ${q.isApproved ? 'admin-badge--approved' : 'admin-badge--pending'}`} style={{ fontSize: '0.65rem' }}>{q.isApproved ? 'Approved' : 'Pending'}</span>
                                <span className="admin-dashboard-feed-date">{formatDate(q.submittedAt ?? '')}</span>
                              </div>
                              <div className="admin-dashboard-feed-preview">{q.question.slice(0, 100)}{q.question.length > 100 ? '…' : ''}</div>
                            </div>
                          ))}
                          <button type="button" className="admin-dashboard-card-action" style={{ marginTop: '0.5rem' }} onClick={() => navigateTo('questions')}>View All Questions →</button>
                        </div>
                      )
                    }
                  </div>
                </div>

                <div className="admin-dashboard-activity" style={{ marginTop: '1rem' }}>
                  <div className="admin-dashboard-activity-col" style={{ gridColumn: '1 / -1' }}>
                    <h3 className="admin-dashboard-activity-heading">Study Users &amp; Enrollments</h3>
                    {stats?.studyEnrollment?.users?.length
                      ? (
                        <div className="admin-dashboard-feed">
                          {stats.studyEnrollment.users.map(user => (
                            <div key={user.id} className="admin-dashboard-feed-item">
                              <div className="admin-dashboard-feed-meta">
                                <strong>{user.displayName || user.username}</strong>
                                {user.displayName && <span className="admin-dashboard-feed-email">{user.username}</span>}
                              </div>
                              <div className="admin-dashboard-feed-preview">
                                {user.enrolledStudies.length
                                  ? `Enrolled in: ${user.enrolledStudies.map(study => study.title).join(', ')}`
                                  : 'Not enrolled in any studies yet.'}
                              </div>
                            </div>
                          ))}
                        </div>
                      )
                      : <p className="admin-stats-note">No study users yet.</p>
                    }
                  </div>
                </div>
              </section>
            )
          })()}

          {/* SUBSCRIBERS */}
          {adminView === 'subscribers' && (() => {
            const filteredSubs = subscribers.filter(s =>
              !subscriberSearch.trim() ||
              s.name.toLowerCase().includes(subscriberSearch.toLowerCase()) ||
              s.email.toLowerCase().includes(subscriberSearch.toLowerCase())
            )
            return (
              <section className="admin-panel-section">
                <div className="admin-panel-head">
                  <h2>Subscribers</h2>
                  <p>{subscribers.length} people have opted in to email updates.</p>
                </div>
                <div className="admin-toolbar" style={{ marginBottom: '1rem', display: 'flex', gap: '0.75rem', alignItems: 'center', flexWrap: 'wrap' }}>
                  <input
                    type="search"
                    placeholder="Search by name or email…"
                    value={subscriberSearch}
                    onChange={e => setSubscriberSearch(e.target.value)}
                    style={{ minWidth: '240px', maxWidth: '400px', width: '100%' }}
                  />
                  <form method="post" action="/api/admin-subscribers/export" style={{ display: 'inline' }}>
                    <button type="submit" className="btn-admin-reset">Export CSV</button>
                  </form>
                </div>
                {filteredSubs.length === 0 ? (
                  <p className="admin-stats-note">No subscribers{subscriberSearch ? ' match your search' : ' yet'}.</p>
                ) : (
                  <div className="admin-visits-table-wrap">
                    <table className="admin-visits-table">
                      <thead>
                        <tr>
                          <th>Name</th>
                          <th>Email</th>
                          <th>Source</th>
                          <th>Subscribed</th>
                        </tr>
                      </thead>
                      <tbody>
                        {filteredSubs.map((sub, i) => (
                          <tr key={`${sub.email}-${i}`}>
                            <td>{sub.name}</td>
                            <td><a href={`mailto:${sub.email}`}>{sub.email}</a></td>
                            <td>{sub.source === 'download' ? 'Download' : 'Contact Form'}</td>
                            <td>{formatDate(sub.subscribedAt)}</td>
                          </tr>
                        ))}
                      </tbody>
                    </table>
                  </div>
                )}
              </section>
            )
          })()}

          {/* HOMEPAGE */}
          {adminView === 'homepage' && (
            <section className="admin-panel-section">
              <div className="admin-panel-head">
                <h2>Homepage</h2>
                <p>Controls the hero, share section, PRISM video block, and "Where to Next" navigation cards.</p>
              </div>

              {renderSectionJumpNav('homepage')}

              <div className="admin-panel-subhead" id="homepage-hero">
                <h3>Hero</h3>
                <p>Primary landing page headline, tag line, and hero call-to-action labels.</p>
              </div>
              {FIELDS.filter(f => f.section === 'hero').map(({ key, label, multiline }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  {multiline
                    ? <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={4} />
                    : <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />}
                  {(key.includes('ArtUrl') || key.includes('ImageUrl')) && renderImageAssetSelector(form[key] as string, value => handleChange(key, value), `field-${key}-asset`)}
                  {(key.includes('ArtUrl') || key.includes('ImageUrl')) && renderImagePreview(form[key] as string, `${label} preview`)}
                </div>
              ))}

              <div className="admin-panel-subhead" id="homepage-share">
                <h3>Share Section</h3>
                <p>Controls the share block that points visitors toward the next step.</p>
              </div>
              {FIELDS.filter(f => f.section === 'share').map(({ key, label, multiline }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  {multiline
                    ? <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={4} />
                    : <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />}
                  {key.includes('ImageUrl') && renderImageAssetSelector(form[key] as string, value => handleChange(key, value), `field-${key}-asset`)}
                  {key.includes('ImageUrl') && renderImagePreview(form[key] as string, `${label} preview`)}
                </div>
              ))}

              <div className="admin-panel-subhead" id="homepage-prism">
                <h3>PRISM Block</h3>
                <p>Video URL, intro text, pattern steps, and closing copy for the PRISM section.</p>
              </div>
              {FIELDS.filter(f => f.section === 'prism').map(({ key, label, multiline }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  {multiline
                    ? <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={4} />
                    : <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />}
                </div>
              ))}

              <div className="admin-panel-subhead" id="homepage-where-to-next-labels">
                <h3>Where to Next — Section Labels</h3>
              </div>
              <div className="admin-field">
                <label htmlFor="field-whereToNextEyebrow">Eyebrow</label>
                <input id="field-whereToNextEyebrow" type="text" value={form.whereToNextEyebrow ?? ''} onChange={e => setForm(f => ({ ...f, whereToNextEyebrow: e.target.value }))} />
              </div>
              <div className="admin-field">
                <label htmlFor="field-whereToNextHeading">Heading</label>
                <input id="field-whereToNextHeading" type="text" value={form.whereToNextHeading ?? ''} onChange={e => setForm(f => ({ ...f, whereToNextHeading: e.target.value }))} />
              </div>

              <div className="admin-panel-subhead" id="homepage-where-to-next-cards">
                <h3>Where to Next — Navigation Cards</h3>
                <p>Edit the title, description, and link path for each card.</p>
              </div>
              {(form.whereToNextCards ?? []).map((card, idx) => (
                <div key={card.id} className="admin-array-row">
                  <div className="admin-array-fields">
                    <div className="admin-field">
                      <label htmlFor={`wtn-title-${card.id}`}>Card {idx + 1} — Title</label>
                      <input id={`wtn-title-${card.id}`} type="text" value={card.title} onChange={e => setForm(f => ({ ...f, whereToNextCards: (f.whereToNextCards ?? []).map(c => c.id === card.id ? { ...c, title: e.target.value } : c) }))} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`wtn-desc-${card.id}`}>Card {idx + 1} — Description</label>
                      <input id={`wtn-desc-${card.id}`} type="text" value={card.description} onChange={e => setForm(f => ({ ...f, whereToNextCards: (f.whereToNextCards ?? []).map(c => c.id === card.id ? { ...c, description: e.target.value } : c) }))} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`wtn-path-${card.id}`}>Card {idx + 1} — Link Path</label>
                      <input id={`wtn-path-${card.id}`} type="text" value={card.path} onChange={e => setForm(f => ({ ...f, whereToNextCards: (f.whereToNextCards ?? []).map(c => c.id === card.id ? { ...c, path: e.target.value } : c) }))} />
                    </div>
                  </div>
                  <button type="button" className="btn-admin-remove" onClick={() => setForm(f => ({ ...f, whereToNextCards: (f.whereToNextCards ?? []).filter(c => c.id !== card.id) }))}>Remove</button>
                </div>
              ))}
              <button
                type="button"
                className="btn-admin-add"
                onClick={() => setForm(f => ({ ...f, whereToNextCards: [...(f.whereToNextCards ?? []), { id: Date.now().toString(36), title: '', description: '', path: '/' }] }))}
              >
                + Add Card
              </button>
              {renderSaveStatus()}
            </section>
          )}

          {/* START HERE */}
          {adminView === 'start-here' && (
            <section className="admin-panel-section">
              <div className="admin-panel-head">
                <h2>Start Here Page</h2>
                <p>Edit the /start-here page — heading, intro, and the three step cards.</p>
              </div>

              {renderSectionJumpNav('start-here')}

              <div className="admin-panel-subhead" id="start-here-intro">
                <h3>Intro</h3>
                <p>Top-level heading and opening copy for the page.</p>
              </div>
              {FIELDS.filter(f => ['startHereHeading', 'startHereIntro'].includes(f.key)).map(({ key, label, multiline }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  {multiline
                    ? <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={4} />
                    : <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />}
                </div>
              ))}

              <div className="admin-panel-subhead" id="start-here-step-1">
                <h3>Step 1</h3>
              </div>
              {FIELDS.filter(f => ['startHereStep1Title', 'startHereStep1Body', 'startHereStep1Cta'].includes(f.key)).map(({ key, label, multiline }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  {multiline
                    ? <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={4} />
                    : <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />}
                </div>
              ))}

              <div className="admin-panel-subhead" id="start-here-step-2">
                <h3>Step 2</h3>
              </div>
              {FIELDS.filter(f => ['startHereStep2Title', 'startHereStep2Body', 'startHereStep2Cta'].includes(f.key)).map(({ key, label, multiline }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  {multiline
                    ? <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={4} />
                    : <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />}
                </div>
              ))}

              <div className="admin-panel-subhead" id="start-here-step-3">
                <h3>Step 3</h3>
              </div>
              {FIELDS.filter(f => ['startHereStep3Title', 'startHereStep3Body', 'startHereStep3Cta'].includes(f.key)).map(({ key, label, multiline }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  {multiline
                    ? <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={4} />
                    : <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />}
                </div>
              ))}
              {renderSaveStatus()}
            </section>
          )}

          {/* ABOUT */}
          {adminView === 'about' && (
            <section className="admin-panel-section">
              <div className="admin-panel-head">
                <h2>About</h2>
                <p>Manage the main show description, Nate bio, and about images.</p>
              </div>

              {renderSectionJumpNav('about')}

              <div className="admin-panel-subhead" id="about-show-copy">
                <h3>Show Copy</h3>
                <p>Top section content that explains the show and listening invitation.</p>
              </div>
              {FIELDS.filter(f => ['aboutShowHeading', 'aboutShowP1', 'aboutShowP2', 'aboutShowEyebrow', 'aboutListenBtnLabel'].includes(f.key)).map(({ key, label, multiline }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  {multiline ? (
                    <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={4} />
                  ) : (
                    <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />
                  )}
                </div>
              ))}

              <div className="admin-panel-subhead" id="about-nate-bio">
                <h3>Nate Bio</h3>
                <p>Profile eyebrow and biography copy for the About Nate block.</p>
              </div>
              {FIELDS.filter(f => ['aboutEyebrow', 'aboutNate'].includes(f.key)).map(({ key, label, multiline }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  {multiline ? (
                    <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={4} />
                  ) : (
                    <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />
                  )}
                </div>
              ))}

              <div className="admin-panel-subhead" id="about-images">
                <h3>Images</h3>
                <p>Portrait and scripture artwork used on the About page.</p>
              </div>
              {FIELDS.filter(f => ['aboutPhotoUrl', 'aboutVerseArtUrl'].includes(f.key)).map(({ key, label, multiline }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  {multiline ? (
                    <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={4} />
                  ) : (
                    <>
                      <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />
                      {(key.includes('ImageUrl') || key.includes('PhotoUrl') || key.includes('ArtUrl')) && renderImageAssetSelector(form[key] as string, value => handleChange(key, value), `field-${key}-asset`)}
                      {key === 'aboutPhotoUrl' && <p className="admin-field-note">This controls the portrait shown on the left side of the About page.</p>}
                      {key === 'aboutVerseArtUrl' && <p className="admin-field-note">This controls the scripture artwork shown under the “About the Show” text. It is separate from the Contact page image.</p>}
                      {key === 'aboutPhotoUrl' && renderImagePreview(form[key] as string, 'About page portrait preview')}
                      {key === 'aboutVerseArtUrl' && renderImagePreview(form[key] as string, 'About page scripture artwork preview')}
                    </>
                  )}
                </div>
              ))}
              {renderSaveStatus()}

              <div className="admin-panel-subhead" id="about-testimonials">
                <h3>Listener Testimonials</h3>
                <p>Quotes shown on the About page and homepage. Add Apple Podcast reviews, listener emails, or any kind words.</p>
              </div>
              {(form.testimonials ?? []).length === 0 && (
                <p className="admin-stats-note">No testimonials yet. Add one below.</p>
              )}
              {(form.testimonials ?? []).map(item => (
                <AdminCollapsibleCard
                  key={item.id}
                  title={item.name || 'Unnamed listener'}
                  subtitle={item.source || 'No source'}
                >
                  <div className="admin-array-row">
                    <div className="admin-array-fields">
                      <div className="admin-field">
                        <label htmlFor={`testimonial-quote-${item.id}`}>Quote</label>
                        <textarea id={`testimonial-quote-${item.id}`} rows={3} placeholder="What they said…" value={item.quote} onChange={e => setForm(f => ({ ...f, testimonials: (f.testimonials ?? []).map(t => t.id === item.id ? { ...t, quote: e.target.value } : t) }))} />
                      </div>
                      <div className="admin-field">
                        <label htmlFor={`testimonial-name-${item.id}`}>Name</label>
                        <input id={`testimonial-name-${item.id}`} type="text" placeholder="e.g. Sarah M." value={item.name} onChange={e => setForm(f => ({ ...f, testimonials: (f.testimonials ?? []).map(t => t.id === item.id ? { ...t, name: e.target.value } : t) }))} />
                      </div>
                      <div className="admin-field">
                        <label htmlFor={`testimonial-source-${item.id}`}>Source (optional)</label>
                        <input id={`testimonial-source-${item.id}`} type="text" placeholder="e.g. Apple Podcasts" value={item.source ?? ''} onChange={e => setForm(f => ({ ...f, testimonials: (f.testimonials ?? []).map(t => t.id === item.id ? { ...t, source: e.target.value } : t) }))} />
                      </div>
                    </div>
                    <button type="button" className="btn-admin-remove" onClick={() => setForm(f => ({ ...f, testimonials: (f.testimonials ?? []).filter(t => t.id !== item.id) }))}>Remove</button>
                  </div>
                </AdminCollapsibleCard>
              ))}
              <button type="button" className="btn-admin-add" onClick={() => setForm(f => ({ ...f, testimonials: [...(f.testimonials ?? []), { id: Date.now().toString(36), quote: '', name: '', source: '' }] }))}>
                + Add Testimonial
              </button>
            </section>
          )}

          {/* CONTACT */}
          {adminView === 'contact' && (
            <section className="admin-panel-section">
              <div className="admin-panel-head">
                <h2>Contact</h2>
                <p>Manage the contact page profile image and contact copy.</p>
              </div>

              {renderSectionJumpNav('contact')}

              <div className="admin-panel-subhead" id="contact-profile">
                <h3>Profile</h3>
                <p>Name, role, headline, and image shown at the top of the contact page.</p>
              </div>
              {FIELDS.filter(f => ['contactPhotoUrl', 'contactEyebrow', 'contactHeading', 'contactName', 'contactRole'].includes(f.key)).map(({ key, label, multiline }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  {multiline ? (
                    <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={4} />
                  ) : (
                    <>
                      <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />
                      {(key.includes('ImageUrl') || key.includes('PhotoUrl')) && renderImageAssetSelector(form[key] as string, value => handleChange(key, value), `field-${key}-asset`)}
                      {(key.includes('ImageUrl') || key.includes('PhotoUrl')) && renderImagePreview(form[key] as string, `${label} preview`)}
                    </>
                  )}
                </div>
              ))}

              <div className="admin-panel-subhead" id="contact-intro">
                <h3>Intro Copy</h3>
                <p>Intro paragraph, quote, and main contact bullet points.</p>
              </div>
              {FIELDS.filter(f => ['contactQuote', 'contactIntro', 'contactPoint1', 'contactPoint2'].includes(f.key)).map(({ key, label, multiline }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  {multiline ? (
                    <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={4} />
                  ) : (
                    <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />
                  )}
                </div>
              ))}

              <div className="admin-panel-subhead" id="contact-scripture">
                <h3>Scripture</h3>
              </div>
              {FIELDS.filter(f => ['contactVerse', 'contactVerseRef'].includes(f.key)).map(({ key, label, multiline }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  {multiline ? (
                    <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={4} />
                  ) : (
                    <>
                      <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />
                      {(key.includes('ImageUrl') || key.includes('PhotoUrl')) && renderImageAssetSelector(form[key] as string, value => handleChange(key, value), `field-${key}-asset`)}
                      {(key.includes('ImageUrl') || key.includes('PhotoUrl')) && renderImagePreview(form[key] as string, `${label} preview`)}
                    </>
                  )}
                </div>
              ))}
              {renderSaveStatus()}
            </section>
          )}

          {/* PODCAST HUB */}
          {adminView === 'podcast' && (
            <section className="admin-panel-section">
              <div className="admin-panel-head">
                <h2>Podcast Hub</h2>
                <p>Manage all podcast-related content from one place.</p>
              </div>
              <div className="admin-tabs admin-tabs--podcast">
                <button type="button" className={`admin-tab${podcastTab === 'current-series' ? ' admin-tab--active' : ''}`} onClick={() => setPodcastTab('current-series')}>Current Series</button>
                <button type="button" className={`admin-tab${podcastTab === 'episode-highlights' ? ' admin-tab--active' : ''}`} onClick={() => setPodcastTab('episode-highlights')}>Ep. Highlights</button>
                <button type="button" className={`admin-tab${podcastTab === 'finished-books' ? ' admin-tab--active' : ''}`} onClick={() => setPodcastTab('finished-books')}>Finished Books</button>
<button type="button" className={`admin-tab${podcastTab === 'episode-scripts' ? ' admin-tab--active' : ''}`} onClick={() => setPodcastTab('episode-scripts')}>Episode Scripts</button>
                <button type="button" className={`admin-tab${podcastTab === 'rss-feed' ? ' admin-tab--active' : ''}`} onClick={() => setPodcastTab('rss-feed')}>RSS Feed</button>
              </div>
            </section>
          )}

          {/* CURRENT SERIES */}
          {(adminView === 'current-series' || (adminView === 'podcast' && podcastTab === 'current-series')) && (
            <section className="admin-panel-section">
              <div className="admin-panel-head">
                <h2>Current Series</h2>
                <p>Update the active series card shown on the homepage and episodes page.</p>
              </div>
              {FIELDS.filter(f => f.section === 'series' && !['studyGuideTitle', 'studyGuideDescription', 'studyGuideUrl'].includes(f.key)).map(({ key, label, multiline }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  {multiline ? (
                    <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={4} />
                  ) : (
                    <>
                      <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />
                      {(key.includes('ImageUrl') || key.includes('PhotoUrl')) && renderImageAssetSelector(form[key] as string, value => handleChange(key, value), `field-${key}-asset`)}
                      {(key.includes('ImageUrl') || key.includes('PhotoUrl')) && renderImagePreview(form[key] as string, `${label} preview`)}
                    </>
                  )}
                </div>
              ))}
              <p className="admin-stats-note">Companion study guide title, description, and Amazon link are now managed in Downloads.</p>
              {renderSaveStatus()}
              <div className="admin-end-series-wrap">
                <hr style={{ border: 'none', borderTop: '1px solid #2a2518', margin: '2rem 0 1.5rem' }} />
                {!endSeriesOpen ? (
                  <button type="button" className="btn-danger-outline" onClick={() => setEndSeriesOpen(true)}>
                    End Current Series &amp; Start New Book
                  </button>
                ) : (
                  <div className="admin-end-series-panel">
                    <h3 style={{ marginBottom: '0.5rem' }}>Archive &ldquo;{form.seriesTitle}&rdquo; to Finished Books</h3>
                    <p className="admin-stats-note" style={{ marginBottom: '1rem' }}>
                      This will save the current series as a finished book and clear Episode Highlights. Fill in the new series details below, then confirm.
                    </p>
                    <div className="admin-field">
                      <label>New Series Title</label>
                      <input type="text" placeholder="e.g. Study of Romans" value={endSeriesNewTitle} onChange={e => setEndSeriesNewTitle(e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label>New Series Image URL</label>
                      <input type="text" placeholder="/images/romans-cover.png" value={endSeriesNewImage} onChange={e => setEndSeriesNewImage(e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label>New Season Number</label>
                      <input type="number" placeholder="2" value={endSeriesNewSeason} onChange={e => setEndSeriesNewSeason(e.target.value)} />
                    </div>
                    <div style={{ display: 'flex', gap: '0.75rem', marginTop: '1rem' }}>
                      <button
                        type="button"
                        className="btn-primary"
                        disabled={!endSeriesNewTitle.trim() || !endSeriesNewSeason.trim()}
                        onClick={() => {
                          const archivedBook = {
                            id: Date.now().toString(36),
                            title: form.seriesTitle,
                            description: form.seriesDescription,
                            imageUrl: form.seriesImageUrl,
                            season: parseInt(endSeriesNewSeason) - 1 || 1,
                          }
                          setForm(f => ({
                            ...f,
                            finishedBooks: [...(f.finishedBooks ?? []), archivedBook],
                            podcastFeaturedLinks: [],
                            seriesTitle: endSeriesNewTitle.trim(),
                            seriesImageUrl: endSeriesNewImage.trim() || f.seriesImageUrl,
                            seriesLabel: 'Now Playing',
                          }))
                          setEndSeriesOpen(false)
                          setEndSeriesNewTitle('')
                          setEndSeriesNewImage('')
                          setEndSeriesNewSeason('')
                        }}
                      >
                        Confirm &amp; Archive
                      </button>
                      <button type="button" className="btn-secondary" onClick={() => setEndSeriesOpen(false)}>Cancel</button>
                    </div>
                  </div>
                )}
              </div>
            </section>
          )}

          {/* EPISODE HIGHLIGHTS */}
          {(adminView === 'episode-highlights' || (adminView === 'podcast' && podcastTab === 'episode-highlights')) && (
            <section className="admin-panel-section" aria-label="Episode highlights">
              <div className="admin-panel-head">
                <h2>Episode Highlights</h2>
                <p>Featured episodes shown on the Episodes page. Fill in discussion questions, show notes, or an embed URL and the highlight automatically gets its own detail page at <code>/episodes/[id]</code>.</p>
              </div>
              {(form.podcastFeaturedLinks ?? []).length === 0 && (
                <p className="admin-stats-note">No episode highlights yet. Add one below.</p>
              )}
              {(form.podcastFeaturedLinks ?? []).map(item => (
                <AdminCollapsibleCard
                  key={item.id}
                  title={item.title || 'Untitled episode highlight'}
                  subtitle={item.episodeNumber ? `Episode ${item.episodeNumber}` : 'No episode number yet'}
                >
                  <div className="admin-array-row">
                      <div className="admin-array-fields">
                        <div className="admin-field">
                          <label htmlFor={`podcast-ep-${item.id}`}>Episode Number</label>
                          <input id={`podcast-ep-${item.id}`} type="text" placeholder="e.g. 42" value={item.episodeNumber ?? ''} onChange={e => updatePodcastLink(item.id, 'episodeNumber', e.target.value)} />
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`podcast-title-${item.id}`}>Title</label>
                          <input id={`podcast-title-${item.id}`} type="text" value={item.title} onChange={e => updatePodcastLink(item.id, 'title', e.target.value)} />
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`podcast-summary-${item.id}`}>Summary</label>
                          <textarea id={`podcast-summary-${item.id}`} rows={2} value={item.summary} onChange={e => updatePodcastLink(item.id, 'summary', e.target.value)} />
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`podcast-url-${item.id}`}>Platform URL (external link)</label>
                          <input id={`podcast-url-${item.id}`} type="text" placeholder="https://open.spotify.com/..." value={item.url} onChange={e => updatePodcastLink(item.id, 'url', e.target.value)} />
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`podcast-embed-${item.id}`}>Episode Audio (optional — enables in-page player)</label>
                          <select id={`podcast-embed-${item.id}`} value={item.embedUrl ?? ''} onChange={e => updatePodcastLink(item.id, 'embedUrl', e.target.value)}>
                            <option value="">— No audio —</option>
                            {rssEpisodes.map(ep => (
                              <option key={ep.audioUrl} value={ep.audioUrl}>{ep.title}</option>
                            ))}
                            {item.embedUrl && !rssEpisodes.some(ep => ep.audioUrl === item.embedUrl) && (
                              <option value={item.embedUrl}>{item.embedUrl}</option>
                            )}
                          </select>
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`podcast-notes-${item.id}`}>Show Notes</label>
                          <textarea id={`podcast-notes-${item.id}`} rows={4} placeholder="Key points, scripture references, timestamps..." value={item.showNotes ?? ''} onChange={e => updatePodcastLink(item.id, 'showNotes', e.target.value)} />
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`podcast-dq-${item.id}`}>Discussion Questions (one per line)</label>
                          <textarea id={`podcast-dq-${item.id}`} rows={5} placeholder="What stood out to you in this passage?" value={(item.discussionQuestions ?? []).join('\n')} onChange={e => updatePodcastLinkQuestions(item.id, e.target.value)} />
                        </div>
                      </div>
                      <button type="button" className="btn-admin-remove" onClick={() => removePodcastLink(item.id)}>Remove</button>
                    </div>
                </AdminCollapsibleCard>
              ))}
              <button type="button" className="btn-admin-add" onClick={addPodcastLink}>+ Add Episode Highlight</button>
              {renderSaveStatus()}
            </section>
          )}

          {/* FINISHED BOOKS */}
          {(adminView === 'podcast' && podcastTab === 'finished-books') && (
            <section className="admin-panel-section" aria-label="Finished books">
              <div className="admin-panel-head">
                <h2>Finished Books</h2>
                <p>Series that have been completed. Each one gets a playlist page at <code>/finished/:id</code>.</p>
              </div>
              {(form.finishedBooks ?? []).length === 0 && (
                <p className="admin-stats-note">No finished books yet. Use &ldquo;End Current Series&rdquo; on the Current Series tab to archive one, or add one manually below.</p>
              )}
              {(form.finishedBooks ?? []).map(book => (
                <AdminCollapsibleCard key={book.id} title={book.title || 'Untitled'} subtitle={`Season ${book.season}`}>
                  <div className="admin-array-row">
                    <div className="admin-array-fields">
                      <div className="admin-field">
                        <label>Title</label>
                        <input type="text" value={book.title} onChange={e => setForm(f => ({ ...f, finishedBooks: (f.finishedBooks ?? []).map(b => b.id === book.id ? { ...b, title: e.target.value } : b) }))} />
                      </div>
                      <div className="admin-field">
                        <label>Description</label>
                        <textarea rows={3} value={book.description} onChange={e => setForm(f => ({ ...f, finishedBooks: (f.finishedBooks ?? []).map(b => b.id === book.id ? { ...b, description: e.target.value } : b) }))} />
                      </div>
                      <div className="admin-field">
                        <label>Cover Image URL</label>
                        <input type="text" value={book.imageUrl} onChange={e => setForm(f => ({ ...f, finishedBooks: (f.finishedBooks ?? []).map(b => b.id === book.id ? { ...b, imageUrl: e.target.value } : b) }))} />
                        {renderImageAssetSelector(book.imageUrl, value => setForm(f => ({ ...f, finishedBooks: (f.finishedBooks ?? []).map(b => b.id === book.id ? { ...b, imageUrl: value } : b) })), `finished-book-img-${book.id}`)}
                        {renderImagePreview(book.imageUrl, `${book.title} cover`)}
                      </div>
                      <div className="admin-field">
                        <label>Season Number</label>
                        <input type="number" value={book.season} onChange={e => setForm(f => ({ ...f, finishedBooks: (f.finishedBooks ?? []).map(b => b.id === book.id ? { ...b, season: parseInt(e.target.value) || 1 } : b) }))} />
                      </div>
                    </div>
                    <button type="button" className="btn-admin-remove" onClick={() => setForm(f => ({ ...f, finishedBooks: (f.finishedBooks ?? []).filter(b => b.id !== book.id) }))}>Remove</button>
                  </div>
                </AdminCollapsibleCard>
              ))}
              <button
                type="button"
                className="btn-admin-add"
                onClick={() => setForm(f => ({ ...f, finishedBooks: [...(f.finishedBooks ?? []), { id: Date.now().toString(36), title: '', description: '', imageUrl: '', season: 1 }] }))}
              >
                + Add Finished Book
              </button>
              {renderSaveStatus()}
            </section>
          )}

          {/* DOWNLOADS */}
          {adminView === 'downloads' && (
            <section className="admin-panel-section">
              <div className="admin-panel-head">
                <h2>Downloads</h2>
                <p>Manage the featured study guide, current download library, and previous series downloads.</p>
              </div>

              {renderSectionJumpNav('downloads')}

              <div className="admin-content-summary">
                <div className="admin-summary-card"><h3>Companion Study Guide</h3><p>Included</p></div>
                <div className="admin-summary-card"><h3>Current Downloads</h3><p>{resourceLinks.length}</p></div>
              </div>

              <div className="admin-section-header" id="downloads-study-guide">
                <h3>Companion Study Guide</h3>
                <p>This is the featured primary download at the top of the Downloads page.</p>
              </div>
              <div className="admin-array-row">
                <div className="admin-array-fields">
                  <div className="admin-field">
                    <label htmlFor="resources-study-guide-title">Title</label>
                    <input id="resources-study-guide-title" type="text" value={form.studyGuideTitle} placeholder="Companion Study Guide" onChange={e => handleChange('studyGuideTitle', e.target.value)} />
                  </div>
                  <div className="admin-field">
                    <label htmlFor="resources-study-guide-description">Description</label>
                    <textarea id="resources-study-guide-description" rows={3} value={form.studyGuideDescription} placeholder="Describe the study guide download." onChange={e => handleChange('studyGuideDescription', e.target.value)} />
                  </div>
                  <div className="admin-field">
                    <label htmlFor="resources-study-guide-download-url">Primary Download URL</label>
                    <input id="resources-study-guide-download-url" type="url" value={form.studyGuideDownloadUrl} placeholder="/uploads/new-guide.pdf or https://..." onChange={e => handleChange('studyGuideDownloadUrl', e.target.value)} />
                    {renderFileAssetSelector(form.studyGuideDownloadUrl, value => handleChange('studyGuideDownloadUrl', value), 'resources-study-guide-download-url-asset')}
                    <p className="admin-stats-note">This is the file URL the main guide download form will deliver after submission.</p>
                  </div>
                  <div className="admin-field">
                    <label htmlFor="resources-study-guide-url">Printed Copy URL (Amazon button)</label>
                    <input id="resources-study-guide-url" type="url" value={form.studyGuideUrl} placeholder="https://..." onChange={e => handleChange('studyGuideUrl', e.target.value)} />
                  </div>
                  <div className="admin-field">
                    <label htmlFor="resources-study-guide-amazon-label">Amazon Button Label</label>
                    <input id="resources-study-guide-amazon-label" type="text" value={form.studyGuideAmazonButtonLabel} placeholder="Get it on Amazon" onChange={e => handleChange('studyGuideAmazonButtonLabel', e.target.value)} />
                  </div>
                </div>
              </div>

              <div className="admin-section-header" id="downloads-library">
                <h3>Download Library</h3>
                <p>Manage the current downloads shown below the featured guide.</p>
              </div>
              {resourceLinks.length === 0 && <p className="admin-stats-note">No resources yet.</p>}
              {resourceLinks.map(link => (
                <AdminCollapsibleCard
                  key={link.id}
                  title={link.label || 'Untitled download'}
                  subtitle={link.description || 'Current download item'}
                >
                  <div className="admin-array-row">
                      <div className="admin-array-fields">
                        <div className="admin-field">
                          <label htmlFor={`resource-label-${link.id}`}>Label</label>
                          <input id={`resource-label-${link.id}`} type="text" value={link.label} placeholder="Resource title" onChange={e => updateLink(link.id, 'label', e.target.value)} />
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`resource-url-${link.id}`}>URL</label>
                          <input id={`resource-url-${link.id}`} type="url" value={link.url} placeholder="https://..." onChange={e => updateLink(link.id, 'url', e.target.value)} />
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`resource-description-${link.id}`}>Description</label>
                          <textarea id={`resource-description-${link.id}`} rows={3} value={link.description ?? ''} placeholder="Description shown on the download page" onChange={e => updateLink(link.id, 'description', e.target.value)} />
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`resource-amazon-url-${link.id}`}>Amazon URL (optional)</label>
                          <input id={`resource-amazon-url-${link.id}`} type="url" value={link.amazonUrl ?? ''} placeholder="https://amazon.com/..." onChange={e => updateLink(link.id, 'amazonUrl', e.target.value)} />
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`resource-amazon-label-${link.id}`}>Amazon Button Label</label>
                          <input id={`resource-amazon-label-${link.id}`} type="text" value={link.amazonLabel ?? ''} placeholder="Get it on Amazon" onChange={e => updateLink(link.id, 'amazonLabel', e.target.value)} />
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`resource-image-${link.id}`}>Image URL</label>
                          <input id={`resource-image-${link.id}`} type="text" value={link.imageUrl ?? ''} placeholder="/uploads/example.png" onChange={e => updateLink(link.id, 'imageUrl', e.target.value)} />
                          {renderImageAssetSelector(link.imageUrl, value => updateLink(link.id, 'imageUrl', value), `resource-image-${link.id}-asset`)}
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`resource-tags-${link.id}`}>Tags</label>
                          <input id={`resource-tags-${link.id}`} type="text" value={(link.tags ?? []).join(', ')} placeholder="sermon, bible study, faith" onChange={e => updateLink(link.id, 'tags', e.target.value.split(',').map(tag => tag.trim()).filter(Boolean))} />
                        </div>
                      </div>
                      <button type="button" className="btn-admin-remove" onClick={() => removeLink(link.id)}>Remove</button>
                    </div>
                </AdminCollapsibleCard>
              ))}
              <button type="button" className="btn-admin-add" onClick={addResource}>+ Add Download</button>

              {renderSaveStatus()}
            </section>
          )}

          {/* CUSTOM LINKS */}
          {adminView === 'custom-links' && (
            <section className="admin-panel-section">
              <div className="admin-panel-head">
                <h2>Custom Links</h2>
                <p>Add links to show in platform buttons, footer navigation, or the More Resources download library.</p>
              </div>

              <div className="admin-panel-subhead">
                <h3>Homepage External Links</h3>
                <p>Links shown on the homepage, separate from the footer.</p>
              </div>
              {(form.customLinks ?? []).filter(link => link.placement === 'externalSites').length === 0 && (
                <p className="admin-stats-note">No homepage external links yet.</p>
              )}
              {(form.customLinks ?? []).filter(link => link.placement === 'externalSites').map(link => (
                <div key={link.id} className="admin-array-row">
                  <div className="admin-array-fields">
                    <div className="admin-field">
                      <label htmlFor={`external-site-link-label-${link.id}`}>Label</label>
                      <input id={`external-site-link-label-${link.id}`} type="text" value={link.label} placeholder="e.g. Bible study partner" onChange={e => updateLink(link.id, 'label', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`external-site-link-url-${link.id}`}>URL</label>
                      <input id={`external-site-link-url-${link.id}`} type="url" value={link.url} placeholder="https://..." onChange={e => updateLink(link.id, 'url', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`external-site-link-description-${link.id}`}>Description</label>
                      <textarea id={`external-site-link-description-${link.id}`} rows={3} value={link.description ?? ''} placeholder="Short description shown on the homepage" onChange={e => updateLink(link.id, 'description', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`external-site-link-image-${link.id}`}>Image URL</label>
                      <input id={`external-site-link-image-${link.id}`} type="text" value={link.imageUrl ?? ''} placeholder="/uploads/example.png" onChange={e => updateLink(link.id, 'imageUrl', e.target.value)} />
                      {renderImageAssetSelector(link.imageUrl, value => updateLink(link.id, 'imageUrl', value), `external-site-link-image-${link.id}-asset`)}
                      {renderImagePreview(link.imageUrl, `${link.label || 'External site link'} image preview`)}
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`external-site-link-tags-${link.id}`}>Tags</label>
                      <input id={`external-site-link-tags-${link.id}`} type="text" value={(link.tags ?? []).join(', ')} placeholder="external, homepage, ministry" onChange={e => updateLink(link.id, 'tags', e.target.value.split(',').map(tag => tag.trim()).filter(Boolean))} />
                    </div>
                    <div className="admin-field">
                      <label>Quick Action</label>
                      <button type="button" className="btn-admin-apply" onClick={() => updateLink(link.id, 'placement', 'footer')}>Move to Footer Nav</button>
                    </div>
                    <div className="admin-field">
                      <label>Shows On</label>
                      <p className="admin-stats-note">{getCustomLinkPlacementLabel(link.placement)}</p>
                      <a href={getCustomLinkPreviewPath(link.placement)} target="_blank" rel="noopener noreferrer" className="btn-admin-secondary">Preview Destination Page</a>
                    </div>
                  </div>
                  <button type="button" className="btn-admin-remove" onClick={() => removeLink(link.id)}>Remove</button>
                </div>
              ))}
              <button type="button" className="btn-admin-add" onClick={addExternalSiteLink}>+ Add Homepage External Link</button>

              <div className="admin-panel-subhead">
                <h3>Platform + Footer Links</h3>
                <p>Links that appear on the listen buttons row or footer nav.</p>
              </div>
              {(form.customLinks ?? []).filter(link => link.placement !== 'resources' && link.placement !== 'otherSites' && link.placement !== 'externalSites').length === 0 && (
                <p className="admin-stats-note">No custom links yet.</p>
              )}
              {(form.customLinks ?? []).filter(link => link.placement !== 'resources' && link.placement !== 'otherSites' && link.placement !== 'externalSites').map(link => (
                <div key={link.id} className="admin-array-row">
                  <div className="admin-array-fields">
                    <div className="admin-field">
                      <label htmlFor={`link-label-${link.id}`}>Label</label>
                      <input id={`link-label-${link.id}`} type="text" value={link.label} placeholder="e.g. iHeart Radio" onChange={e => updateLink(link.id, 'label', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`link-url-${link.id}`}>URL</label>
                      <input id={`link-url-${link.id}`} type="url" value={link.url} placeholder="https://..." onChange={e => updateLink(link.id, 'url', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`link-image-${link.id}`}>Image URL</label>
                      <input id={`link-image-${link.id}`} type="text" value={link.imageUrl ?? ''} placeholder="/uploads/example.png" onChange={e => updateLink(link.id, 'imageUrl', e.target.value)} />
                      {renderImageAssetSelector(link.imageUrl, value => updateLink(link.id, 'imageUrl', value), `link-image-${link.id}-asset`)}
                      {renderImagePreview(link.imageUrl, `${link.label || 'Custom link'} image preview`)}
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`link-tags-${link.id}`}>Tags</label>
                      <input id={`link-tags-${link.id}`} type="text" value={(link.tags ?? []).join(', ')} placeholder="listen, podcast, study" onChange={e => updateLink(link.id, 'tags', e.target.value.split(',').map(tag => tag.trim()).filter(Boolean))} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`link-placement-${link.id}`}>Show in</label>
                      <select id={`link-placement-${link.id}`} value={link.placement} onChange={e => updateLink(link.id, 'placement', e.target.value)}>
                        <option value="platforms">Platform Buttons (Listen section)</option>
                        <option value="footer">Footer Nav</option>
                        <option value="resources">More Resources Section</option>
                      </select>
                    </div>
                    <div className="admin-field">
                      <label>Quick Action</label>
                      <button type="button" className="btn-admin-apply" onClick={() => moveLinkToResources(link.id)}>Move to Resources</button>
                    </div>
                    <div className="admin-field">
                      <label>Shows On</label>
                      <p className="admin-stats-note">{getCustomLinkPlacementLabel(link.placement)}</p>
                      <a href={getCustomLinkPreviewPath(link.placement)} target="_blank" rel="noopener noreferrer" className="btn-admin-secondary">Preview Destination Page</a>
                    </div>
                  </div>
                  <button type="button" className="btn-admin-remove" onClick={() => removeLink(link.id)}>Remove</button>
                </div>
              ))}
              <button type="button" className="btn-admin-add" onClick={addLink}>+ Add Platform/Footer Link</button>

              <div className="admin-panel-subhead">
                <h3>Other Site Links</h3>
                <p>External links you want to surface in the footer dropdown.</p>
              </div>
              {(form.customLinks ?? []).filter(link => link.placement === 'otherSites').length === 0 && (
                <p className="admin-stats-note">No other site links yet.</p>
              )}
              {(form.customLinks ?? []).filter(link => link.placement === 'otherSites').map(link => (
                <div key={link.id} className="admin-array-row">
                  <div className="admin-array-fields">
                    <div className="admin-field">
                      <label htmlFor={`other-site-link-label-${link.id}`}>Label</label>
                      <input id={`other-site-link-label-${link.id}`} type="text" value={link.label} placeholder="e.g. Recommended ministry" onChange={e => updateLink(link.id, 'label', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`other-site-link-url-${link.id}`}>URL</label>
                      <input id={`other-site-link-url-${link.id}`} type="url" value={link.url} placeholder="https://..." onChange={e => updateLink(link.id, 'url', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`other-site-link-image-${link.id}`}>Image URL</label>
                      <input id={`other-site-link-image-${link.id}`} type="text" value={link.imageUrl ?? ''} placeholder="/uploads/example.png" onChange={e => updateLink(link.id, 'imageUrl', e.target.value)} />
                      {renderImageAssetSelector(link.imageUrl, value => updateLink(link.id, 'imageUrl', value), `other-site-link-image-${link.id}-asset`)}
                      {renderImagePreview(link.imageUrl, `${link.label || 'Other site link'} image preview`)}
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`other-site-link-tags-${link.id}`}>Tags</label>
                      <input id={`other-site-link-tags-${link.id}`} type="text" value={(link.tags ?? []).join(', ')} placeholder="external, ministry, study" onChange={e => updateLink(link.id, 'tags', e.target.value.split(',').map(tag => tag.trim()).filter(Boolean))} />
                    </div>
                    <div className="admin-field">
                      <label>Quick Action</label>
                      <button type="button" className="btn-admin-apply" onClick={() => updateLink(link.id, 'placement', 'footer')}>Move to Footer Nav</button>
                    </div>
                    <div className="admin-field">
                      <label>Shows On</label>
                      <p className="admin-stats-note">{getCustomLinkPlacementLabel(link.placement)}</p>
                      <a href={getCustomLinkPreviewPath(link.placement)} target="_blank" rel="noopener noreferrer" className="btn-admin-secondary">Preview Destination Page</a>
                    </div>
                  </div>
                  <button type="button" className="btn-admin-remove" onClick={() => removeLink(link.id)}>Remove</button>
                </div>
              ))}
              <button type="button" className="btn-admin-add" onClick={addOtherSiteLink}>+ Add Other Site Link</button>

              <div className="admin-panel-subhead">
                <h3>More Resources Links</h3>
                <p>These create cards in Downloads → More guides, worksheets, and past study downloads.</p>
              </div>
              {(form.customLinks ?? []).filter(link => link.placement === 'resources').length === 0 && (
                <p className="admin-stats-note">No resource links yet.</p>
              )}
              {(form.customLinks ?? []).filter(link => link.placement === 'resources').map(link => (
                <div key={link.id} className="admin-array-row">
                  <div className="admin-array-fields">
                    <div className="admin-field">
                      <label htmlFor={`resources-link-label-${link.id}`}>Label</label>
                      <input id={`resources-link-label-${link.id}`} type="text" value={link.label} placeholder="Resource title" onChange={e => updateLink(link.id, 'label', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`resources-link-url-${link.id}`}>Download URL</label>
                      <input id={`resources-link-url-${link.id}`} type="url" value={link.url} placeholder="https://..." onChange={e => updateLink(link.id, 'url', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`resources-link-description-${link.id}`}>Description</label>
                      <textarea id={`resources-link-description-${link.id}`} rows={3} value={link.description ?? ''} placeholder="Description shown on the download page" onChange={e => updateLink(link.id, 'description', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`resources-link-image-${link.id}`}>Image URL</label>
                      <input id={`resources-link-image-${link.id}`} type="text" value={link.imageUrl ?? ''} placeholder="/uploads/example.png" onChange={e => updateLink(link.id, 'imageUrl', e.target.value)} />
                      {renderImageAssetSelector(link.imageUrl, value => updateLink(link.id, 'imageUrl', value), `resources-link-image-${link.id}-asset`)}
                      {renderImagePreview(link.imageUrl, `${link.label || 'Resource link'} image preview`)}
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`resources-link-amazon-url-${link.id}`}>Amazon URL (optional)</label>
                      <input id={`resources-link-amazon-url-${link.id}`} type="url" value={link.amazonUrl ?? ''} placeholder="https://amazon.com/..." onChange={e => updateLink(link.id, 'amazonUrl', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`resources-link-amazon-label-${link.id}`}>Amazon Button Label</label>
                      <input id={`resources-link-amazon-label-${link.id}`} type="text" value={link.amazonLabel ?? ''} placeholder="Get it on Amazon" onChange={e => updateLink(link.id, 'amazonLabel', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`resources-link-tags-${link.id}`}>Tags</label>
                      <input id={`resources-link-tags-${link.id}`} type="text" value={(link.tags ?? []).join(', ')} placeholder="worksheet, study, pdf" onChange={e => updateLink(link.id, 'tags', e.target.value.split(',').map(tag => tag.trim()).filter(Boolean))} />
                    </div>
                    <div className="admin-field">
                      <label>Quick Action</label>
                      <button type="button" className="btn-admin-apply" onClick={() => updateLink(link.id, 'placement', 'platforms')}>Move to Platform Buttons</button>
                    </div>
                    <div className="admin-field">
                      <label>Shows On</label>
                      <p className="admin-stats-note">{getCustomLinkPlacementLabel(link.placement)}</p>
                      <a href={getCustomLinkPreviewPath(link.placement)} target="_blank" rel="noopener noreferrer" className="btn-admin-secondary">Preview Destination Page</a>
                    </div>
                  </div>
                  <button type="button" className="btn-admin-remove" onClick={() => removeLink(link.id)}>Remove</button>
                </div>
              ))}
              <button type="button" className="btn-admin-add" onClick={addResource}>+ Add Resource Link</button>
              {renderSaveStatus()}
            </section>
          )}

          {/* CONTENT BLOCKS */}
          {adminView === 'content-blocks' && (
            <section className="admin-panel-section">
              <div className="admin-panel-head">
                <h2>Content Blocks</h2>
                <p>Add extra text sections to any page. Choose which page each block appears on.</p>
              </div>
              {(form.customBlocks ?? []).length === 0 && <p className="admin-stats-note">No custom content blocks yet.</p>}
              {(form.customBlocks ?? []).map(block => (
                <div key={block.id} className="admin-array-row">
                  <div className="admin-array-fields">
                    <div className="admin-field">
                      <label htmlFor={`block-page-${block.id}`}>Page</label>
                      <select id={`block-page-${block.id}`} value={block.page ?? 'downloads'} onChange={e => updateBlock(block.id, 'page', e.target.value)}>
                        <option value="homepage">Homepage</option>
                        <option value="start-here">Start Here</option>
                        <option value="episodes">Episodes</option>
                        <option value="downloads">Downloads</option>
                        <option value="about">About</option>
                        <option value="contact">Contact</option>
                        <option value="questions">Q&amp;A</option>
                      </select>
                      <p className="admin-stats-note">Shows on: {getContentBlockPageLabel(block.page ?? 'downloads')}</p>
                      <a href={getContentBlockPreviewPath(block.page ?? 'downloads')} target="_blank" rel="noopener noreferrer" className="btn-admin-secondary">Preview Destination Page</a>
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`block-heading-${block.id}`}>Heading</label>
                      <input id={`block-heading-${block.id}`} type="text" value={block.heading} placeholder="Section heading" onChange={e => updateBlock(block.id, 'heading', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`block-body-${block.id}`}>Body Text</label>
                      <textarea id={`block-body-${block.id}`} value={block.body} rows={3} placeholder="Write your content here…" onChange={e => updateBlock(block.id, 'body', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`block-link-url-${block.id}`}>Button Link URL <span className="admin-field-optional">(optional)</span></label>
                      <input id={`block-link-url-${block.id}`} type="url" value={block.linkUrl ?? ''} placeholder="https://…" onChange={e => updateBlock(block.id, 'linkUrl', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`block-link-label-${block.id}`}>Button Label <span className="admin-field-optional">(optional)</span></label>
                      <input id={`block-link-label-${block.id}`} type="text" value={block.linkLabel ?? ''} placeholder="e.g. Get the book on Amazon" onChange={e => updateBlock(block.id, 'linkLabel', e.target.value)} />
                    </div>
                  </div>
                  <button type="button" className="btn-admin-remove" onClick={() => removeBlock(block.id)}>Remove</button>
                </div>
              ))}
              <button type="button" className="btn-admin-add" onClick={addBlock}>+ Add Content Block</button>
              {renderSaveStatus()}
            </section>
          )}

          {/* BOOKS */}
          {adminView === 'books' && (
            <section className="admin-panel-section">
              <div className="admin-panel-head">
                <h2>Books</h2>
                <p>Amazon books shown in the Reading List section on the Downloads page. Add a cover image, description, and Amazon link for each book.</p>
              </div>
              {(form.customLinks ?? []).filter(l => l.placement === 'books').length === 0 && <p className="admin-stats-note">No books yet.</p>}
              {(form.customLinks ?? []).filter(l => l.placement === 'books').map(book => (
                <div key={book.id} className="admin-array-row">
                  <div className="admin-array-fields">
                    <div className="admin-field">
                      <label htmlFor={`book-title-${book.id}`}>Title</label>
                      <input id={`book-title-${book.id}`} type="text" value={book.label} placeholder="Book title" onChange={e => updateLink(book.id, 'label', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`book-description-${book.id}`}>Description</label>
                      <textarea id={`book-description-${book.id}`} rows={3} value={book.description ?? ''} placeholder="Short description of the book" onChange={e => updateLink(book.id, 'description', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`book-image-${book.id}`}>Cover Image URL</label>
                      <input id={`book-image-${book.id}`} type="text" value={book.imageUrl ?? ''} placeholder="/uploads/cover.jpg" onChange={e => updateLink(book.id, 'imageUrl', e.target.value)} />
                      {renderImageAssetSelector(book.imageUrl, value => updateLink(book.id, 'imageUrl', value), `book-image-${book.id}-asset`)}
                      {renderImagePreview(book.imageUrl, `${book.label || 'Book'} cover`)}
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`book-amazon-url-${book.id}`}>Amazon URL</label>
                      <input id={`book-amazon-url-${book.id}`} type="url" value={book.amazonUrl ?? ''} placeholder="https://amazon.com/..." onChange={e => updateLink(book.id, 'amazonUrl', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`book-amazon-label-${book.id}`}>Amazon Button Label</label>
                      <input id={`book-amazon-label-${book.id}`} type="text" value={book.amazonLabel ?? ''} placeholder="View on Amazon" onChange={e => updateLink(book.id, 'amazonLabel', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`book-url-${book.id}`}>Link URL <span className="admin-field-optional">(used if no Amazon URL is set)</span></label>
                      <input id={`book-url-${book.id}`} type="url" value={book.url ?? ''} placeholder="https://..." onChange={e => updateLink(book.id, 'url', e.target.value)} />
                    </div>
                  </div>
                  <button type="button" className="btn-admin-remove" onClick={() => removeLink(book.id)}>Remove</button>
                </div>
              ))}
              <button type="button" className="btn-admin-add" onClick={addBook}>+ Add Book</button>
              {renderSaveStatus()}
            </section>
          )}

          {/* RECOMMENDED RESOURCES */}
          {adminView === 'recommended-resources' && (
            <section className="admin-panel-section">
              <div className="admin-panel-head">
                <h2>Recommended Resources</h2>
                <p>Curated resources shown at the bottom of the Downloads page. When at least one is added, it replaces the default Truth For Life widget. Add a title, image, link, and description for each resource.</p>
              </div>
              {(form.customLinks ?? []).filter(l => l.placement === 'recommended').length === 0 && (
                <p className="admin-stats-note">No custom recommended resources yet — the Truth For Life widget shows by default.</p>
              )}
              {(form.customLinks ?? []).filter(l => l.placement === 'recommended').map(item => (
                <div key={item.id} className="admin-array-row">
                  <div className="admin-array-fields">
                    <div className="admin-field">
                      <label htmlFor={`rec-title-${item.id}`}>Title</label>
                      <input id={`rec-title-${item.id}`} type="text" value={item.label} placeholder="Resource title" onChange={e => updateLink(item.id, 'label', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`rec-url-${item.id}`}>Link URL</label>
                      <input id={`rec-url-${item.id}`} type="url" value={item.url} placeholder="https://..." onChange={e => updateLink(item.id, 'url', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`rec-description-${item.id}`}>Description</label>
                      <textarea id={`rec-description-${item.id}`} rows={3} value={item.description ?? ''} placeholder="What is this resource?" onChange={e => updateLink(item.id, 'description', e.target.value)} />
                    </div>
                    <div className="admin-field">
                      <label htmlFor={`rec-image-${item.id}`}>Image URL</label>
                      <input id={`rec-image-${item.id}`} type="text" value={item.imageUrl ?? ''} placeholder="/uploads/example.jpg" onChange={e => updateLink(item.id, 'imageUrl', e.target.value)} />
                      {renderImageAssetSelector(item.imageUrl, value => updateLink(item.id, 'imageUrl', value), `rec-image-${item.id}-asset`)}
                      {renderImagePreview(item.imageUrl, `${item.label || 'Resource'} image`)}
                    </div>
                  </div>
                  <button type="button" className="btn-admin-remove" onClick={() => removeLink(item.id)}>Remove</button>
                </div>
              ))}
              <button type="button" className="btn-admin-add" onClick={addRecommended}>+ Add Recommended Resource</button>
              {renderSaveStatus()}
            </section>
          )}

          {/* STUDIES */}
          {adminView === 'colossians-study' && (
            <section className="admin-panel-section">
              <div className="admin-panel-head">
                <h2>Studies</h2>
                <p>Manage multiple study tracks and their lesson sections (text, commentary, Greek notes, audio, and questions).</p>
              </div>

              {(form.studies ?? []).length === 0 && <p className="admin-stats-note">No studies yet.</p>}

              {(form.studies ?? []).map(study => (
                <AdminCollapsibleCard
                  key={study.id}
                  title={`${study.title || 'Untitled study'} · /study/${study.slug || 'slug'}`}
                  subtitle={study.description || 'Study description'}
                  open={false}
                >
                    <div className="admin-array-row" style={{ marginBottom: '1rem' }}>
                      <div className="admin-array-fields">
                        <div className="admin-field">
                          <label htmlFor={`study-title-${study.id}`}>Study Title</label>
                          <input id={`study-title-${study.id}`} type="text" value={study.title} placeholder="Colossians: Rooted in Christ" onChange={e => updateStudyProgram(study.id, 'title', e.target.value)} />
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`study-slug-${study.id}`}>Slug</label>
                          <input id={`study-slug-${study.id}`} type="text" value={study.slug} placeholder="colossians" onChange={e => updateStudyProgram(study.id, 'slug', e.target.value.trim().toLowerCase().replace(/[^a-z0-9-]/g, '-'))} />
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`study-bible-book-${study.id}`}>Bible Book (for passage lookup)</label>
                          <input id={`study-bible-book-${study.id}`} type="text" value={study.bibleBook ?? ''} placeholder="colossians (leave blank to use slug)" onChange={e => updateStudyProgram(study.id, 'bibleBook', e.target.value.trim().toLowerCase().replace(/\s+/g, ''))} />
                          <p className="admin-stats-note" style={{ marginTop: '0.25rem' }}>Lowercase book name used to fetch BSB passage text. E.g. colossians, ephesians, 1corinthians. Leave blank if the slug matches the book name.</p>
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`study-status-${study.id}`}>Status</label>
                          <select id={`study-status-${study.id}`} value={study.status} onChange={e => updateStudyProgram(study.id, 'status', e.target.value)}>
                            <option value="active">Active</option>
                            <option value="planned">Planned</option>
                          </select>
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`study-description-${study.id}`}>Description</label>
                          <textarea id={`study-description-${study.id}`} rows={3} value={study.description} placeholder="Brief description for the study hub card" onChange={e => updateStudyProgram(study.id, 'description', e.target.value)} />
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`study-home-eyebrow-${study.id}`}>Homepage Card Eyebrow</label>
                          <input id={`study-home-eyebrow-${study.id}`} type="text" value={study.homepageEyebrow ?? ''} placeholder="New Study" onChange={e => updateStudyProgram(study.id, 'homepageEyebrow', e.target.value)} />
                        </div>
                        <div className="admin-field">
                          <label htmlFor={`study-new-tag-label-${study.id}`}>NEW Tag Label</label>
                          <input id={`study-new-tag-label-${study.id}`} type="text" value={study.newTagLabel ?? 'NEW'} placeholder="NEW" onChange={e => updateStudyProgram(study.id, 'newTagLabel', e.target.value)} />
                        </div>
                        <div className="admin-field" style={{ display: 'flex', alignItems: 'center', gap: '0.6rem' }}>
                          <input id={`study-home-feature-${study.id}`} type="checkbox" checked={study.showOnHomepage === true} onChange={e => updateStudyProgram(study.id, 'showOnHomepage', e.target.checked)} />
                          <label htmlFor={`study-home-feature-${study.id}`}>Feature this study card on homepage</label>
                        </div>
                        <div className="admin-field" style={{ display: 'flex', alignItems: 'center', gap: '0.6rem' }}>
                          <input id={`study-show-new-tag-${study.id}`} type="checkbox" checked={study.showNewTag === true} onChange={e => updateStudyProgram(study.id, 'showNewTag', e.target.checked)} />
                          <label htmlFor={`study-show-new-tag-${study.id}`}>Show NEW tag on homepage card</label>
                        </div>
                      </div>
                      <div className="admin-field">
                        <label htmlFor={`study-chapters-${study.id}`}>Number of Chapters</label>
                        <input id={`study-chapters-${study.id}`} type="number" min="1" max="999" value={study.numberOfChapters} placeholder="4" onChange={e => updateStudyProgram(study.id, "numberOfChapters", Number(e.target.value) || 1)} />
                      </div>
                      <button type="button" className="btn-admin-remove" onClick={() => removeStudyProgram(study.id)}>Remove Study</button>
                    </div>

                    {(study.sections ?? []).length === 0 && (
                      <p className="admin-stats-note">No sections yet for this study.</p>
                    )}

                    <DndContext
                      sensors={dragSensors}
                      collisionDetection={closestCenter}
                      onDragEnd={({ active, over }) => {
                        const overId = over?.id
                        if (!overId || active.id === overId) return
                        setForm(f => {
                          const studies = f.studies.map(s => {
                            if (s.id !== study.id) return s
                            const oldIndex = s.sections.findIndex(sec => sec.id === active.id)
                            const newIndex = s.sections.findIndex(sec => sec.id === overId)
                            if (oldIndex === -1 || newIndex === -1) return s
                            const newSections = arrayMove(s.sections, oldIndex, newIndex)
                            return { ...s, sections: newSections }
                          })
                          return { ...f, studies }
                        })
                      }}
                    >
                      <SortableContext
                        items={(study.sections ?? []).map(section => section.id)}
                        strategy={verticalListSortingStrategy}
                      >
                        {(study.sections ?? []).map(section => (
                          <SortableLessonSection
                            key={section.id}
                            section={section}
                            study={study}
                            updateStudySection={updateStudySection}
                            removeStudySection={removeStudySection}
                            rssEpisodes={rssEpisodes}
                          />
                        ))}
                      </SortableContext>
                    </DndContext>


                    <button type="button" className="btn-admin-add" onClick={() => addStudySection(study.id)}>+ Add Lesson Section</button>
                </AdminCollapsibleCard>
              ))}

              <button type="button" className="btn-admin-add" onClick={addStudyProgram}>+ Add Study Program</button>
              {renderSaveStatus()}
            </section>
          )}

          {/* GLOBAL / FOOTER & PLATFORM */}
          {adminView === 'global' && (
            <section className="admin-panel-section">
              <div className="admin-panel-head">
                <h2>Footer &amp; Global Settings</h2>
                <p>Edit the site footer text, platform links, header label, and analytics cookie banner.</p>
              </div>

              {renderSectionJumpNav('global')}

              <div className="admin-panel-subhead" id="global-footer"><h3>Footer</h3></div>
              {FIELDS.filter(f => f.section === 'global' && ['footerTitle','footerSubtitle','footerEmail','footerCopyright','footerPrivacyNote'].includes(f.key)).map(({ key, label, multiline }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  {multiline
                    ? <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={3} />
                    : (
                      <>
                        <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />
                        {key.includes('ImageUrl') && renderImageAssetSelector(form[key] as string, value => handleChange(key, value), `field-${key}-asset`)}
                        {key.includes('ImageUrl') && renderImagePreview(form[key] as string, `${label} preview`)}
                      </>
                    )}
                </div>
              ))}

              <div className="admin-panel-subhead" id="global-header"><h3>Header</h3></div>
              {FIELDS.filter(f => f.section === 'global' && f.key === 'headerFollowLabel').map(({ key, label }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />
                </div>
              ))}

              <div className="admin-panel-subhead" id="global-platforms"><h3>Platform URLs</h3></div>
              {FIELDS.filter(f => f.section === 'global' && f.key.startsWith('platform')).map(({ key, label }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />
                </div>
              ))}

              <div className="admin-panel-subhead" id="global-banner"><h3>Analytics Banner</h3></div>
              {FIELDS.filter(f => f.section === 'global' && f.key === 'cookieBannerText').map(({ key, label }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={3} />
                </div>
              ))}

              <div className="admin-panel-subhead" id="global-welcome-email"><h3>Welcome Email</h3></div>
              {FIELDS.filter(f => f.section === 'global' && f.key.startsWith('welcomeEmail')).map(({ key, label, multiline }) => (
                <div className="admin-field" key={key}>
                  <label htmlFor={`field-${key}`}>{label}</label>
                  {multiline
                    ? <textarea id={`field-${key}`} value={form[key] as string} onChange={e => handleChange(key, e.target.value)} rows={3} />
                    : <input id={`field-${key}`} type="text" value={form[key] as string} onChange={e => handleChange(key, e.target.value)} />}
                </div>
              ))}

              {renderSaveStatus()}
            </section>
          )}

          {/* EPISODE SCRIPTS */}
          {(adminView === 'podcast' && podcastTab === 'episode-scripts') && (
            <section className="admin-panel-section">
              <div className="admin-panel-head">
                <h2>Episode Scripts</h2>
                <p>Upload a Word (.docx) script for each episode to make the full content searchable from the site&apos;s global search bar.</p>
              </div>

              {/* Upload form */}
              <div className="admin-script-upload-form">
                <div className="admin-field">
                  <label htmlFor="script-episode-num">Episode Number</label>
                  <input
                    id="script-episode-num"
                    type="number"
                    min="1"
                    placeholder="e.g. 42"
                    value={scriptUploadEpisode}
                    onChange={e => setScriptUploadEpisode(e.target.value)}
                  />
                </div>
                <div className="admin-field">
                  <label htmlFor="script-episode-title">Episode Title <span style={{ color: '#5a5440', fontWeight: 400 }}>(optional — will be pulled from search results)</span></label>
                  <input
                    id="script-episode-title"
                    type="text"
                    placeholder="e.g. Colossians 1:15 — The Image of God"
                    value={scriptUploadTitle}
                    onChange={e => setScriptUploadTitle(e.target.value)}
                  />
                </div>
                <div className="admin-field">
                  <label htmlFor="script-file">Word Document (.docx)</label>
                  <input
                    id="script-file"
                    type="file"
                    accept=".docx,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
                    onChange={e => setScriptUploadFile(e.target.files?.[0] ?? null)}
                  />
                </div>
                <div style={{ display: 'flex', gap: '0.75rem', alignItems: 'center', flexWrap: 'wrap' }}>
                  <button type="button" className="btn-admin-save" onClick={handleScriptUpload} disabled={scriptUploadBusy}>
                    {scriptUploadBusy ? 'Uploading…' : 'Upload Script'}
                  </button>
                  {scriptUploadMsg && <span style={{ fontSize: '0.85rem', color: scriptUploadMsg.startsWith('✓') ? '#6fcf97' : '#e07a7a' }}>{scriptUploadMsg}</span>}
                </div>
              </div>

              {/* Indexed scripts list */}
              <div className="admin-panel-head" style={{ marginTop: '2rem' }}>
                <h3 style={{ margin: 0 }}>Indexed Scripts</h3>
                <button type="button" className="btn-admin-reset" onClick={loadScriptsList} disabled={scriptsLoading} style={{ marginLeft: 'auto' }}>
                  {scriptsLoading ? 'Loading…' : 'Refresh'}
                </button>
              </div>
              {scriptsList.length === 0 ? (
                <p style={{ color: '#5a5440', fontSize: '0.9rem', marginTop: '0.75rem' }}>
                  No scripts uploaded yet.{' '}
                  <button type="button" className="btn-admin-reset" style={{ display: 'inline', padding: '0.1rem 0.5rem' }} onClick={loadScriptsList}>Load list</button>
                </p>
              ) : (
                <div className="admin-script-list">
                  {scriptsList.map(script => (
                    <div key={script.episodeNumber} className="admin-script-row">
                      <div className="admin-script-row-info">
                        <span className="admin-script-ep">Ep. {script.episodeNumber}</span>
                        <span className="admin-script-title">{script.title}</span>
                        <span className="admin-script-meta">{script.wordCount.toLocaleString()} words · {script.filename}</span>
                      </div>
                      <button type="button" className="btn-admin-remove" onClick={() => handleDeleteScript(script.episodeNumber)}>Remove</button>
                    </div>
                  ))}
                </div>
              )}
            </section>
          )}

          {/* RSS FEED */}
          {(adminView === 'podcast' && podcastTab === 'rss-feed') && (() => {
            const canonicalUrl = (form as { seo?: { canonicalUrl?: string } }).seo?.canonicalUrl?.replace(/\/$/, '') ?? ''
            const feedUrl = canonicalUrl ? `${canonicalUrl}/feed.xml` : '/feed.xml'
            const anchorFeedUrl = 'https://anchor.fm/s/11068d290/podcast/rss'
            return (
              <section className="admin-panel-section">
                <div className="admin-panel-head">
                  <h2>RSS Feed</h2>
                  <p>Your podcast feed is available at your own domain and proxies the Anchor source feed.</p>
                </div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: '1.25rem' }}>
                  <div style={{ background: '#1a1a16', border: '1px solid #2a2518', borderRadius: '10px', padding: '1.25rem 1.5rem' }}>
                    <p style={{ margin: '0 0 0.35rem', fontSize: '0.78rem', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#7a7060' }}>Your Feed URL</p>
                    <div style={{ display: 'flex', gap: '0.75rem', alignItems: 'center', flexWrap: 'wrap' }}>
                      <code style={{ fontSize: '0.95rem', color: '#e0d5b5', wordBreak: 'break-all' }}>{feedUrl}</code>
                      <button type="button" className="btn-admin-save" onClick={() => navigator.clipboard.writeText(feedUrl)}>Copy</button>
                      <a href={feedUrl} target="_blank" rel="noopener noreferrer" className="btn-admin-reset">Open ↗</a>
                    </div>
                    <p style={{ margin: '0.75rem 0 0', fontSize: '0.85rem', color: '#7a7060' }}>Submit this URL to Apple Podcasts, Spotify, and other directories to keep your feed on your own domain.</p>
                  </div>
                  <div style={{ background: '#1a1a16', border: '1px solid #2a2518', borderRadius: '10px', padding: '1.25rem 1.5rem' }}>
                    <p style={{ margin: '0 0 0.35rem', fontSize: '0.78rem', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.08em', color: '#7a7060' }}>Anchor Source Feed</p>
                    <div style={{ display: 'flex', gap: '0.75rem', alignItems: 'center', flexWrap: 'wrap' }}>
                      <code style={{ fontSize: '0.88rem', color: '#7a7060', wordBreak: 'break-all' }}>{anchorFeedUrl}</code>
                      <a href={anchorFeedUrl} target="_blank" rel="noopener noreferrer" className="btn-admin-reset">Open ↗</a>
                    </div>
                    <p style={{ margin: '0.75rem 0 0', fontSize: '0.85rem', color: '#7a7060' }}>Your feed.xml proxies this source and rewrites the channel link to your domain. Episodes are cached for 30 minutes.</p>
                  </div>
                </div>
              </section>
            )
          })()}

          {/* STUDY COMMENTS */}
          {adminView === 'study-comments' && (() => {
            const filtered = studyComments.filter(c => {
              if (commentFilter === 'pending') return !c.isApproved
              if (commentFilter === 'approved') return c.isApproved
              return true
            }).filter(c => {
              if (!commentSearch.trim()) return true
              const q = commentSearch.toLowerCase()
              return c.displayName.toLowerCase().includes(q) || c.text.toLowerCase().includes(q) || c.studySlug.includes(q) || c.sectionId.includes(q)
            })

            return (
              <section className="admin-panel-section" aria-label="Study Comment Moderation">
                <div className="admin-panel-head">
                  <h2>Study Comments</h2>
                  <p>Review, approve, and delete comments left on study section pages.</p>
                </div>

                <div className="admin-actions admin-actions--maintenance" style={{ alignItems: 'center' }}>
                  <input
                    type="text"
                    value={commentSearch}
                    onChange={e => setCommentSearch(e.target.value)}
                    placeholder="Search by name, text, or section…"
                    style={{ minWidth: '280px', maxWidth: '480px', width: '100%' }}
                  />
                  <button type="button" className={`btn-admin-reset${commentFilter === 'all' ? ' btn-admin-reset--active' : ''}`} onClick={() => setCommentFilter('all')}>All</button>
                  <button type="button" className={`btn-admin-reset${commentFilter === 'pending' ? ' btn-admin-reset--active' : ''}`} onClick={() => setCommentFilter('pending')}>Pending</button>
                  <button type="button" className={`btn-admin-reset${commentFilter === 'approved' ? ' btn-admin-reset--active' : ''}`} onClick={() => setCommentFilter('approved')}>Approved</button>
                  <button type="button" className="btn-admin-reset" onClick={loadStudyComments} disabled={commentsLoading} style={{ marginLeft: 'auto' }}>
                    {commentsLoading ? 'Loading…' : 'Refresh'}
                  </button>
                </div>

                {!commentsLoaded ? (
                  <div style={{ marginTop: '1.5rem' }}>
                    <button type="button" className="btn-admin-reset" onClick={loadStudyComments} disabled={commentsLoading}>
                      {commentsLoading ? 'Loading…' : 'Load Comments'}
                    </button>
                  </div>
                ) : filtered.length === 0 ? (
                  <p className="admin-stats-note" style={{ marginTop: '1rem' }}>
                    {commentFilter === 'pending' ? 'No pending comments.' : commentFilter === 'approved' ? 'No approved comments.' : 'No comments yet.'}
                  </p>
                ) : (
                  <>
                    <p className="admin-stats-note">Showing {filtered.length} of {studyComments.length} comments.</p>
                    <div className="admin-comment-list">
                      {filtered.map(comment => (
                        <div key={comment.id} className={`admin-comment-card${comment.isApproved ? '' : ' admin-comment-card--pending'}`}>
                          <div className="admin-comment-card-header">
                            <div className="admin-comment-card-meta">
                              <span className="admin-comment-author">{comment.displayName}</span>
                              <span className="admin-comment-location">{comment.studySlug} › {comment.sectionId}</span>
                              <span className="admin-comment-date">{new Date(comment.createdAt).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })}</span>
                            </div>
                            {!comment.isApproved && (
                              <span className="admin-comment-badge admin-comment-badge--pending">Pending</span>
                            )}
                            {comment.isApproved && (
                              <span className="admin-comment-badge admin-comment-badge--approved">Approved</span>
                            )}
                          </div>
                          <p className="admin-comment-text">{comment.text}</p>
                          <div className="admin-comment-actions">
                            {!comment.isApproved && (
                              <button type="button" className="btn-admin-save" onClick={() => handleApproveComment(comment.id)}>Approve</button>
                            )}
                            <button type="button" className="btn-admin-remove" onClick={() => handleDeleteComment(comment.id)}>Delete</button>
                          </div>
                        </div>
                      ))}
                    </div>
                  </>
                )}
              </section>
            )
          })()}

          {/* EMAIL TEMPLATES */}
          {adminView === 'email-templates' && (
            <EmailTemplatesPanel form={form} handleChange={handleChange} renderSaveStatus={renderSaveStatus} />
          )}

          {/* QUESTIONS */}
          {adminView === 'questions' && (
            <section className="admin-panel-section" aria-label="Q&A Management">
              <div className="admin-panel-head">
                <h2>Bible Questions &amp; Answers</h2>
                <p>Manage submitted questions, add manual questions, provide answers, and approve for public display.</p>
              </div>

              <div className="admin-actions admin-actions--maintenance" style={{ alignItems: 'center' }}>
                <input
                  type="text"
                  value={questionSearch}
                  onChange={e => setQuestionSearch(e.target.value)}
                  placeholder="Search by name, question, or answer..."
                  style={{ minWidth: '320px', maxWidth: '520px', width: '100%' }}
                />
                <button type="button" className={`btn-admin-reset${questionFilter === 'all' ? ' btn-admin-reset--active' : ''}`} onClick={() => setQuestionFilter('all')}>All</button>
                <button type="button" className={`btn-admin-reset${questionFilter === 'pending' ? ' btn-admin-reset--active' : ''}`} onClick={() => setQuestionFilter('pending')}>Pending</button>
                <button type="button" className={`btn-admin-reset${questionFilter === 'approved' ? ' btn-admin-reset--active' : ''}`} onClick={() => setQuestionFilter('approved')}>Approved</button>
                <button type="button" className={`btn-admin-reset${questionFilter === 'answered' ? ' btn-admin-reset--active' : ''}`} onClick={() => setQuestionFilter('answered')}>Answered</button>
                <button type="button" className={`btn-admin-reset${questionFilter === 'unanswered' ? ' btn-admin-reset--active' : ''}`} onClick={() => setQuestionFilter('unanswered')}>Unanswered</button>
              </div>
              <p className="admin-stats-note">Showing {filteredAdminQuestions.length} of {questions.length} questions.</p>

              {selectedQuestionIds.size > 0 && (
                <div className="admin-bulk-toolbar">
                  <span>{selectedQuestionIds.size} selected</span>
                  <button type="button" className="btn-admin-remove" onClick={handleBulkDeleteQuestions}>Delete Selected</button>
                  <button type="button" className="btn-admin-reset" onClick={() => setSelectedQuestionIds(new Set())}>Deselect All</button>
                </div>
              )}

              <div className="admin-visits-table-wrap">
                <h3>Add Question Manually</h3>
                <div className="admin-array-row">
                  <div className="admin-array-fields">
                    <div className="admin-field">
                      <label htmlFor="manual-question-first-name">First Name</label>
                      <input
                        id="manual-question-first-name"
                        type="text"
                        value={manualQuestion.firstName}
                        onChange={e => setManualQuestion(curr => ({ ...curr, firstName: e.target.value }))}
                        placeholder="Nate"
                      />
                    </div>
                    <div className="admin-field">
                      <label htmlFor="manual-question-email">Email (optional)</label>
                      <input
                        id="manual-question-email"
                        type="email"
                        value={manualQuestion.email}
                        onChange={e => setManualQuestion(curr => ({ ...curr, email: e.target.value }))}
                        placeholder="optional@email.com"
                      />
                    </div>
                    <div className="admin-field">
                      <label htmlFor="manual-question-question">Question</label>
                      <textarea
                        id="manual-question-question"
                        rows={3}
                        value={manualQuestion.question}
                        onChange={e => setManualQuestion(curr => ({ ...curr, question: e.target.value }))}
                        placeholder="Type the question you want to add to the Q&A list..."
                      />
                    </div>
                    <div className="admin-field">
                      <label htmlFor="manual-question-answer">Answer (optional)</label>
                      <textarea
                        id="manual-question-answer"
                        rows={3}
                        value={manualQuestion.answer}
                        onChange={e => setManualQuestion(curr => ({ ...curr, answer: e.target.value }))}
                        placeholder="Optional: add an answer now"
                      />
                    </div>
                    <div className="admin-field">
                      <label htmlFor="manual-question-approve">Approve Immediately</label>
                      <select
                        id="manual-question-approve"
                        value={manualQuestion.approve ? 'yes' : 'no'}
                        onChange={e => setManualQuestion(curr => ({ ...curr, approve: e.target.value === 'yes' }))}
                      >
                        <option value="no">No</option>
                        <option value="yes">Yes</option>
                      </select>
                    </div>
                  </div>
                  <button
                    type="button"
                    className="btn-admin-save"
                    onClick={handleCreateManualQuestion}
                    disabled={manualQuestionStatus === 'saving'}
                  >
                    {manualQuestionStatus === 'saving' ? 'Adding…' : 'Add Question'}
                  </button>
                </div>
                {manualQuestionMsg && (
                  <p className={`admin-status ${manualQuestionStatus === 'error' ? 'admin-status--err' : 'admin-status--ok'}`}>
                    {manualQuestionMsg}
                  </p>
                )}
              </div>

              {questions.length === 0 ? (
                <p className="admin-stats-note">No questions submitted yet.</p>
              ) : filteredAdminQuestions.length === 0 ? (
                <p className="admin-stats-note">No questions match this filter.</p>
              ) : (
                <div className="admin-questions-list">
                  {visibleAdminQuestions.map(question => (
                    <div key={question.id} className={`admin-question-card${selectedQuestionIds.has(question.id) ? ' admin-question-card--selected' : ''}`}>
                      <div className="admin-question-header">
                        <label className="admin-question-checkbox" title="Select question">
                          <input
                            type="checkbox"
                            checked={selectedQuestionIds.has(question.id)}
                            onChange={e => {
                              setSelectedQuestionIds(prev => {
                                const next = new Set(prev)
                                if (e.target.checked) next.add(question.id)
                                else next.delete(question.id)
                                return next
                              })
                            }}
                          />
                        </label>
                        <div>
                          <p className="admin-question-meta"><strong>{question.firstName}</strong> • {formatDate(question.submittedAt)}</p>
                          <p className="admin-question-text"><strong>Q:</strong> {question.question}</p>
                        </div>
                        <div className="admin-question-status">
                          <span className={`admin-badge ${question.isApproved ? 'admin-badge--approved' : 'admin-badge--pending'}`}>
                            {question.isApproved ? 'Approved' : 'Pending'}
                          </span>
                          {question.answer && <span className="admin-badge admin-badge--answered">Answered</span>}
                        </div>
                      </div>
                      {question.answer && (
                        <div className="admin-question-answer">
                          <p><strong>A:</strong> {question.answer}</p>
                        </div>
                      )}
                      {editingQuestionId === question.id ? (
                        <div className="admin-question-editor">
                          <textarea
                            value={answeredQuestions[question.id] ?? question.answer ?? ''}
                            onChange={e => setAnsweredQuestions(a => ({ ...a, [question.id]: e.target.value }))}
                            rows={4}
                            placeholder="Type your answer here..."
                          />
                          <div className="admin-question-editor-actions">
                            <button type="button" className="btn-admin-save" onClick={() => handleAnswerQuestion(question.id, answeredQuestions[question.id] ?? '')}>Save Answer</button>
                            <button type="button" className="btn-admin-reset" onClick={() => setEditingQuestionId(null)}>Cancel</button>
                          </div>
                        </div>
                      ) : (
                        <div className="admin-question-actions">
                          <button type="button" className="btn-admin-reset" onClick={() => { setEditingQuestionId(question.id); setAnsweredQuestions(a => ({ ...a, [question.id]: question.answer ?? '' })) }}>
                            {question.answer ? 'Edit Answer' : 'Add Answer'}
                          </button>
                          <button type="button" className={`btn-admin-${question.isApproved ? 'remove' : 'reset'}`} onClick={() => handleApproveQuestion(question.id, !question.isApproved)}>
                            {question.isApproved ? 'Unapprove' : 'Approve'}
                          </button>
                          <button type="button" className={`btn-admin-${question.pinned ? 'remove' : 'reset'}`} onClick={() => handlePinQuestion(question.id, !question.pinned)}>
                            {question.pinned ? '📌 Unpin' : '📌 Pin'}
                          </button>
                          <button type="button" className="btn-admin-remove" onClick={() => handleDeleteQuestion(question.id)}>Delete</button>
                        </div>
                      )}
                    </div>
                  ))}
                </div>
              )}

              {filteredAdminQuestions.length > QUESTION_PAGE_SIZE && (
                <div className="qa-pagination" style={{ marginTop: '1rem', justifyContent: 'flex-start' }}>
                  <button
                    type="button"
                    className="qa-page-btn"
                    onClick={() => setQuestionPage(p => Math.max(0, p - 1))}
                    disabled={questionPage === 0}
                  >
                    Prev
                  </button>
                  <span className="qa-page-info">Page {questionPage + 1} / {totalQuestionPages}</span>
                  <button
                    type="button"
                    className="qa-page-btn"
                    onClick={() => setQuestionPage(p => Math.min(totalQuestionPages - 1, p + 1))}
                    disabled={questionPage >= totalQuestionPages - 1}
                  >
                    Next
                  </button>
                </div>
              )}
            </section>
          )}

          {/* STUDY USERS */}
          {adminView === 'study-users' && <StudyUsersPanel studies={normalizeStudies(form)} />}

          {/* ANALYTICS */}
          {adminView === 'analytics' && (
            <AnalyticsPanel
              stats={stats}
              statsStatus={statsStatus}
              statsRange={statsRange}
              onRangeChange={async (range) => {
                setStatsRange(range)
                setStatsStatus('loading')
                try { await reloadStats(range) } catch { setStatsStatus('error') }
              }}
              opsStatus={opsStatus}
              formatDate={formatDate}
              maskIp={maskIp}
              maintenanceMsg={maintenanceMsg}
              selectedBackup={selectedBackup}
              backupFiles={backupFiles}
              selectedBackupPreview={selectedBackupPreview}
              onBackupSelect={setSelectedBackup}
              onRestore={handleRestoreBackup}
              onExport={handleExport}
              onBackupNow={handleBackupNow}
              onDownloadFullBackup={handleDownloadFullBackup}
              onImportFullBackup={handleImportFullBackup}
              fullBackupBusy={fullBackupBusy}
              onPrune={handlePrune}
              onClear={handleClear}
              onPurgeCache={handlePurgeCache}
              onDeployHook={handleDeployHook}
              onRefreshStatus={() => { void reloadOpsStatus() }}
            />
          )}

          {/* ASSETS */}
          {adminView === 'assets' && (
            <section className="admin-panel-section" aria-label="Asset manager">
              <div className="admin-panel-head">
                <h2>Asset Manager</h2>
                <p>Upload, tag, and reuse hosted images from this domain.</p>
              </div>
              <div className="admin-actions admin-actions--maintenance">
                <label className="btn-admin-reset" style={{ cursor: 'pointer' }}>
                  {assetUploadPending ? 'Uploading…' : 'Upload Image or PDF'}
                  <input type="file" accept="image/*,.pdf" style={{ display: 'none' }} onChange={handleAssetUpload} disabled={assetUploadPending} />
                </label>
              </div>
              {assets.length === 0 && <p className="admin-stats-note">No assets uploaded yet.</p>}
              {assets.length > 0 && (
                <div className="admin-visits-table-scroll">
                  <table className="admin-visits-table admin-assets-table">
                    <thead>
                      <tr>
                        <th>Preview</th>
                        <th>Filename</th>
                        <th>Size</th>
                        <th>Updated</th>
                        <th>Tags</th>
                        <th>Actions</th>
                      </tr>
                    </thead>
                    <tbody>
                      {assets.map(asset => (
                        <tr key={asset.filename}>
                          <td>
                            {isImageAsset(asset.filename)
                              ? <img src={asset.url} alt={asset.filename} className="admin-asset-thumb" style={{ width: '72px', height: '52px', objectFit: 'cover' }} />
                              : <span>PDF/File</span>}
                          </td>
                          <td>{asset.filename}</td>
                          <td>{(asset.sizeBytes / 1024).toFixed(1)} KB</td>
                          <td>{formatDate(asset.updatedAt)}</td>
                          <td>
                            <input
                              type="text"
                              className="admin-asset-tags-input"
                              placeholder="Add tags…"
                              value={assetTagEdits[asset.filename] ?? (asset.tags ?? []).join(', ')}
                              onChange={e => setAssetTagEdits(t => ({ ...t, [asset.filename]: e.target.value }))}
                              onBlur={() => handleSaveAssetTags(asset.filename)}
                            />
                          </td>
                          <td>
                            <div className="admin-asset-actions-inline">
                              <button type="button" className="btn-admin-reset btn-admin-reset--compact" onClick={() => { navigator.clipboard.writeText(asset.url).catch(() => {}) }}>Copy URL</button>
                              <button type="button" className="btn-admin-remove" onClick={() => handleDeleteAsset(asset.filename)}>Delete</button>
                            </div>
                          </td>
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
              )}
              {opsMsg && <p className="admin-stats-note">{opsMsg}</p>}
            </section>
          )}

          {/* QR CODES */}
          {adminView === 'qr-codes' && (() => {
            if (!qrLoaded && !qrLoading) loadQrCodes()
            return (
              <section className="admin-panel-section" aria-label="QR Codes">
                <div className="admin-panel-head">
                  <h2>QR Codes</h2>
                  <p>Each QR code is a short redirect at <code>/qr/&lt;slug&gt;</code>. Scans are logged here instead of emailed.</p>
                </div>

                {/* Add new */}
                <div className="admin-form-row" style={{ gap: '8px', flexWrap: 'wrap', alignItems: 'flex-end' }}>
                  <div style={{ flex: '1 1 140px' }}>
                    <label style={{ display: 'block', marginBottom: 4, fontSize: '0.85em' }}>Label</label>
                    <input type="text" placeholder="Spotify Podcast" value={qrNewLabel} onChange={e => setQrNewLabel(e.target.value)} className="admin-input" />
                  </div>
                  <div style={{ flex: '1 1 120px' }}>
                    <label style={{ display: 'block', marginBottom: 4, fontSize: '0.85em' }}>Slug</label>
                    <input type="text" placeholder="spotify" value={qrNewSlug} onChange={e => setQrNewSlug(e.target.value)} className="admin-input" />
                  </div>
                  <div style={{ flex: '2 1 200px' }}>
                    <label style={{ display: 'block', marginBottom: 4, fontSize: '0.85em' }}>Destination URL</label>
                    <input type="url" placeholder="https://…" value={qrNewDest} onChange={e => setQrNewDest(e.target.value)} className="admin-input" />
                  </div>
                  <button type="button" className="btn-admin-save" onClick={handleCreateQrCode} disabled={!qrNewLabel || !qrNewSlug || !qrNewDest}>
                    Add QR Code
                  </button>
                </div>
                {qrMsg && <p className="admin-stats-note" style={{ color: '#c0392b' }}>{qrMsg}</p>}

                {qrLoading && <p className="admin-stats-note">Loading…</p>}

                {/* Code list */}
                {!qrLoading && qrCodes.length === 0 && <p className="admin-stats-note">No QR codes yet.</p>}
                {qrCodes.map(code => {
                  const scans = qrScans.filter(s => s.qrId === code.id)
                  const isEditing = qrEditId === code.id
                  return (
                    <div key={code.id} style={{ border: '1px solid var(--admin-border, #ddd)', borderRadius: 6, padding: '12px 16px', marginBottom: 12 }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
                        <span style={{ fontWeight: 600, flex: 1 }}>{code.label}</span>
                        <code style={{ fontSize: '0.82em', background: 'var(--admin-bg2, #f5f5f5)', padding: '2px 6px', borderRadius: 4 }}>/qr/{code.slug}</code>
                        <span style={{ fontSize: '0.82em', color: '#888' }}>{scans.length} scan{scans.length !== 1 ? 's' : ''}</span>
                        <span style={{ fontSize: '0.8em', color: code.active ? '#27ae60' : '#c0392b' }}>{code.active ? 'Active' : 'Inactive'}</span>
                        <button type="button" className="btn-admin-reset" style={{ fontSize: '0.8em' }} onClick={() => { setQrEditId(isEditing ? null : code.id); setQrEditLabel(code.label); setQrEditDest(code.destination) }}>
                          {isEditing ? 'Cancel' : 'Edit'}
                        </button>
                        <button type="button" className="btn-admin-reset" style={{ fontSize: '0.8em' }} onClick={() => handleToggleQrActive(code.id, !code.active)}>
                          {code.active ? 'Disable' : 'Enable'}
                        </button>
                        <button type="button" className="btn-admin-reset" style={{ fontSize: '0.8em' }} onClick={() => handleClearQrScans(code.id)}>
                          Clear scans
                        </button>
                        <button type="button" className="btn-admin-reset" style={{ fontSize: '0.8em', color: '#c0392b' }} onClick={() => handleDeleteQrCode(code.id)}>
                          Delete
                        </button>
                      </div>
                      <p style={{ margin: '4px 0 0', fontSize: '0.82em', color: '#888', wordBreak: 'break-all' }}>{code.destination}</p>
                      <QrCodeImage url={`${window.location.origin}/qr/${code.slug}`} label={code.label} />
                      {isEditing && (
                        <div style={{ display: 'flex', gap: 8, marginTop: 10, flexWrap: 'wrap' }}>
                          <input type="text" value={qrEditLabel} onChange={e => setQrEditLabel(e.target.value)} className="admin-input" style={{ flex: '1 1 140px' }} placeholder="Label" />
                          <input type="url" value={qrEditDest} onChange={e => setQrEditDest(e.target.value)} className="admin-input" style={{ flex: '2 1 200px' }} placeholder="Destination URL" />
                          <button type="button" className="btn-admin-save" onClick={() => handleSaveQrEdit(code.id)}>Save</button>
                        </div>
                      )}
                      {scans.length > 0 && (
                        <details style={{ marginTop: 10 }}>
                          <summary style={{ cursor: 'pointer', fontSize: '0.85em', color: '#555' }}>Scan history ({scans.length})</summary>
                          <div className="admin-visits-table-scroll" style={{ marginTop: 6 }}>
                            <table className="admin-visits-table">
                              <thead><tr><th>Time</th><th>IP</th><th>Device / UA</th></tr></thead>
                              <tbody>
                                {scans.slice(0, 100).map(s => (
                                  <tr key={s.id}>
                                    <td style={{ whiteSpace: 'nowrap' }}>{new Date(s.scannedAt).toLocaleString()}</td>
                                    <td>{s.ip}</td>
                                    <td style={{ maxWidth: 300, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{s.userAgent}</td>
                                  </tr>
                                ))}
                              </tbody>
                            </table>
                          </div>
                        </details>
                      )}
                    </div>
                  )
                })}
              </section>
            )
          })()}

          {/* SEO & REDIRECTS */}
          {adminView === 'seo' && (
            <section className="admin-panel-section" aria-label="SEO & Redirects">
              <div className="admin-panel-head">
                <h2>SEO &amp; Redirects</h2>
                <p>Control metadata, canonical URL, robots policy, sitemap paths, and short-link redirects.</p>
              </div>

              {renderSectionJumpNav('seo')}

              <div className="admin-section-header" id="seo-metadata">
                <h3>Metadata</h3>
                <p>Search title, descriptions, Open Graph image, canonical URL, robots policy, and sitemap paths.</p>
              </div>
              <div className="admin-array-row">
                <div className="admin-array-fields">
                  <div className="admin-field"><label htmlFor="seo-title">SEO Title</label><input id="seo-title" type="text" value={form.seo?.title ?? ''} onChange={e => updateSeoField('title', e.target.value)} /></div>
                  <div className="admin-field"><label htmlFor="seo-description">SEO Description</label><textarea id="seo-description" rows={3} value={form.seo?.description ?? ''} onChange={e => updateSeoField('description', e.target.value)} /></div>
                  <div className="admin-field"><label htmlFor="seo-og-title">Open Graph Title</label><input id="seo-og-title" type="text" value={form.seo?.ogTitle ?? ''} onChange={e => updateSeoField('ogTitle', e.target.value)} /></div>
                  <div className="admin-field"><label htmlFor="seo-og-description">Open Graph Description</label><textarea id="seo-og-description" rows={3} value={form.seo?.ogDescription ?? ''} onChange={e => updateSeoField('ogDescription', e.target.value)} /></div>
                  <div className="admin-field">
                    <label htmlFor="seo-og-image">Open Graph Image URL</label>
                    <input id="seo-og-image" type="text" value={form.seo?.ogImage ?? ''} onChange={e => updateSeoField('ogImage', e.target.value)} />
                    {renderImageAssetSelector(form.seo?.ogImage, value => updateSeoField('ogImage', value), 'seo-og-image-asset')}
                  </div>
                  <div className="admin-field"><label htmlFor="seo-canonical">Canonical URL</label><input id="seo-canonical" type="text" value={form.seo?.canonicalUrl ?? ''} onChange={e => updateSeoField('canonicalUrl', e.target.value)} /></div>
                  <div className="admin-field"><label htmlFor="seo-robots">Robots Policy</label><input id="seo-robots" type="text" value={form.seo?.robotsPolicy ?? ''} onChange={e => updateSeoField('robotsPolicy', e.target.value)} /></div>
                  <div className="admin-field">
                    <label htmlFor="seo-sitemap">Sitemap Paths (one per line)</label>
                    <textarea id="seo-sitemap" rows={4} value={(form.seo?.sitemapPaths ?? []).join('\n')} onChange={e => updateSeoField('sitemapPaths', e.target.value.split('\n').map(v => v.trim()).filter(Boolean))} />
                  </div>
                </div>
              </div>
              <div className="admin-section-header" id="seo-redirects">
                <h3>Redirect Manager</h3>
                <p>Maintain first-party short links like /spotify without code changes.</p>
              </div>
              {(form.redirects ?? []).map(rule => (
                <div key={rule.id} className="admin-array-row">
                  <div className="admin-array-fields">
                    <div className="admin-field"><label htmlFor={`redirect-path-${rule.id}`}>Path</label><input id={`redirect-path-${rule.id}`} type="text" value={rule.path} onChange={e => updateRedirectRule(rule.id, 'path', e.target.value)} /></div>
                    <div className="admin-field"><label htmlFor={`redirect-target-${rule.id}`}>Target URL</label><input id={`redirect-target-${rule.id}`} type="text" value={rule.target} onChange={e => updateRedirectRule(rule.id, 'target', e.target.value)} /></div>
                    <div className="admin-field">
                      <label htmlFor={`redirect-status-${rule.id}`}>Status</label>
                      <select id={`redirect-status-${rule.id}`} value={rule.statusCode} onChange={e => updateRedirectRule(rule.id, 'statusCode', Number(e.target.value))}>
                        <option value={301}>301 Permanent</option>
                        <option value={302}>302 Temporary</option>
                      </select>
                    </div>
                  </div>
                  <button type="button" className="btn-admin-remove" onClick={() => removeRedirectRule(rule.id)}>Remove</button>
                </div>
              ))}
              <button type="button" className="btn-admin-add" onClick={addRedirectRule}>+ Add Redirect</button>
              {renderSaveStatus()}
            </section>
          )}

          {/* LEGAL */}
          {adminView === 'legal' && (
            <section className="admin-panel-section" aria-label="Legal Pages">
              <div className="admin-panel-head">
                <h2>Legal Pages</h2>
                <p>Edit Privacy and Terms pages from admin.</p>
              </div>

              {renderSectionJumpNav('legal')}

              <div className="admin-section-header" id="legal-privacy">
                <h3>Privacy</h3>
                <p>Edit the title and body content for the privacy page.</p>
              </div>
              <div className="admin-array-row">
                <div className="admin-array-fields">
                  <div className="admin-field"><label htmlFor="legal-privacy-title">Privacy Title</label><input id="legal-privacy-title" type="text" value={form.legal?.privacyTitle ?? ''} onChange={e => updateLegalField('privacyTitle', e.target.value)} /></div>
                  <div className="admin-field">
                    <label htmlFor="legal-privacy-body">Privacy Body (one paragraph per line)</label>
                    <textarea id="legal-privacy-body" rows={4} value={(form.legal?.privacyBody ?? []).join('\n')} onChange={e => updateLegalField('privacyBody', e.target.value.split('\n').map(v => v.trim()).filter(Boolean))} />
                  </div>
                </div>
              </div>

              <div className="admin-section-header" id="legal-terms">
                <h3>Terms</h3>
                <p>Edit the title and body content for the terms page.</p>
              </div>
              <div className="admin-array-row">
                <div className="admin-array-fields">
                  <div className="admin-field"><label htmlFor="legal-terms-title">Terms Title</label><input id="legal-terms-title" type="text" value={form.legal?.termsTitle ?? ''} onChange={e => updateLegalField('termsTitle', e.target.value)} /></div>
                  <div className="admin-field">
                    <label htmlFor="legal-terms-body">Terms Body (one paragraph per line)</label>
                    <textarea id="legal-terms-body" rows={4} value={(form.legal?.termsBody ?? []).join('\n')} onChange={e => updateLegalField('termsBody', e.target.value.split('\n').map(v => v.trim()).filter(Boolean))} />
                  </div>
                </div>
              </div>
              {renderSaveStatus()}
            </section>
          )}

          {/* SECURITY */}
          {adminView === 'security' && (
            <section className="admin-panel-section" aria-label="Security">
              <div className="admin-panel-head">
                <h2>Security</h2>
                <p>Manage two-factor authentication for admin login.</p>
              </div>
              {totpEnabled === null && <p className="admin-stats-note">Loading…</p>}
              {totpEnabled === false && !totpSetupQr && (
                <div className="admin-array-row">
                  <div className="admin-array-fields">
                    <p className="admin-stats-note">2FA is currently <strong>off</strong>. Enable it to require an authenticator app at every login.</p>
                    <button type="button" className="btn-primary" onClick={handleTotpSetupInit}>Enable 2FA</button>
                  </div>
                </div>
              )}
              {totpSetupQr && (
                <div className="admin-array-row">
                  <div className="admin-array-fields">
                    <p className="admin-stats-note">Scan this QR code with your authenticator app, then enter the 6-digit code below to confirm.</p>
                    <img src={totpSetupQr} alt="TOTP QR code" style={{ width: 200, height: 200, display: 'block', margin: '0.5rem 0' }} />
                    {totpSetupSecret && <p className="admin-stats-note" style={{ wordBreak: 'break-all' }}>Manual entry key: <code>{totpSetupSecret}</code></p>}
                    <form onSubmit={handleTotpSetupConfirm} style={{ display: 'flex', gap: '0.5rem', alignItems: 'flex-end', flexWrap: 'wrap' }}>
                      <div className="admin-field" style={{ flex: 1, minWidth: 160 }}>
                        <label htmlFor="totp-confirm-code">Confirmation Code</label>
                        <input id="totp-confirm-code" type="text" inputMode="numeric" value={totpConfirmCode} onChange={e => setTotpConfirmCode(e.target.value)} placeholder="000000" autoFocus required />
                      </div>
                      <button type="submit" className="btn-primary">Confirm &amp; Enable</button>
                      <button type="button" className="btn-secondary" onClick={() => { setTotpSetupQr(null); setTotpSetupSecret(null) }}>Cancel</button>
                    </form>
                  </div>
                </div>
              )}
              {totpEnabled === true && !totpSetupQr && (
                <div className="admin-array-row">
                  <div className="admin-array-fields">
                    <p className="admin-stats-note">2FA is currently <strong>on</strong>. A code from your authenticator app is required at every login.</p>
                    <div style={{ display: 'flex', gap: '0.75rem', flexWrap: 'wrap' }}>
                      <button type="button" className="btn-admin-reset" onClick={handleTotpRegenRecovery}>Regenerate Recovery Codes</button>
                      <button type="button" className="btn-admin-remove" onClick={handleTotpDisable}>Disable 2FA</button>
                    </div>
                  </div>
                </div>
              )}
              {totpRecoveryCodes && (
                <div className="admin-array-row">
                  <div className="admin-array-fields">
                    <p className="admin-stats-note"><strong>Save these recovery codes somewhere safe.</strong> Each can be used once instead of the 6-digit code if you lose access to your authenticator app. They will not be shown again.</p>
                    <ul style={{ fontFamily: 'monospace', lineHeight: 2 }}>{totpRecoveryCodes.map(c => <li key={c}>{c}</li>)}</ul>
                    <button type="button" className="btn-secondary" onClick={() => setTotpRecoveryCodes(null)}>I've saved these</button>
                  </div>
                </div>
              )}
              {totpMsg && <p className="admin-stats-note">{totpMsg}</p>}
            </section>
          )}

          {/* BRAND KIT */}
          {adminView === 'brand' && (
            <section className="admin-panel-section admin-brand-kit" aria-label="Brand kit">
              <div className="admin-panel-head">
                <h2>Brand Kit</h2>
                <p>Reference palette, typography, and implementation status for the live site.</p>
              </div>
              <div className="admin-brand-hero">
                <img src="/images/podcast-art.jpeg" alt="Verse by Verse with Nate artwork" />
                <div>
                  <p className="admin-brand-kicker">Verse by Verse with Nate</p>
                  <h3>A Journey Through Scripture</h3>
                  <p className="admin-brand-tagline">Verse by verse. Nugget by nugget.</p>
                </div>
              </div>
              <div className="admin-brand-grid">
                <article className="admin-brand-card">
                  <h3>Color Palette</h3>
                  <div className="admin-brand-swatches">
                    {BRAND_KIT_SWATCHES.map(swatch => (
                      <div key={swatch.hex} className="admin-brand-swatch">
                        <div className="admin-brand-swatch-block" style={{ background: swatch.hex }} />
                        <div className="admin-brand-swatch-copy">
                          <strong>{swatch.name}</strong>
                          <span>{swatch.hex}</span>
                          <p>{swatch.role}</p>
                        </div>
                      </div>
                    ))}
                  </div>
                </article>
                <article className="admin-brand-card">
                  <h3>Typography</h3>
                  <div className="admin-brand-type-list">
                    {BRAND_KIT_TYPE.map(item => (
                      <div key={item.label} className="admin-brand-type-row">
                        <p className="admin-brand-type-label">{item.label}</p>
                        <p className="admin-brand-type-spec">{item.spec}</p>
                        <p className="admin-brand-type-sample">{item.sample}</p>
                      </div>
                    ))}
                  </div>
                </article>
              </div>
              <article className="admin-brand-card admin-brand-card--verify">
                <h3>Verified On Site</h3>
                <ul className="admin-brand-checklist">
                  {BRAND_KIT_VERIFICATION.map(item => <li key={item}>{item}</li>)}
                </ul>
                <p className="admin-brand-note">This admin panel reflects the live implementation now loaded from the shared font import and brand tokens.</p>
              </article>
            </section>
          )}

        </main>

        {/* ── Live preview pane ── */}
        {previewOpen && (
          <div className="admin-preview-pane">
            <div className="admin-preview-toolbar">
              <span className="admin-preview-label">Live Preview</span>
              <span className="admin-preview-hint">Updates as you type</span>
            </div>
            <iframe
              ref={previewIframeRef}
              src="/preview"
              title="Site preview"
              className="admin-preview-iframe"
              onLoad={() => {
                // Once the iframe loads, immediately push the current form state and view
                previewIframeRef.current?.contentWindow?.postMessage(
                  { type: 'admin-preview-content', content: form, view: adminView },
                  window.location.origin
                )
              }}
            />
          </div>
        )}

      </div>
    </div>
  )
}
