---
import AppLayout from '../../layouts/AppLayout.astro';
import {
  getAllInvites, getAllUsersPublic, revokeInvite,
  createInvite, updateUserRole, deactivateUser, updateUserAdminFields,
  getUserPublicById, getAllJoinRequests,
  createPulse, updatePulse, publishPulse, closePulse, deletePulse,
  getAllPulses, getPulseById, getPulseWithCounts,
  createRoadmapItem, updateRoadmapItem, deleteRoadmapItem,
  setRoadmapAttributions, getAllRoadmapItems, getRoadmapItem,
  createEvent, updateEvent, deleteEvent, getAllEvents, getEventBySlug,
  getEventRsvpCount, getEventById,
  createDispatch, updateDispatch, publishDispatch, archiveDispatch,
  deleteDispatch, getAllDispatchesForAdmin, getDispatchById,
  recordActivity, getAllActivityForAdmin,
} from '../../lib/db';
import { generateInviteToken, inviteExpiresAt } from '../../lib/auth';
import { fmtDate } from '../../lib/markdown';
import { parseFocusTags } from '../../lib/format';
import { notifyPulseOpened } from '../../lib/notify';
import PulsesTab from '../../components/admin/PulsesTab.astro';
import RoadmapTab from '../../components/admin/RoadmapTab.astro';
import EventsTab from '../../components/admin/EventsTab.astro';
import ActivityTab from '../../components/admin/ActivityTab.astro';
import DispatchesTab from '../../components/admin/DispatchesTab.astro';
import UserEditTab from '../../components/admin/UserEditTab.astro';
import type { Role, RoadmapStatus, EventKind, DispatchKind, DispatchStatus } from '../../lib/db';

const user = Astro.locals.user;

// Guard: fenja only
if (user.role !== 'fenja') {
  return Astro.redirect('/');
}

const tab = Astro.url.searchParams.get('tab') ?? 'invitations';

let newInviteToken: string | null = null;
let formError: string | null = null;
let actionMsg: string | null = null;

if (Astro.request.method === 'POST') {
  const data = await Astro.request.formData();
  const action = String(data.get('action') ?? '');

  if (action === 'create_invite') {
    const name         = String(data.get('name') ?? '').trim();
    const email        = String(data.get('email') ?? '').trim().toLowerCase();
    const organisation = String(data.get('organisation') ?? '').trim();
    const role         = String(data.get('role') ?? '') as Role;

    if (!name || !email || !organisation || !['pilot','cab','fenja'].includes(role)) {
      formError = 'All fields are required.';
    } else {
      const { token, tokenHash } = generateInviteToken();
      createInvite({
        token_hash: tokenHash,
        email,
        name,
        organisation,
        role,
        expires_at: inviteExpiresAt(),
        created_by_user_id: user.id,
      });
      newInviteToken = `${Astro.url.origin}/invite/${token}`;
    }
  } else if (action === 'revoke_invite') {
    const id = Number(data.get('invite_id'));
    if (id) revokeInvite(id);
    return Astro.redirect('/admin?tab=invitations&msg=revoked');
  } else if (action === 'change_role') {
    const userId = Number(data.get('user_id'));
    const newRole = String(data.get('role')) as Role;
    if (userId && ['pilot','cab','fenja'].includes(newRole)) {
      updateUserRole(userId, newRole);
    }
    return Astro.redirect('/admin?tab=participants&msg=updated');
  } else if (action === 'deactivate_user') {
    const userId = Number(data.get('user_id'));
    if (userId && userId !== user.id) deactivateUser(userId);
    return Astro.redirect('/admin?tab=participants&msg=deactivated');

  // ── User profile edit (title / pull_quote / focus_tags) ─────
  } else if (action === 'update_user_admin') {
    const userId = Number(data.get('user_id'));
    if (userId) {
      const title      = String(data.get('title') ?? '').trim() || null;
      const pullQuote  = String(data.get('pull_quote') ?? '').trim() || null;
      const tagsInput  = String(data.get('focus_tags') ?? '');
      const focusTags  = parseFocusTags(tagsInput);
      updateUserAdminFields(userId, { title, pull_quote: pullQuote, focus_tags: focusTags });
    }
    return Astro.redirect(`/admin?tab=participants&edit=${userId}&msg=user_updated`);

  // ── Dispatches ───────────────────────────────────────────────
  } else if (action === 'create_dispatch' || action === 'update_dispatch') {
    const title    = String(data.get('title') ?? '').trim();
    const body     = String(data.get('body') ?? '');
    const excerpt  = String(data.get('excerpt') ?? '').trim() || null;
    const kind     = String(data.get('kind') ?? '') as DispatchKind;
    const authorId = Number(data.get('author_id'));
    const status   = String(data.get('status') ?? 'draft') as DispatchStatus;

    // Parse optional poll attachment fields.
    const pollExplicit = String(data.get('poll_explicit') ?? '') === '1';
    const pollQuestion = String(data.get('poll_question') ?? '').trim();
    const pollOpts     = [0, 1, 2, 3]
      .map(i => String(data.get(`poll_option_${i}`) ?? '').trim())
      .filter(s => s.length > 0);
    const pollOpens    = String(data.get('poll_opens_at') ?? '');
    const pollCloses   = String(data.get('poll_closes_at') ?? '');
    let pollInput: { question: string; options: string[]; opens_at: string; closes_at: string } | null = null;
    if (pollQuestion && pollOpts.length >= 2 && pollOpens && pollCloses) {
      pollInput = {
        question: pollQuestion,
        options: pollOpts,
        opens_at: toSqlDate(pollOpens),
        closes_at: toSqlDate(pollCloses),
      };
    }

    if (!title || !body || !['decision','update','behind_the_scenes','note'].includes(kind)) {
      formError = 'Title, body, and a valid kind are required.';
    } else if (action === 'create_dispatch') {
      createDispatch({ title, body, excerpt, kind, author_id: authorId || user.id, status, poll: pollInput });
      return Astro.redirect('/admin?tab=dispatches&msg=dispatch_created');
    } else {
      const id = Number(data.get('dispatch_id'));
      if (id) updateDispatch(id, {
        title, body, excerpt, kind, author_id: authorId || user.id,
        poll: pollInput, pollExplicit,
      });
      return Astro.redirect(`/admin?tab=dispatches&edit=${id}&msg=dispatch_updated`);
    }
  } else if (action === 'publish_dispatch') {
    const id = Number(data.get('dispatch_id'));
    if (id) publishDispatch(id);
    return Astro.redirect('/admin?tab=dispatches&msg=dispatch_published');
  } else if (action === 'archive_dispatch') {
    const id = Number(data.get('dispatch_id'));
    if (id) archiveDispatch(id);
    return Astro.redirect('/admin?tab=dispatches&msg=dispatch_archived');
  } else if (action === 'delete_dispatch') {
    const id = Number(data.get('dispatch_id'));
    if (id) deleteDispatch(id);
    return Astro.redirect('/admin?tab=dispatches&msg=dispatch_deleted');

  // ── Pulses ───────────────────────────────────────────────────
  } else if (action === 'create_pulse' || action === 'update_pulse') {
    const question = String(data.get('question') ?? '').trim();
    const context  = String(data.get('context') ?? '').trim() || null;
    const opens_at = toSqlDate(String(data.get('opens_at') ?? ''));
    const closes_at = toSqlDate(String(data.get('closes_at') ?? ''));
    const publish = String(data.get('publish') ?? '') === '1';
    const options = [0, 1, 2, 3]
      .map(i => String(data.get(`option_${i}`) ?? '').trim())
      .filter(s => s.length > 0);

    if (!question || options.length < 2 || !opens_at || !closes_at) {
      formError = 'Question, at least 2 options, and both dates are required.';
    } else if (action === 'create_pulse') {
      const id = createPulse({
        question, context, options, opens_at, closes_at,
        status: publish ? 'open' : 'draft',
        created_by: user.id,
      });
      if (publish) {
        recordActivity(user.id, 'pulse_opened', 'pulse', id);
        const p = getPulseById(id);
        if (p) notifyPulseOpened(p);
      }
      return Astro.redirect('/admin?tab=pulses&msg=pulse_created');
    } else {
      const id = Number(data.get('pulse_id'));
      if (id) updatePulse(id, { question, context, options, opens_at, closes_at });
      return Astro.redirect('/admin?tab=pulses&msg=pulse_updated');
    }
  } else if (action === 'publish_pulse') {
    const id = Number(data.get('pulse_id'));
    if (id) {
      publishPulse(id);
      recordActivity(user.id, 'pulse_opened', 'pulse', id);
      const p = getPulseById(id);
      if (p) notifyPulseOpened(p);
    }
    return Astro.redirect('/admin?tab=pulses&msg=pulse_published');
  } else if (action === 'close_pulse') {
    const id = Number(data.get('pulse_id'));
    if (id) closePulse(id);
    return Astro.redirect('/admin?tab=pulses&msg=pulse_closed');
  } else if (action === 'delete_pulse') {
    const id = Number(data.get('pulse_id'));
    if (id) deletePulse(id);
    return Astro.redirect('/admin?tab=pulses&msg=pulse_deleted');

  // ── Roadmap ──────────────────────────────────────────────────
  } else if (action === 'create_roadmap' || action === 'update_roadmap') {
    const title         = String(data.get('title') ?? '').trim();
    const description   = String(data.get('description') ?? '').trim();
    const status        = String(data.get('status') ?? '') as RoadmapStatus;
    const target        = String(data.get('target') ?? '').trim() || null;
    const displayOrder  = Number(data.get('display_order') ?? 0);
    const metadataText  = String(data.get('metadata_text') ?? '').trim() || null;
    const attributedIds = data.getAll('attributed_user_ids').map(v => Number(v)).filter(Boolean);

    if (!title || !['shipping','in_beta','exploring','considering'].includes(status)) {
      formError = 'Title and status are required.';
    } else if (action === 'create_roadmap') {
      const id = createRoadmapItem({ title, description, status, target, display_order: displayOrder, metadata_text: metadataText });
      setRoadmapAttributions(id, attributedIds);
      if (status === 'shipping') recordActivity(user.id, 'roadmap_shipped', 'roadmap', id);
      return Astro.redirect('/admin?tab=roadmap&msg=roadmap_created');
    } else {
      const id = Number(data.get('roadmap_id'));
      if (id) {
        const { shippedNow } = updateRoadmapItem(id, {
          title, description, status, target, display_order: displayOrder, metadata_text: metadataText,
        });
        setRoadmapAttributions(id, attributedIds);
        if (shippedNow) recordActivity(user.id, 'roadmap_shipped', 'roadmap', id);
      }
      return Astro.redirect('/admin?tab=roadmap&msg=roadmap_updated');
    }
  } else if (action === 'delete_roadmap') {
    const id = Number(data.get('roadmap_id'));
    if (id) deleteRoadmapItem(id);
    return Astro.redirect('/admin?tab=roadmap&msg=roadmap_deleted');
  } else if (action === 'move_roadmap') {
    const id = Number(data.get('roadmap_id'));
    const dir = String(data.get('direction') ?? '');
    if (id && (dir === 'up' || dir === 'down')) {
      moveRoadmapItem(id, dir);
    }
    return Astro.redirect('/admin?tab=roadmap&msg=roadmap_moved');

  // ── Events ───────────────────────────────────────────────────
  } else if (action === 'create_event' || action === 'update_event') {
    const slug        = String(data.get('slug') ?? '').trim().toLowerCase();
    const title       = String(data.get('title') ?? '').trim();
    const kind        = String(data.get('kind') ?? '') as EventKind;
    const description = String(data.get('description') ?? '').trim();
    const location    = String(data.get('location') ?? '').trim();
    const starts_at   = toSqlDate(String(data.get('starts_at') ?? ''));
    const ends_at     = String(data.get('ends_at') ?? '').trim()
      ? toSqlDate(String(data.get('ends_at') ?? ''))
      : null;
    const capacity    = Number(data.get('capacity') ?? 0) || null;
    const photo_url   = String(data.get('photo_url') ?? '').trim() || null;

    if (!slug || !title || !starts_at || !['dinner','office_hours','summit','virtual'].includes(kind)) {
      formError = 'Slug, title, kind, and start date are required.';
    } else if (action === 'create_event') {
      createEvent({ slug, title, kind, description, location, starts_at, ends_at, capacity, photo_url, created_by: user.id });
      return Astro.redirect('/admin?tab=events&msg=event_created');
    } else {
      const id = Number(data.get('event_id'));
      if (id) updateEvent(id, { title, kind, description, location, starts_at, ends_at, capacity, photo_url });
      return Astro.redirect('/admin?tab=events&msg=event_updated');
    }
  } else if (action === 'delete_event') {
    const id = Number(data.get('event_id'));
    if (id) deleteEvent(id);
    return Astro.redirect('/admin?tab=events&msg=event_deleted');
  }
}

/** "2026-05-11T12:00" (datetime-local input) → "2026-05-11 12:00:00" (SQL UTC). */
function toSqlDate(input: string): string {
  if (!input) return '';
  // datetime-local format: YYYY-MM-DDTHH:MM (no timezone). Treat as UTC.
  return input.replace('T', ' ') + (input.length === 16 ? ':00' : '');
}

/** Swap display_order with the neighbour in the same status column. */
function moveRoadmapItem(id: number, dir: 'up' | 'down'): void {
  const all = getAllRoadmapItems();
  const item = all.find(r => r.id === id);
  if (!item) return;
  const sameStatus = all
    .filter(r => r.status === item.status)
    .sort((a, b) => a.display_order - b.display_order || a.id - b.id);
  const idx = sameStatus.findIndex(r => r.id === id);
  const swapIdx = dir === 'up' ? idx - 1 : idx + 1;
  if (swapIdx < 0 || swapIdx >= sameStatus.length) return;
  const other = sameStatus[swapIdx];
  updateRoadmapItem(item.id, {
    title: item.title, description: item.description, status: item.status,
    target: item.target, display_order: other.display_order, metadata_text: item.metadata_text,
  });
  updateRoadmapItem(other.id, {
    title: other.title, description: other.description, status: other.status,
    target: other.target, display_order: item.display_order, metadata_text: other.metadata_text,
  });
}

const invites      = getAllInvites();
const users        = getAllUsersPublic();
const joinRequests = getAllJoinRequests();

const editId = Number(Astro.url.searchParams.get('edit') ?? 0) || null;
const viewId = Number(Astro.url.searchParams.get('view') ?? 0) || null;

const fenjaUsers = users.filter(u => u.role === 'fenja');
const editingUser = tab === 'participants' && editId ? getUserPublicById(editId) : null;

const dispatches      = tab === 'dispatches' ? getAllDispatchesForAdmin() : [];
const dispatchEditing = tab === 'dispatches' && editId ? getDispatchById(editId) : null;
const dispatchEditingPoll = dispatchEditing?.pulse_id ? getPulseById(dispatchEditing.pulse_id) : null;

// Per-tab data
const pulses        = tab === 'pulses'    ? getAllPulses() : [];
const pulseEditing  = tab === 'pulses' && editId ? getPulseById(editId) : null;
const pulseViewing  = tab === 'pulses' && viewId ? getPulseWithCounts(viewId, user.id) : null;

const roadmapItems  = tab === 'roadmap'   ? getAllRoadmapItems() : [];
const roadmapEditing = tab === 'roadmap' && editId ? getRoadmapItem(editId) : null;
const cabUsers      = tab === 'roadmap' ? users.filter(u => u.role === 'cab' || u.role === 'pilot') : [];

const events        = tab === 'events'    ? getAllEvents() : [];
const eventEditing  = tab === 'events' && editId ? getEventById(editId) : null;
const eventViewing  = tab === 'events' && viewId ? getEventById(viewId) : null;
const eventViewingRsvps = tab === 'events' && viewId && eventViewing
  ? getEventRsvpCount(eventViewing.slug)
  : null;

const activityRows  = tab === 'activity'  ? getAllActivityForAdmin(200) : [];

const MSGS: Record<string, string> = {
  revoked:             'Invite revoked.',
  updated:             'Role updated.',
  deactivated:         'User deactivated.',
  user_updated:        'Member profile updated.',
  pulse_created:       'Pulse saved.',
  pulse_updated:       'Pulse updated.',
  pulse_published:     'Pulse published — members notified.',
  pulse_closed:        'Pulse closed.',
  pulse_deleted:       'Pulse deleted.',
  roadmap_created:     'Roadmap item saved.',
  roadmap_updated:     'Roadmap item updated.',
  roadmap_deleted:     'Roadmap item deleted.',
  roadmap_moved:       'Roadmap reordered.',
  event_created:       'Event saved.',
  event_updated:       'Event updated.',
  event_deleted:       'Event deleted.',
  dispatch_created:    'Dispatch saved.',
  dispatch_updated:    'Dispatch updated.',
  dispatch_published:  'Dispatch published.',
  dispatch_archived:   'Dispatch archived.',
  dispatch_deleted:    'Dispatch deleted.',
};
actionMsg = Astro.url.searchParams.get('msg');
---
<AppLayout title="Admin" user={user}>
  <div class="page">

    <header class="page-header">
      <p class="label-sm eyebrow">Admin</p>
      <h1 class="display-md page-title">Control panel.</h1>
    </header>

    <!-- Tabs (Pulses entity merged into Dispatches — polls now attach to articles) -->
    <div class="tabs">
      <a href="/admin?tab=dispatches"   class:list={['tab label-sm', { active: tab === 'dispatches' }]}>Dispatches</a>
      <a href="/admin?tab=roadmap"      class:list={['tab label-sm', { active: tab === 'roadmap' }]}>Roadmap</a>
      <a href="/admin?tab=events"       class:list={['tab label-sm', { active: tab === 'events' }]}>Events</a>
      <a href="/admin?tab=invitations"  class:list={['tab label-sm', { active: tab === 'invitations' }]}>Invitations</a>
      <a href="/admin?tab=participants" class:list={['tab label-sm', { active: tab === 'participants' }]}>Participants</a>
      <a href="/admin?tab=join"         class:list={['tab label-sm', { active: tab === 'join' }]}>
        Join requests {joinRequests.length > 0 && <span class="tab-count">{joinRequests.length}</span>}
      </a>
      <a href="/admin?tab=activity"     class:list={['tab label-sm', { active: tab === 'activity' }]}>Activity</a>
    </div>

    {actionMsg && (
      <p class="action-msg body-sm" role="status">
        {MSGS[actionMsg] ?? ''}
      </p>
    )}

    {formError && (
      <p class="form-error body-sm" role="alert">{formError}</p>
    )}

    <!-- Invitations tab -->
    {tab === 'invitations' && (
      <div class="tab-content">

        {/* New invite form */}
        <section class="section">
          <h2 class="label-sm section-heading">Generate invite link</h2>

          {formError && (
            <p class="form-error body-sm" role="alert">{formError}</p>
          )}

          {newInviteToken && (
            <div class="invite-result">
              <p class="label-sm invite-result-label">Copy this link and send it personally. It expires in 14 days and is single-use.</p>
              <div class="invite-link-row">
                <code class="invite-link body-sm">{newInviteToken}</code>
                <button
                  type="button"
                  class="copy-btn label-sm"
                  data-copy={newInviteToken}
                  onclick="navigator.clipboard.writeText(this.dataset.copy);this.textContent='Copied'"
                >
                  Copy
                </button>
              </div>
            </div>
          )}

          <form method="POST" class="invite-form" novalidate>
            <input type="hidden" name="action" value="create_invite" />
            <div class="form-grid">
              <div class="field">
                <label for="name" class="label-sm field-label">Name</label>
                <input type="text" id="name" name="name" class="input body-md" required />
              </div>
              <div class="field">
                <label for="email" class="label-sm field-label">Email</label>
                <input type="email" id="email" name="email" class="input body-md" required />
              </div>
              <div class="field">
                <label for="organisation" class="label-sm field-label">Organisation</label>
                <input type="text" id="organisation" name="organisation" class="input body-md" required />
              </div>
              <div class="field">
                <label for="role" class="label-sm field-label">Role</label>
                <select id="role" name="role" class="select body-md" required>
                  <option value="pilot">Pilot</option>
                  <option value="cab">CAB</option>
                  <option value="fenja">Fenja</option>
                </select>
              </div>
            </div>
            <button type="submit" class="btn-primary label-sm">Generate link</button>
          </form>
        </section>

        {/* Invite table */}
        <section class="section">
          <h2 class="label-sm section-heading">Outstanding invites</h2>
          {invites.filter((i) => !i.used_at).length === 0 ? (
            <p class="body-sm empty-msg">No outstanding invites.</p>
          ) : (
            <table class="data-table">
              <thead>
                <tr>
                  <th class="label-sm">Name</th>
                  <th class="label-sm">Email</th>
                  <th class="label-sm">Organisation</th>
                  <th class="label-sm">Role</th>
                  <th class="label-sm">Expires</th>
                  <th class="label-sm">Action</th>
                </tr>
              </thead>
              <tbody>
                {invites.filter((i) => !i.used_at).map((invite) => (
                  <tr>
                    <td class="body-sm">{invite.name}</td>
                    <td class="body-sm">{invite.email}</td>
                    <td class="body-sm">{invite.organisation}</td>
                    <td class="body-sm" style="text-transform:capitalize">{invite.role}</td>
                    <td class="body-sm">{fmtDate(invite.expires_at)}</td>
                    <td>
                      <form method="POST" class="inline-form">
                        <input type="hidden" name="action" value="revoke_invite" />
                        <input type="hidden" name="invite_id" value={invite.id} />
                        <button type="submit" class="danger-btn label-sm">Revoke</button>
                      </form>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </section>

      </div>
    )}

    <!-- Participants tab -->
    {tab === 'participants' && editingUser && (
      <UserEditTab member={editingUser} />
    )}

    {tab === 'participants' && !editingUser && (
      <div class="tab-content">
        <section class="section">
          <h2 class="label-sm section-heading">All participants</h2>
          <table class="data-table">
            <thead>
              <tr>
                <th class="label-sm">Name</th>
                <th class="label-sm">Email</th>
                <th class="label-sm">Organisation</th>
                <th class="label-sm">Role</th>
                <th class="label-sm">Last seen</th>
                <th class="label-sm">Actions</th>
              </tr>
            </thead>
            <tbody>
              {users.map((u) => (
                <tr class:list={[{ self: u.id === user.id }]}>
                  <td class="body-sm">{u.name}</td>
                  <td class="body-sm">{u.email}</td>
                  <td class="body-sm">{u.organisation}</td>
                  <td>
                    {u.id !== user.id ? (
                      <form method="POST" class="inline-form role-form">
                        <input type="hidden" name="action" value="change_role" />
                        <input type="hidden" name="user_id" value={u.id} />
                        <select name="role" class="select-inline label-sm" onchange="this.form.submit()">
                          <option value="pilot" selected={u.role === 'pilot'}>Pilot</option>
                          <option value="cab"   selected={u.role === 'cab'}>CAB</option>
                          <option value="fenja" selected={u.role === 'fenja'}>Fenja</option>
                        </select>
                      </form>
                    ) : (
                      <span class="body-sm" style="text-transform:capitalize">{u.role}</span>
                    )}
                  </td>
                  <td class="body-sm muted">
                    {u.last_seen_at ? fmtDate(u.last_seen_at) : 'Never'}
                  </td>
                  <td class="action-cell">
                    <a href={`/admin?tab=participants&edit=${u.id}`} class="action-link label-sm">Edit</a>
                    {u.id !== user.id && (
                      <form method="POST" class="inline-form">
                        <input type="hidden" name="action" value="deactivate_user" />
                        <input type="hidden" name="user_id" value={u.id} />
                        <button type="submit" class="danger-btn label-sm"
                          onclick="return confirm('Deactivate this user?')">
                          Deactivate
                        </button>
                      </form>
                    )}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </section>
      </div>
    )}

    <!-- Join requests tab -->
    {tab === 'join' && (
      <div class="tab-content">
        <section class="section">
          <h2 class="label-sm section-heading">Join requests</h2>
          <p class="body-sm section-note">
            Users who clicked "I want to join" on the home page. Use this to prioritise
            follow-up and generate invite links.
          </p>
          {joinRequests.length === 0 ? (
            <p class="body-sm empty-msg">No join requests yet.</p>
          ) : (
            <table class="data-table">
              <thead>
                <tr>
                  <th class="label-sm">Name</th>
                  <th class="label-sm">Email</th>
                  <th class="label-sm">Organisation</th>
                  <th class="label-sm">Requested</th>
                </tr>
              </thead>
              <tbody>
                {joinRequests.map((jr) => (
                  <tr>
                    <td class="body-sm">{jr.user_name}</td>
                    <td class="body-sm">{jr.user_email}</td>
                    <td class="body-sm">{jr.user_organisation}</td>
                    <td class="body-sm muted">{fmtDate(jr.created_at)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </section>
      </div>
    )}

    {tab === 'pulses' && (
      <PulsesTab pulses={pulses} editing={pulseEditing} viewing={pulseViewing} />
    )}

    {tab === 'roadmap' && (
      <RoadmapTab items={roadmapItems} editing={roadmapEditing} cabUsers={cabUsers} />
    )}

    {tab === 'events' && (
      <EventsTab events={events} editing={eventEditing} viewing={eventViewing} viewingRsvps={eventViewingRsvps} />
    )}

    {tab === 'activity' && (
      <ActivityTab rows={activityRows} />
    )}

    {tab === 'dispatches' && (
      <DispatchesTab dispatches={dispatches} editing={dispatchEditing} editingPoll={dispatchEditingPoll} fenjaUsers={fenjaUsers} currentUserId={user.id} />
    )}

  </div>
</AppLayout>

<style>
  .page {
    padding: var(--space-12) var(--space-20) var(--space-16);
    max-width: var(--content-max);
    margin: 0 auto;
  }

  /* ── Header ──────────────────────────────────────────────────────── */
  .page-header {
    max-width: 44rem;
    margin-bottom: var(--space-8);
  }

  .eyebrow {
    letter-spacing: var(--tracking-wider);
    text-transform: uppercase;
    color: var(--on-surface-muted);
    margin-bottom: var(--space-3);
  }

  .page-title { margin: 0; }

  /* ── Tabs ────────────────────────────────────────────────────────── */
  .tabs {
    display: flex;
    gap: var(--space-1);
    margin-bottom: var(--space-8);
    border-bottom: var(--ghost-border);
    padding-bottom: var(--space-2);
  }

  .tab {
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-sm);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--on-surface-muted);
    text-decoration: none;
    border-bottom: none;
    transition: color var(--duration-fast) var(--ease-standard),
                background var(--duration-fast) var(--ease-standard);
  }
  .tab:hover { color: var(--on-surface-variant); background: var(--surface-container-low); border-bottom: none; }
  .tab.active { color: var(--on-surface); background: var(--surface-container); }

  .tab-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--secondary);
    color: var(--on-secondary);
    border-radius: var(--radius-full);
    font-size: var(--text-label-sm);
    font-weight: 700;
    min-width: 1.25rem;
    height: 1.25rem;
    padding: 0 var(--space-1);
    margin-left: var(--space-2);
  }

  .section-note {
    color: var(--on-surface-muted);
    margin: 0;
    max-width: var(--reading-max);
  }

  /* ── Messages ────────────────────────────────────────────────────── */
  .action-msg {
    padding: var(--space-3) var(--space-4);
    background: rgba(109, 140, 124, 0.1);
    border-radius: var(--radius-sm);
    color: var(--pigment-copper);
    margin-bottom: var(--space-6);
  }

  .form-error {
    padding: var(--space-3) var(--space-4);
    background: rgba(185, 107, 88, 0.08);
    border-radius: var(--radius-sm);
    color: var(--pigment-terracotta);
    margin-bottom: var(--space-4);
  }

  /* ── Tab content ─────────────────────────────────────────────────── */
  .tab-content {
    display: flex;
    flex-direction: column;
    gap: var(--space-12);
  }

  /* ── Section ─────────────────────────────────────────────────────── */
  .section {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
  }

  .section-heading {
    letter-spacing: var(--tracking-wider);
    text-transform: uppercase;
    color: var(--on-surface-muted);
  }

  .empty-msg {
    color: var(--on-surface-muted);
    margin: 0;
  }

  /* ── Invite result ───────────────────────────────────────────────── */
  .invite-result {
    background: rgba(109, 140, 124, 0.08);
    border-radius: var(--radius-md);
    padding: var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
  }

  .invite-result-label {
    color: var(--pigment-copper);
    letter-spacing: var(--tracking-wide);
    font-weight: 600;
  }

  .invite-link-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
  }

  .invite-link {
    font-family: var(--font-mono);
    background: var(--background);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    color: var(--on-surface);
    word-break: break-all;
    flex: 1;
  }

  .copy-btn {
    padding: var(--space-2) var(--space-3);
    background: var(--secondary);
    color: var(--on-secondary);
    border: none;
    border-radius: var(--radius-sm);
    font-family: var(--font-sans);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    cursor: pointer;
    white-space: nowrap;
    flex-shrink: 0;
  }

  /* ── Invite form ─────────────────────────────────────────────────── */
  .invite-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    max-width: 48rem;
  }

  .form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-4);
  }

  .field {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
  }

  .field-label {
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--on-surface-variant);
  }

  .input,
  .select {
    width: 100%;
    padding: var(--space-3) var(--space-4);
    background: var(--surface-container-lowest);
    border: var(--ghost-border);
    border-radius: var(--radius-sm);
    font-family: var(--font-sans);
    font-size: var(--text-body-md);
    color: var(--on-surface);
    outline: none;
    transition: border-color var(--duration-fast) var(--ease-standard),
                box-shadow var(--duration-fast) var(--ease-standard);
    box-sizing: border-box;
  }
  .input:focus,
  .select:focus {
    border-color: var(--secondary);
    box-shadow: 0 0 0 3px rgba(120, 95, 83, 0.12);
  }

  .btn-primary {
    align-self: flex-start;
    padding: var(--space-2) var(--space-6);
    background: linear-gradient(180deg, var(--secondary) 0%, var(--secondary-dim) 100%);
    color: var(--on-secondary);
    border: none;
    border-radius: var(--radius-md);
    font-family: var(--font-sans);
    font-weight: 600;
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    cursor: pointer;
    transition: opacity var(--duration-fast) var(--ease-standard);
  }
  .btn-primary:hover { opacity: 0.9; }

  /* ── Data table ──────────────────────────────────────────────────── */
  .data-table {
    width: 100%;
    border-collapse: collapse;
  }

  .data-table th {
    text-align: left;
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--on-surface-muted);
    padding: var(--space-2) var(--space-3) var(--space-2) 0;
    border-bottom: var(--ghost-border);
    font-weight: 500;
  }

  .data-table td {
    padding: var(--space-3) var(--space-3) var(--space-3) 0;
    border-bottom: var(--ghost-border);
    color: var(--on-surface-variant);
    vertical-align: middle;
  }

  .data-table tr.self td {
    color: var(--on-surface-muted);
  }

  .muted { color: var(--on-surface-muted) !important; }

  /* ── Inline elements ─────────────────────────────────────────────── */
  .inline-form { display: inline; }

  .select-inline {
    background: none;
    border: var(--ghost-border);
    border-radius: var(--radius-sm);
    font-family: var(--font-sans);
    font-size: var(--text-label-md);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--on-surface-variant);
    padding: 0.2em var(--space-3);
    cursor: pointer;
    outline: none;
  }

  .danger-btn {
    background: none;
    border: none;
    cursor: pointer;
    font-family: var(--font-sans);
    font-size: var(--text-label-md);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--pigment-terracotta);
    padding: 0.2em var(--space-2);
    border-radius: var(--radius-sm);
    transition: background var(--duration-fast) var(--ease-standard);
  }
  .danger-btn:hover {
    background: rgba(185, 107, 88, 0.08);
  }

  .action-cell { display: flex; gap: var(--space-3); align-items: center; flex-wrap: wrap; }
  .action-link {
    background: none;
    border: none;
    color: var(--on-surface-muted);
    text-decoration: none;
    border-bottom: none;
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    cursor: pointer;
    font-family: var(--font-sans);
    font-size: var(--text-label-md);
    transition: color var(--duration-fast) var(--ease-standard);
    padding: 0;
  }
  .action-link:hover { color: var(--on-surface-variant); border-bottom: none; }
</style>
