/**
 * AdminPage — /admin route (D-02: dedicated gated route, not SettingsSheet extension).
 *
 * Non-admin users are redirected to /calendar at the App.tsx route level (UX, D-03).
 * The server enforces 403 on every /api/admin/* request (Plan 03, requireAdmin).
 *
 * UI-SPEC §Surface 1 (/admin route):
 *   - "Admin Settings" heading (18px/600)
 *   - Centered content column, maxWidth 640px on desktop
 *   - var(--space-12) top/bottom padding, var(--space-6) horizontal padding
 *
 * UI-SPEC §Surface 2 (MEMBERS section):
 *   - 32px avatar swatch (var(--color-member-N)) + member name + credential status badge
 *   - "Rotate" or "Add credential" action button per hasCredential
 *   - Opens CredentialSheet in admin-rotate or admin-add mode
 *
 * UI-SPEC §Surface 5 (SHARED CALENDAR section):
 *   - Radio group, one row per synced calendar
 *   - "Currently shared" label on active selection
 *   - Two-tap Save (disabled until selection differs from saved)
 *   - Empty state when no calendars synced
 *
 * Security: client isAdmin gate is UX only. Server 403 is the real boundary (D-03).
 */

import { useState, useRef, useEffect, useMemo } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { CheckCircle, AlertCircle, Loader2 } from 'lucide-react';
import {
  fetchAdminMembers,
  fetchAdminCalendars,
  setSharedCalendar,
  fetchAdminTimezone,
  setAdminTimezone,
  fetchCreateMember,
  fetchAdminResetPassword,
  type AdminMember,
  type AdminCalendar,
} from '../api/client.js';
import { CredentialSheet, type CredentialSheetMode } from '../components/CredentialSheet.js';
import { useIsPhone } from '../hooks/useIsPhone.js';
import { useFocusTrap } from '../hooks/useFocusTrap.js';

// ── Styles ─────────────────────────────────────────────────────────────────

const sectionLabelStyle: React.CSSProperties = {
  fontSize: 'var(--text-label-size, 13px)',
  fontWeight: 600,
  color: 'var(--color-text-muted)',
  textTransform: 'uppercase',
  letterSpacing: '0.06em',
  marginBottom: 'var(--space-2, 8px)',
};

// ── AdminPage ──────────────────────────────────────────────────────────────

export function AdminPage() {
  const queryClient = useQueryClient();

  // Phone detection for toast bottom offset (WR-05: resize-aware)
  const phone = useIsPhone();

  // Success toast state (D-08).
  // WR-03: store a unique id per toast so an identical repeated message still
  // re-announces (aria-live re-fires on remount) and the 3s timer resets.
  const [toast, setToast] = useState<{ id: number; msg: string } | null>(null);
  const showToast = (msg: string) => setToast({ id: Date.now(), msg });

  // Auto-dismiss toast after 3000ms — mirrors SyncStateToast lines 72-79.
  // `toast` is a fresh object per showToast() call, so a repeated identical
  // message produces a new reference here → the timer restarts (WR-03).
  useEffect(() => {
    if (!toast) return;
    const timer = setTimeout(() => setToast(null), 3000);
    return () => clearTimeout(timer);
  }, [toast]);

  // Two-tab navigation state (D-10)
  const [activeTab, setActiveTab] = useState<'members' | 'settings'>('members');

  // Credential sheet state
  const [sheetOpen, setSheetOpen] = useState(false);
  const [sheetMode, setSheetMode] = useState<CredentialSheetMode>('admin-add');
  const [sheetMember, setSheetMember] = useState<AdminMember | null>(null);
  const triggerRef = useRef<HTMLButtonElement>(null);

  // Reset-password sheet state (Surface 11B)
  const [resetSheetOpen, setResetSheetOpen] = useState(false);
  const [resetTargetMember, setResetTargetMember] = useState<AdminMember | null>(null);
  // resetTriggerRef: stores the exact button that opened the reset sheet so focus can return on close
  const resetTriggerRef = useRef<HTMLButtonElement | null>(null);

  // Create-member form state (Surface 11A)
  const [createDisplayName, setCreateDisplayName] = useState('');
  const [createUsername, setCreateUsername] = useState('');
  const [createPassword, setCreatePassword] = useState('');
  const [createConfirmPassword, setCreateConfirmPassword] = useState('');
  const [createError, setCreateError] = useState<string | null>(null);

  // Shared calendar picker state
  const [selectedCalendarId, setSelectedCalendarId] = useState<number | null>(null);

  // Timezone picker state
  const [timezoneInput, setTimezoneInput] = useState<string | null>(null);
  // Searchable combobox state: tzSearch is the live filter text while the list is
  // open (null = closed, input shows the selected zone). tzActiveIndex tracks the
  // keyboard-highlighted option.
  const [tzOpen, setTzOpen] = useState(false);
  const [tzSearch, setTzSearch] = useState<string | null>(null);
  const [tzActiveIndex, setTzActiveIndex] = useState(0);
  const tzBlurTimer = useRef<ReturnType<typeof setTimeout> | null>(null);

  // WR-07: clear any pending blur-close timer on unmount so it can't fire
  // setState after the component is gone.
  useEffect(() => {
    return () => {
      if (tzBlurTimer.current) clearTimeout(tzBlurTimer.current);
    };
  }, []);

  // Members query
  const membersQuery = useQuery({
    queryKey: ['admin', 'members'],
    queryFn: fetchAdminMembers,
    retry: false,
    staleTime: 60 * 1000,
  });

  // Calendars query
  const calendarsQuery = useQuery({
    queryKey: ['admin', 'calendars'],
    queryFn: fetchAdminCalendars,
    retry: false,
    staleTime: 60 * 1000,
  });

  // Derive current saved shared calendar id from the data
  const currentSharedId = calendarsQuery.data?.calendars.find((c) => c.isShared)?.id ?? null;

  // Effective selected = user pick OR fallback to current saved
  const effectiveSelected = selectedCalendarId ?? currentSharedId;

  // Timezone query
  const timezoneQuery = useQuery({
    queryKey: ['admin', 'timezone'],
    queryFn: fetchAdminTimezone,
    retry: false,
    staleTime: 60 * 1000,
  });

  // Timezone save mutation
  const timezoneMutation = useMutation({
    mutationFn: (tz: string) => setAdminTimezone(tz),
    onSuccess: () => {
      void queryClient.invalidateQueries({ queryKey: ['admin', 'timezone'] });
      setTimezoneInput(null); // reset local override after save
    },
  });

  // Detected browser timezone (D-02). IN-07: memoize — the resolved zone is
  // stable for the session, no need to recompute every render.
  const detectedTz = useMemo(() => Intl.DateTimeFormat().resolvedOptions().timeZone, []);

  // Effective timezone input value: local override → stored value → ''
  const storedTimezone = timezoneQuery.data?.timezone ?? '';
  const effectiveTimezoneInput = timezoneInput ?? storedTimezone;

  // WR-01: derive isExplicit so we only apply the no-op guard when the timezone
  // has ALREADY been explicitly saved. On first run (isExplicitlySet: false) the
  // admin must be able to confirm/save the displayed system-default — even if the
  // input value already matches the fallback string. Keep pending and empty-input
  // guards unconditional.
  const isExplicit = timezoneQuery.data?.isExplicitlySet ?? false;

  // Save is disabled when:
  //   - mutation is in-flight (pending), OR
  //   - input is empty, OR
  //   - the timezone IS already explicitly set AND the input is unchanged (no-op)
  const timezoneSaveDisabled =
    timezoneMutation.isPending ||
    effectiveTimezoneInput === '' ||
    (isExplicit && effectiveTimezoneInput === storedTimezone);

  // IANA zones list (Intl.supportedValuesOf may not be present in all runtimes)
  const ianaZones: string[] =
    typeof (Intl as { supportedValuesOf?: (key: string) => string[] }).supportedValuesOf ===
    'function'
      ? (Intl as { supportedValuesOf: (key: string) => string[] }).supportedValuesOf('timeZone')
      : [];

  // Searchable combobox: filter zones by the live search text (case-insensitive,
  // ignoring underscores so "new york" matches "America/New_York"). When the search
  // is empty the full list shows — so tapping the field reveals every zone with no
  // typing required.
  const tzNorm = (s: string) => s.toLowerCase().replace(/_/g, ' ');
  const tzQuery = tzOpen ? tzNorm(tzSearch ?? '') : '';
  const filteredZones = tzQuery
    ? ianaZones.filter((tz) => tzNorm(tz).includes(tzQuery))
    : ianaZones;

  // WR-06: clamp the active index into the CURRENT filtered list every render.
  // tzActiveIndex is reset to 0 on filter changes, but batched updates can leave
  // it referencing an index past the end of a freshly-shrunk list for one render;
  // aria-activedescendant and the visual highlight must both use this clamped value
  // so the announced row and highlighted row never disagree.
  const tzActiveIndexClamped = Math.min(tzActiveIndex, Math.max(0, filteredZones.length - 1));

  // Commit a zone selection from the list, then close.
  function selectTimezone(tz: string) {
    if (tzBlurTimer.current) clearTimeout(tzBlurTimer.current);
    setTimezoneInput(tz);
    setTzSearch(null);
    setTzOpen(false);
  }

  // Save shared calendar mutation
  const sharedCalMutation = useMutation({
    mutationFn: (calId: number) => setSharedCalendar(calId),
    onSuccess: () => {
      void queryClient.invalidateQueries({ queryKey: ['admin', 'calendars'] });
      // Also invalidate events so the shared lane updates
      void queryClient.invalidateQueries({ queryKey: ['events'] });
      setSelectedCalendarId(null); // reset picker
    },
  });

  // Roving tabindex keyboard handler for the two-tab strip (D-10).
  // WR-02: full WAI-ARIA tabs pattern — ArrowLeft/Right wrap around the ends,
  // Home/End jump to the first/last tab.
  function handleTabKeyDown(e: React.KeyboardEvent, current: 'members' | 'settings') {
    const order = ['members', 'settings'] as const;
    const idx = order.indexOf(current);
    let next: (typeof order)[number] | null = null;
    if (e.key === 'ArrowRight') {
      next = order[(idx + 1) % order.length];
    } else if (e.key === 'ArrowLeft') {
      next = order[(idx - 1 + order.length) % order.length];
    } else if (e.key === 'Home') {
      next = order[0];
    } else if (e.key === 'End') {
      next = order[order.length - 1];
    }
    if (!next) return;
    e.preventDefault();
    setActiveTab(next);
    (
      e.currentTarget.parentElement?.querySelector(`[id="admin-tab-${next}"]`) as HTMLElement | null
    )?.focus();
  }

  // Open credential sheet for a member
  function openSheet(member: AdminMember, buttonRef: React.RefObject<HTMLButtonElement | null>) {
    // Capture the button so focus can return on close
    (triggerRef as React.MutableRefObject<HTMLElement | null>).current = buttonRef.current;
    setSheetMember(member);
    setSheetMode(member.hasCredential ? 'admin-rotate' : 'admin-add');
    setSheetOpen(true);
  }

  // Create-member mutation (Surface 11A)
  const createMemberMutation = useMutation({
    mutationFn: async () => {
      // Client-side validation (server also validates; this is for UX)
      if (createPassword !== createConfirmPassword) {
        throw new Error('mismatch');
      }
      if (createPassword.length < 8) {
        throw new Error('short');
      }
      await fetchCreateMember({
        displayName: createDisplayName.trim(),
        username: createUsername.trim(),
        password: createPassword,
      });
    },
    onSuccess: () => {
      // Clear form + refresh member list
      setCreateDisplayName('');
      setCreateUsername('');
      setCreatePassword('');
      setCreateConfirmPassword('');
      setCreateError(null);
      void queryClient.invalidateQueries({ queryKey: ['admin', 'members'] });
      void queryClient.invalidateQueries({ queryKey: ['me'] });
      showToast('Member added.');
    },
    onError: (err) => {
      const msg = err instanceof Error ? err.message : 'server';
      if (msg === 'mismatch') {
        setCreateError('Passwords do not match.');
      } else if (msg === 'short') {
        setCreateError('Password is too short. Use at least 8 characters.');
      } else if (msg === 'conflict' || msg.includes('409')) {
        setCreateError('That username is already in use. Choose a different one.');
      } else {
        setCreateError('Something went wrong. Please try again.');
      }
    },
  });

  const createSubmitDisabled =
    createMemberMutation.isPending ||
    createDisplayName.trim().length === 0 ||
    createUsername.trim().length === 0 ||
    createPassword.length === 0 ||
    createConfirmPassword.length === 0;

  const saveDisabled =
    sharedCalMutation.isPending ||
    effectiveSelected === null ||
    effectiveSelected === currentSharedId;

  return (
    <div
      style={{
        height: '100%',
        overflowY: 'auto',
        fontFamily: 'var(--font-family-base)',
        // Bottom padding to clear the 56px fixed tab bar on phone
        paddingBottom: 'calc(56px + env(safe-area-inset-bottom, 0px))',
      }}
    >
      <div
        style={{
          maxWidth: '640px',
          margin: '0 auto',
          padding: 'var(--space-12, 48px) var(--space-6, 24px)',
        }}
      >
        {/* Page heading */}
        <h1
          style={{
            margin: '0 0 var(--space-6, 24px) 0',
            fontSize: 'var(--text-heading-size, 18px)',
            fontWeight: 600,
            lineHeight: 'var(--text-heading-line-height, 1.25)',
            color: 'var(--color-text-primary)',
          }}
        >
          Admin Settings
        </h1>

        {/* ── Two-tab strip (D-10) ──────────────────────────────────────────── */}
        <div
          role="tablist"
          style={{
            display: 'flex',
            borderBottom: '1px solid var(--color-border-subtle, var(--color-border))',
            marginBottom: 'var(--space-6, 24px)',
          }}
        >
          {(['members', 'settings'] as const).map((id) => (
            <button
              key={id}
              role="tab"
              id={`admin-tab-${id}`}
              aria-selected={activeTab === id}
              aria-controls={`admin-panel-${id}`}
              tabIndex={activeTab === id ? 0 : -1}
              onClick={() => setActiveTab(id)}
              onKeyDown={(e) => handleTabKeyDown(e, id)}
              style={{
                background: 'none',
                border: 'none',
                cursor: 'pointer',
                padding: 'var(--space-3, 12px) var(--space-4, 16px)',
                minHeight: '44px',
                fontSize: 'var(--text-label-size, 13px)',
                fontWeight: activeTab === id ? 600 : 400,
                color:
                  activeTab === id ? 'var(--color-text-primary)' : 'var(--color-text-secondary)',
                borderBottom:
                  activeTab === id ? '2px solid var(--color-member-0)' : '2px solid transparent',
                transition: 'color 0.1s ease, border-color 0.1s ease',
                fontFamily: 'var(--font-family-base)',
              }}
            >
              {id === 'members' ? 'Members & Accounts' : 'Settings'}
            </button>
          ))}
        </div>

        {/* ── Tab panel: Members & Accounts ────────────────────────────────── */}
        <div
          role="tabpanel"
          id="admin-panel-members"
          aria-labelledby="admin-tab-members"
          tabIndex={0}
          hidden={activeTab !== 'members'}
        >
          {/* ── MEMBERS section ───────────────────────────────────────────── */}
          <section aria-label="Members" style={{ marginBottom: 'var(--space-8, 32px)' }}>
            <div style={sectionLabelStyle}>Members</div>

            {membersQuery.isLoading && (
              <div
                style={{
                  padding: 'var(--space-4, 16px) 0',
                  color: 'var(--color-text-muted)',
                  fontSize: 'var(--text-body-size, 15px)',
                }}
              >
                Loading members…
              </div>
            )}

            {membersQuery.isError && (
              <div
                style={{
                  color: 'var(--color-destructive)',
                  fontSize: 'var(--text-body-size, 15px)',
                  padding: 'var(--space-4, 16px) 0',
                }}
              >
                Could not load members.
              </div>
            )}

            {membersQuery.data && (
              <div>
                {membersQuery.data.members.map((member, idx) => (
                  <MemberRow
                    key={member.id}
                    member={member}
                    colorIndex={idx}
                    onAction={(buttonRef) => openSheet(member, buttonRef)}
                    onResetPassword={(buttonRef) => {
                      // Capture trigger button so focus can return on close
                      resetTriggerRef.current = buttonRef.current;
                      setResetTargetMember(member);
                      setResetSheetOpen(true);
                    }}
                  />
                ))}
              </div>
            )}
          </section>

          {/* ── LOCAL ACCOUNTS section ──────────────────────────────────────── */}
          <section aria-label="Local Accounts" style={{ marginBottom: 'var(--space-8, 32px)' }}>
            <div style={sectionLabelStyle}>Local Accounts</div>

            {/* Surface 11A — Add member inline form */}
            <div
              style={{
                border: '1px solid var(--color-border-subtle, var(--color-border))',
                borderRadius: '8px',
                padding: 'var(--space-4, 16px)',
                marginBottom: 'var(--space-6, 24px)',
              }}
            >
              <div
                style={{
                  fontSize: 'var(--text-body-size, 15px)',
                  fontWeight: 600,
                  color: 'var(--color-text-primary)',
                  marginBottom: 'var(--space-4, 16px)',
                }}
              >
                Add member
              </div>

              {/* Display name */}
              <div style={{ marginBottom: 'var(--space-3, 12px)' }}>
                <label
                  htmlFor="admin-create-display-name"
                  style={{
                    display: 'block',
                    fontSize: 'var(--text-label-size, 13px)',
                    fontWeight: 600,
                    color: 'var(--color-text-primary)',
                    marginBottom: 'var(--space-1, 4px)',
                  }}
                >
                  Display name
                </label>
                <input
                  id="admin-create-display-name"
                  type="text"
                  value={createDisplayName}
                  onChange={(e) => setCreateDisplayName(e.target.value)}
                  style={{
                    width: '100%',
                    boxSizing: 'border-box',
                    padding: 'var(--space-3, 12px) var(--space-4, 16px)',
                    border: '1px solid var(--color-border)',
                    borderRadius: 'var(--space-1, 4px)',
                    fontSize: 'var(--text-body-size, 15px)',
                    color: 'var(--color-text-primary)',
                    background: 'var(--color-surface)',
                    fontFamily: 'var(--font-family-base)',
                    outline: 'none',
                    minHeight: '44px',
                  }}
                />
              </div>

              {/* Username */}
              <div style={{ marginBottom: 'var(--space-3, 12px)' }}>
                <label
                  htmlFor="admin-create-username"
                  style={{
                    display: 'block',
                    fontSize: 'var(--text-label-size, 13px)',
                    fontWeight: 600,
                    color: 'var(--color-text-primary)',
                    marginBottom: 'var(--space-1, 4px)',
                  }}
                >
                  Username
                </label>
                <input
                  id="admin-create-username"
                  type="text"
                  autoComplete="off"
                  spellCheck={false}
                  autoCapitalize="none"
                  value={createUsername}
                  onChange={(e) => setCreateUsername(e.target.value)}
                  style={{
                    width: '100%',
                    boxSizing: 'border-box',
                    padding: 'var(--space-3, 12px) var(--space-4, 16px)',
                    border: '1px solid var(--color-border)',
                    borderRadius: 'var(--space-1, 4px)',
                    fontSize: 'var(--text-body-size, 15px)',
                    color: 'var(--color-text-primary)',
                    background: 'var(--color-surface)',
                    fontFamily: 'var(--font-family-base)',
                    outline: 'none',
                    minHeight: '44px',
                  }}
                />
              </div>

              {/* Initial password */}
              <div style={{ marginBottom: 'var(--space-3, 12px)' }}>
                <label
                  htmlFor="admin-create-password"
                  style={{
                    display: 'block',
                    fontSize: 'var(--text-label-size, 13px)',
                    fontWeight: 600,
                    color: 'var(--color-text-primary)',
                    marginBottom: 'var(--space-1, 4px)',
                  }}
                >
                  Initial password
                </label>
                <input
                  id="admin-create-password"
                  type="password"
                  autoComplete="new-password"
                  value={createPassword}
                  onChange={(e) => setCreatePassword(e.target.value)}
                  style={{
                    width: '100%',
                    boxSizing: 'border-box',
                    padding: 'var(--space-3, 12px) var(--space-4, 16px)',
                    border: '1px solid var(--color-border)',
                    borderRadius: 'var(--space-1, 4px)',
                    fontSize: 'var(--text-body-size, 15px)',
                    color: 'var(--color-text-primary)',
                    background: 'var(--color-surface)',
                    fontFamily: 'var(--font-family-base)',
                    outline: 'none',
                    minHeight: '44px',
                  }}
                />
              </div>

              {/* Confirm password */}
              <div style={{ marginBottom: 'var(--space-4, 16px)' }}>
                <label
                  htmlFor="admin-create-confirm-password"
                  style={{
                    display: 'block',
                    fontSize: 'var(--text-label-size, 13px)',
                    fontWeight: 600,
                    color: 'var(--color-text-primary)',
                    marginBottom: 'var(--space-1, 4px)',
                  }}
                >
                  Confirm password
                </label>
                <input
                  id="admin-create-confirm-password"
                  type="password"
                  autoComplete="new-password"
                  value={createConfirmPassword}
                  onChange={(e) => setCreateConfirmPassword(e.target.value)}
                  style={{
                    width: '100%',
                    boxSizing: 'border-box',
                    padding: 'var(--space-3, 12px) var(--space-4, 16px)',
                    border: '1px solid var(--color-border)',
                    borderRadius: 'var(--space-1, 4px)',
                    fontSize: 'var(--text-body-size, 15px)',
                    color: 'var(--color-text-primary)',
                    background: 'var(--color-surface)',
                    fontFamily: 'var(--font-family-base)',
                    outline: 'none',
                    minHeight: '44px',
                  }}
                />
              </div>

              {/* Inline error */}
              {createError && (
                <div
                  style={{
                    fontSize: 'var(--text-label-size, 13px)',
                    fontWeight: 400,
                    color: 'var(--color-destructive)',
                    marginBottom: 'var(--space-3, 12px)',
                  }}
                >
                  {createError}
                </div>
              )}

              {/* Action row */}
              <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
                <button
                  type="button"
                  disabled={createSubmitDisabled}
                  onClick={() => {
                    setCreateError(null);
                    createMemberMutation.mutate();
                  }}
                  style={{
                    background: createSubmitDisabled
                      ? 'var(--color-border, #e2e4e9)'
                      : 'var(--color-member-0, #4a90d9)',
                    color: '#ffffff',
                    border: 'none',
                    cursor: createSubmitDisabled ? 'default' : 'pointer',
                    fontSize: 'var(--text-label-size, 13px)',
                    fontWeight: 600,
                    minHeight: '44px',
                    minWidth: '44px',
                    padding: '0 var(--space-6, 24px)',
                    borderRadius: 'var(--space-1, 4px)',
                    fontFamily: 'var(--font-family-base)',
                    transition: 'background 0.15s ease',
                    display: 'flex',
                    alignItems: 'center',
                    gap: 'var(--space-2, 8px)',
                  }}
                >
                  {createMemberMutation.isPending && (
                    <Loader2
                      size={14}
                      aria-hidden="true"
                      style={{ animation: 'spin 1s linear infinite', flexShrink: 0 }}
                    />
                  )}
                  Add member
                </button>
              </div>
            </div>
          </section>
        </div>
        {/* end admin-panel-members */}

        {/* ── Tab panel: Settings ───────────────────────────────────────────── */}
        <div
          role="tabpanel"
          id="admin-panel-settings"
          aria-labelledby="admin-tab-settings"
          tabIndex={0}
          hidden={activeTab !== 'settings'}
        >
          {/* ── SHARED CALENDAR section ──────────────────────────────────────── */}
          <section aria-label="Shared Calendar" style={{ marginBottom: 'var(--space-8, 32px)' }}>
            <div style={sectionLabelStyle}>Shared Calendar</div>

            <p
              style={{
                margin: '0 0 var(--space-4, 16px) 0',
                fontSize: 'var(--text-body-size, 15px)',
                fontWeight: 400,
                color: 'var(--color-text-secondary)',
                lineHeight: 1.5,
              }}
            >
              The shared family calendar is visible to all members in the same color lane.
            </p>

            {calendarsQuery.isLoading && (
              <div
                style={{
                  color: 'var(--color-text-muted)',
                  fontSize: 'var(--text-body-size, 15px)',
                  padding: 'var(--space-2, 8px) 0',
                }}
              >
                Loading calendars…
              </div>
            )}

            {calendarsQuery.isError && (
              <div
                style={{
                  color: 'var(--color-destructive)',
                  fontSize: 'var(--text-body-size, 15px)',
                  padding: 'var(--space-2, 8px) 0',
                }}
              >
                Could not load calendars.
              </div>
            )}

            {calendarsQuery.data && calendarsQuery.data.calendars.length === 0 && (
              <EmptyCalendarsState />
            )}

            {calendarsQuery.data && calendarsQuery.data.calendars.length > 0 && (
              <>
                <div
                  role="radiogroup"
                  aria-label="Select shared calendar"
                  style={{ marginBottom: 'var(--space-4, 16px)' }}
                >
                  {calendarsQuery.data.calendars.map((cal) => (
                    <CalendarRadioRow
                      key={cal.id}
                      calendar={cal}
                      isSelected={effectiveSelected === cal.id}
                      onSelect={() => setSelectedCalendarId(cal.id)}
                    />
                  ))}
                </div>

                {/* Two-tap Save button */}
                <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
                  <button
                    type="button"
                    disabled={saveDisabled}
                    onClick={() => {
                      if (effectiveSelected !== null) {
                        sharedCalMutation.mutate(effectiveSelected);
                      }
                    }}
                    style={{
                      background: saveDisabled
                        ? 'var(--color-border, #E2E4E9)'
                        : 'var(--color-member-0, #4A90D9)',
                      color: '#ffffff',
                      border: 'none',
                      cursor: saveDisabled ? 'default' : 'pointer',
                      fontSize: 'var(--text-label-size, 13px)',
                      fontWeight: 600,
                      minHeight: '44px',
                      minWidth: '44px',
                      padding: '0 var(--space-6, 24px)',
                      borderRadius: 'var(--space-1, 4px)',
                      fontFamily: 'var(--font-family-base)',
                      transition: 'background 0.15s ease',
                    }}
                  >
                    {sharedCalMutation.isPending ? 'Saving…' : 'Save'}
                  </button>
                </div>

                {sharedCalMutation.isError && (
                  <div
                    style={{
                      color: 'var(--color-destructive)',
                      fontSize: 'var(--text-label-size, 13px)',
                      marginTop: 'var(--space-2, 8px)',
                      textAlign: 'right',
                    }}
                  >
                    Something went wrong. Please try again.
                  </div>
                )}
              </>
            )}
          </section>

          {/* ── TIMEZONE section ─────────────────────────────────────────────── */}
          <section aria-label="Timezone">
            <div style={sectionLabelStyle}>Timezone</div>

            {timezoneQuery.isLoading && (
              <div
                style={{
                  color: 'var(--color-text-muted)',
                  fontSize: 'var(--text-body-size, 15px)',
                  padding: 'var(--space-2, 8px) 0',
                }}
              >
                Loading timezone…
              </div>
            )}

            {timezoneQuery.isError && (
              <div
                style={{
                  color: 'var(--color-destructive)',
                  fontSize: 'var(--text-body-size, 15px)',
                  padding: 'var(--space-2, 8px) 0',
                }}
              >
                Could not load timezone setting.
              </div>
            )}

            {timezoneQuery.data && (
              <>
                {/* System-default notice (D-06) */}
                {!timezoneQuery.data.isExplicitlySet && (
                  <div
                    style={{
                      fontSize: 'var(--text-label-size, 13px)',
                      color: 'var(--color-text-muted)',
                      marginBottom: 'var(--space-3, 12px)',
                      fontStyle: 'italic',
                    }}
                  >
                    Using system default — save a timezone to make it explicit.
                  </div>
                )}

                {/* IANA picker — searchable combobox. Focusing shows the full list
                    (no typing/erasing needed); typing filters it case-insensitively
                    (underscores ignored, so "new york" matches America/New_York). */}
                <div style={{ position: 'relative', marginBottom: 'var(--space-3, 12px)' }}>
                  <input
                    type="text"
                    role="combobox"
                    aria-label="Household timezone"
                    aria-expanded={tzOpen}
                    aria-controls="tz-listbox"
                    aria-autocomplete="list"
                    aria-activedescendant={
                      tzOpen && filteredZones.length ? `tz-opt-${tzActiveIndexClamped}` : undefined
                    }
                    autoComplete="off"
                    value={tzOpen ? (tzSearch ?? '') : effectiveTimezoneInput}
                    placeholder={
                      tzOpen ? effectiveTimezoneInput || 'Search timezones…' : 'Search timezones…'
                    }
                    onFocus={() => {
                      if (tzBlurTimer.current) clearTimeout(tzBlurTimer.current);
                      setTzOpen(true);
                      setTzSearch('');
                      setTzActiveIndex(0);
                    }}
                    onChange={(e) => {
                      setTzSearch(e.target.value);
                      setTzOpen(true);
                      setTzActiveIndex(0);
                    }}
                    onKeyDown={(e) => {
                      if (e.key === 'ArrowDown') {
                        e.preventDefault();
                        if (!tzOpen) {
                          setTzOpen(true);
                          setTzSearch('');
                        }
                        // WR-06: clamp first, then advance — never start from a
                        // stale index past the end of the current filtered list.
                        setTzActiveIndex((i) =>
                          Math.min(
                            Math.min(i, Math.max(0, filteredZones.length - 1)) + 1,
                            filteredZones.length - 1,
                          ),
                        );
                      } else if (e.key === 'ArrowUp') {
                        e.preventDefault();
                        setTzActiveIndex((i) =>
                          Math.max(Math.min(i, Math.max(0, filteredZones.length - 1)) - 1, 0),
                        );
                      } else if (e.key === 'Enter') {
                        // WR-06: commit the CLAMPED active option so Enter selects
                        // the same row the user sees highlighted / AT announces.
                        if (tzOpen && filteredZones[tzActiveIndexClamped]) {
                          e.preventDefault();
                          selectTimezone(filteredZones[tzActiveIndexClamped]);
                        }
                      } else if (e.key === 'Tab') {
                        // WR-07: commit the highlighted option on Tab WITHOUT
                        // preventDefault, so focus still advances to Save and the
                        // admin can't tab away leaving raw search text uncommitted.
                        if (tzOpen && filteredZones[tzActiveIndexClamped]) {
                          selectTimezone(filteredZones[tzActiveIndexClamped]);
                        }
                      } else if (e.key === 'Escape') {
                        setTzOpen(false);
                        setTzSearch(null);
                      }
                    }}
                    onBlur={() => {
                      // WR-07: options call preventDefault() on onMouseDown, so a
                      // click never blurs the input first — but a real focus change
                      // (Tab handled above, or clicking elsewhere) still needs to
                      // close the listbox. Defer one tick so any in-flight option
                      // mousedown settles before we close.
                      tzBlurTimer.current = setTimeout(() => {
                        setTzOpen(false);
                        setTzSearch(null);
                      }, 0);
                    }}
                    style={{
                      width: '100%',
                      boxSizing: 'border-box',
                      padding: 'var(--space-2, 8px) var(--space-3, 12px)',
                      fontSize: 'var(--text-body-size, 15px)',
                      fontFamily: 'var(--font-family-base)',
                      border: '1px solid var(--color-border)',
                      borderRadius: 'var(--space-1, 4px)',
                      color: 'var(--color-text-primary)',
                      background: 'var(--color-surface, #ffffff)',
                      minHeight: '44px',
                    }}
                  />
                  {tzOpen && (
                    <ul
                      id="tz-listbox"
                      role="listbox"
                      aria-label="Timezones"
                      style={{
                        position: 'absolute',
                        zIndex: 10,
                        top: 'calc(100% + 4px)',
                        left: 0,
                        right: 0,
                        margin: 0,
                        padding: 'var(--space-1, 4px)',
                        listStyle: 'none',
                        maxHeight: '260px',
                        overflowY: 'auto',
                        background: 'var(--color-surface, #ffffff)',
                        border: '1px solid var(--color-border)',
                        borderRadius: 'var(--space-1, 4px)',
                        boxShadow: '0 6px 20px rgba(0,0,0,0.12)',
                      }}
                    >
                      {filteredZones.length === 0 && (
                        <li
                          style={{
                            padding: 'var(--space-2, 8px) var(--space-3, 12px)',
                            color: 'var(--color-text-muted)',
                            fontSize: 'var(--text-body-size, 15px)',
                          }}
                        >
                          No matching timezones
                        </li>
                      )}
                      {filteredZones.map((tz, i) => {
                        // WR-06: highlight the clamped active row so the visual
                        // highlight matches aria-activedescendant exactly.
                        const active = i === tzActiveIndexClamped;
                        return (
                          <li
                            key={tz}
                            id={`tz-opt-${i}`}
                            role="option"
                            aria-selected={tz === effectiveTimezoneInput}
                            ref={
                              active
                                ? (el) => {
                                    el?.scrollIntoView({ block: 'nearest' });
                                  }
                                : undefined
                            }
                            onMouseDown={(e) => e.preventDefault()}
                            onMouseEnter={() => setTzActiveIndex(i)}
                            onClick={() => selectTimezone(tz)}
                            style={{
                              padding: 'var(--space-2, 8px) var(--space-3, 12px)',
                              fontSize: 'var(--text-body-size, 15px)',
                              fontFamily: 'var(--font-family-base)',
                              color: 'var(--color-text-primary)',
                              borderRadius: 'var(--space-1, 4px)',
                              cursor: 'pointer',
                              background: active ? 'var(--color-member-0, #4A90D9)' : 'transparent',
                              ...(active ? { color: '#ffffff' } : null),
                              minHeight: '44px',
                              display: 'flex',
                              alignItems: 'center',
                            }}
                          >
                            {tz}
                          </li>
                        );
                      })}
                    </ul>
                  )}
                </div>

                {/* Use detected zone affordance (D-02) */}
                {detectedTz && detectedTz !== effectiveTimezoneInput && (
                  <div style={{ marginBottom: 'var(--space-3, 12px)' }}>
                    <button
                      type="button"
                      onClick={() => setTimezoneInput(detectedTz)}
                      style={{
                        background: 'none',
                        border: 'none',
                        padding: 0,
                        cursor: 'pointer',
                        fontSize: 'var(--text-label-size, 13px)',
                        color: 'var(--color-member-0, #4A90D9)',
                        fontFamily: 'var(--font-family-base)',
                        textDecoration: 'underline',
                      }}
                    >
                      Use detected: {detectedTz}
                    </button>
                  </div>
                )}

                {/* Save button */}
                <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
                  <button
                    type="button"
                    disabled={timezoneSaveDisabled}
                    onClick={() => {
                      if (effectiveTimezoneInput) {
                        timezoneMutation.mutate(effectiveTimezoneInput);
                      }
                    }}
                    style={{
                      background: timezoneSaveDisabled
                        ? 'var(--color-border, #E2E4E9)'
                        : 'var(--color-member-0, #4A90D9)',
                      color: '#ffffff',
                      border: 'none',
                      cursor: timezoneSaveDisabled ? 'default' : 'pointer',
                      fontSize: 'var(--text-label-size, 13px)',
                      fontWeight: 600,
                      minHeight: '44px',
                      minWidth: '44px',
                      padding: '0 var(--space-6, 24px)',
                      borderRadius: 'var(--space-1, 4px)',
                      fontFamily: 'var(--font-family-base)',
                      transition: 'background 0.15s ease',
                    }}
                  >
                    {timezoneMutation.isPending ? 'Saving…' : 'Save'}
                  </button>
                </div>

                {timezoneMutation.isError && (
                  <div
                    style={{
                      color: 'var(--color-destructive)',
                      fontSize: 'var(--text-label-size, 13px)',
                      marginTop: 'var(--space-2, 8px)',
                      textAlign: 'right',
                    }}
                  >
                    Could not save timezone. Please check the value and try again.
                  </div>
                )}
              </>
            )}
          </section>
        </div>
        {/* end admin-panel-settings */}
      </div>
      {/* end centered content column */}

      {/* ── Success toast (D-08) ──────────────────────────────────────────────── */}
      {toast && (
        // WR-03: key on toast.id so an identical repeated message remounts and
        // aria-live re-announces it (and the dismiss timer restarts).
        <div
          key={toast.id}
          role="status"
          aria-live="polite"
          aria-atomic="true"
          style={{
            position: 'fixed',
            bottom: phone
              ? 'calc(var(--bottom-chrome-h) + var(--space-4, 16px))'
              : 'var(--space-6, 24px)',
            left: '50%',
            transform: 'translateX(-50%)',
            zIndex: 400,
            background: 'var(--color-surface-raised, #ffffff)',
            border: '1px solid var(--color-border)',
            borderRadius: 'var(--space-2, 8px)',
            boxShadow: '0 2px 8px rgba(0,0,0,0.12)',
            padding: 'var(--space-3, 12px) var(--space-4, 16px)',
            display: 'flex',
            alignItems: 'center',
            gap: 'var(--space-2, 8px)',
            fontSize: 'var(--text-label-size, 13px)',
            fontWeight: 'var(--text-label-weight, 400)' as React.CSSProperties['fontWeight'],
            lineHeight: 'var(--text-label-line-height, 1.4)',
            fontFamily: 'var(--font-family-base)',
            color: 'var(--color-text-primary)',
            // WR-04: let the toast wrap instead of overflowing 90vw (nowrap + maxWidth
            // overflows and trips the layout suite's no-horizontal-overflow rule).
            maxWidth: '90vw',
          }}
        >
          <CheckCircle
            size={16}
            aria-hidden="true"
            style={{ color: 'var(--color-member-0)', flexShrink: 0 }}
          />
          <span>{toast.msg}</span>
        </div>
      )}

      {/* Credential sheet — admin-rotate or admin-add */}
      {sheetMember && (
        <CredentialSheet
          isOpen={sheetOpen}
          onClose={() => setSheetOpen(false)}
          mode={sheetMode}
          memberName={sheetMember.displayName}
          memberId={sheetMember.id}
          triggerRef={triggerRef}
        />
      )}

      {/* Surface 11B — Reset password sheet */}
      {resetTargetMember && (
        <ResetPasswordSheet
          isOpen={resetSheetOpen}
          onClose={() => {
            setResetSheetOpen(false);
            // Return focus to trigger
            if (resetTriggerRef.current) {
              resetTriggerRef.current.focus();
            }
          }}
          onSuccess={() => showToast('Password reset.')}
          member={resetTargetMember}
        />
      )}
    </div>
  );
}

// ── MemberRow ──────────────────────────────────────────────────────────────

interface MemberRowProps {
  member: AdminMember;
  colorIndex: number;
  onAction: (buttonRef: React.RefObject<HTMLButtonElement | null>) => void;
  onResetPassword?: (buttonRef: React.RefObject<HTMLButtonElement | null>) => void;
}

function MemberRow({ member, colorIndex, onAction, onResetPassword }: MemberRowProps) {
  const buttonRef = useRef<HTMLButtonElement>(null);
  const resetBtnRef = useRef<HTMLButtonElement>(null);

  return (
    <div
      style={{
        display: 'flex',
        alignItems: 'center',
        gap: 'var(--space-3, 12px)',
        minHeight: '44px',
        padding: 'var(--space-2, 8px) 0',
        borderBottom: '1px solid var(--color-border-subtle, var(--color-border))',
      }}
    >
      {/* Avatar swatch */}
      <div
        aria-hidden="true"
        style={{
          width: '32px',
          height: '32px',
          borderRadius: '50%',
          background: `var(--color-member-${colorIndex}, var(--color-member-0))`,
          flexShrink: 0,
        }}
      />

      {/* Name + status */}
      <div style={{ flex: 1, minWidth: 0 }}>
        <div
          style={{
            fontSize: 'var(--text-body-size, 15px)',
            fontWeight: 400,
            color: 'var(--color-text-primary)',
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            whiteSpace: 'nowrap',
          }}
        >
          {member.displayName ?? 'Member'}
        </div>

        {/* Credential status badge */}
        <div
          style={{
            display: 'flex',
            alignItems: 'center',
            gap: 'var(--space-1, 4px)',
            marginTop: '2px',
          }}
        >
          {member.hasCredential ? (
            <>
              <CheckCircle
                size={16}
                aria-hidden="true"
                style={{ color: 'var(--color-text-secondary)', flexShrink: 0 }}
              />
              <span
                style={{
                  fontSize: 'var(--text-label-size, 13px)',
                  color: 'var(--color-text-secondary)',
                }}
              >
                Credential set
              </span>
            </>
          ) : (
            <>
              <AlertCircle
                size={16}
                aria-hidden="true"
                style={{ color: 'var(--color-text-muted)', flexShrink: 0 }}
              />
              <span
                style={{
                  fontSize: 'var(--text-label-size, 13px)',
                  color: 'var(--color-text-muted)',
                }}
              >
                No credential
              </span>
            </>
          )}
        </div>
      </div>

      {/* Action button row */}
      <div style={{ display: 'flex', gap: 'var(--space-2, 8px)', flexShrink: 0 }}>
        {/* Credential rotate/add button */}
        <button
          ref={buttonRef}
          type="button"
          onClick={() => onAction(buttonRef)}
          style={{
            background: 'none',
            border: '1px solid var(--color-border)',
            borderRadius: 'var(--space-1, 4px)',
            cursor: 'pointer',
            fontSize: 'var(--text-label-size, 13px)',
            fontWeight: 600,
            color: 'var(--color-text-primary)',
            minHeight: '44px',
            minWidth: '44px',
            padding: '0 var(--space-3, 12px)',
            fontFamily: 'var(--font-family-base)',
          }}
        >
          {member.hasCredential ? 'Rotate' : 'Add credential'}
        </button>

        {/* Surface 11B — Reset password button (only for members with a local credential) */}
        {member.hasLocalCredential && onResetPassword && (
          <button
            ref={resetBtnRef}
            type="button"
            onClick={() => onResetPassword(resetBtnRef)}
            style={{
              background: 'none',
              border: '1px solid var(--color-border)',
              borderRadius: 'var(--space-1, 4px)',
              cursor: 'pointer',
              fontSize: 'var(--text-label-size, 13px)',
              fontWeight: 600,
              color: 'var(--color-text-primary)',
              minHeight: '44px',
              minWidth: '44px',
              padding: '0 var(--space-3, 12px)',
              fontFamily: 'var(--font-family-base)',
            }}
          >
            Reset password
          </button>
        )}
      </div>
    </div>
  );
}

// ── CalendarRadioRow ────────────────────────────────────────────────────────

interface CalendarRadioRowProps {
  calendar: AdminCalendar;
  isSelected: boolean;
  onSelect: () => void;
}

function CalendarRadioRow({ calendar, isSelected, onSelect }: CalendarRadioRowProps) {
  return (
    <div
      role="radio"
      aria-checked={isSelected}
      tabIndex={0}
      onClick={onSelect}
      onKeyDown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          onSelect();
        }
      }}
      style={{
        display: 'flex',
        alignItems: 'center',
        gap: 'var(--space-3, 12px)',
        minHeight: '44px',
        padding: 'var(--space-2, 8px) 0',
        cursor: 'pointer',
      }}
    >
      {/* Radio indicator: 20px circle */}
      <div
        aria-hidden="true"
        style={{
          width: '20px',
          height: '20px',
          borderRadius: '50%',
          flexShrink: 0,
          border: isSelected ? 'none' : '2px solid var(--color-border)',
          background: isSelected ? 'var(--color-member-0, #4A90D9)' : 'transparent',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        {isSelected && (
          <div
            style={{
              width: '8px',
              height: '8px',
              borderRadius: '50%',
              background: '#ffffff',
            }}
          />
        )}
      </div>

      {/* Calendar name */}
      <span
        style={{
          fontSize: 'var(--text-body-size, 15px)',
          fontWeight: 400,
          color: 'var(--color-text-primary)',
          flex: 1,
        }}
      >
        {calendar.displayName}
      </span>

      {/* Currently shared label */}
      {calendar.isShared && (
        <span
          style={{
            fontSize: 'var(--text-label-size, 13px)',
            fontWeight: 400,
            color: 'var(--color-member-0, #4A90D9)',
          }}
        >
          Currently shared
        </span>
      )}
    </div>
  );
}

// ── ResetPasswordSheet ──────────────────────────────────────────────────────

/**
 * Surface 11B — Admin password reset sheet.
 * Opens as a bottom sheet (mobile) / centered modal (desktop).
 * Pattern: CredentialSheet (role=dialog, aria-modal, Escape closes, focus returns to trigger).
 * No current-password field — admin reset does not require knowing the old password.
 */

interface ResetPasswordSheetProps {
  isOpen: boolean;
  onClose: () => void;
  onSuccess?: () => void;
  member: AdminMember;
}

function ResetPasswordSheet({ isOpen, onClose, onSuccess, member }: ResetPasswordSheetProps) {
  // WR-05: resize-aware phone detection.
  const sheetPhone = useIsPhone();
  const [newPassword, setNewPassword] = useState('');
  const [confirmPassword, setConfirmPassword] = useState('');
  const [error, setError] = useState<string | null>(null);
  const headingRef = useRef<HTMLHeadingElement>(null);
  // WR-01: trap Tab/Shift+Tab inside the dialog (matches aria-modal="true").
  const dialogRef = useRef<HTMLDivElement>(null);
  const handleDialogKeyDown = useFocusTrap(dialogRef);

  // Escape closes the sheet
  useEffect(() => {
    if (!isOpen) return;
    const onKeyDown = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onClose();
    };
    document.addEventListener('keydown', onKeyDown);
    return () => document.removeEventListener('keydown', onKeyDown);
  }, [isOpen, onClose]);

  // Focus heading on open
  useEffect(() => {
    if (isOpen && headingRef.current) {
      headingRef.current.focus();
    }
  }, [isOpen]);

  function handleClose() {
    setNewPassword('');
    setConfirmPassword('');
    setError(null);
    onClose();
  }

  const resetMutation = useMutation({
    mutationFn: async () => {
      if (newPassword !== confirmPassword) throw new Error('mismatch');
      await fetchAdminResetPassword(member.id, newPassword);
    },
    onSuccess: () => {
      handleClose();
      onSuccess?.();
    },
    onError: (err) => {
      const msg = err instanceof Error ? err.message : 'server';
      if (msg === 'mismatch') {
        setError('Passwords do not match.');
      } else {
        setError('Something went wrong. Please try again.');
      }
    },
  });

  const isPending = resetMutation.isPending;
  const submitDisabled = isPending || newPassword.length === 0 || confirmPassword.length === 0;

  if (!isOpen) return null;

  return (
    <>
      {/* Backdrop */}
      <div
        onClick={handleClose}
        aria-hidden="true"
        style={{
          position: 'fixed',
          inset: 0,
          background: 'var(--color-overlay, rgba(0,0,0,0.32))',
          zIndex: 300,
        }}
      />

      {/* Sheet — phone: bottom-sheet / desktop: centered modal (D-09) */}
      <div
        ref={dialogRef}
        role="dialog"
        aria-modal="true"
        aria-label="Reset password"
        onKeyDown={handleDialogKeyDown}
        style={
          sheetPhone
            ? {
                position: 'fixed',
                bottom: 0,
                left: 0,
                right: 0,
                background: 'var(--color-surface, #ffffff)',
                borderRadius: '12px 12px 0 0',
                boxShadow: '0 -4px 24px rgba(0,0,0,0.15)',
                padding: 'var(--space-6, 24px)',
                zIndex: 301,
                fontFamily: 'var(--font-family-base)',
              }
            : {
                position: 'fixed',
                top: '50%',
                left: '50%',
                transform: 'translate(-50%, -50%)',
                maxWidth: '480px',
                width: 'calc(100% - var(--space-8, 32px))',
                maxHeight: 'calc(100dvh - var(--space-8, 32px))',
                overflowY: 'auto',
                background: 'var(--color-surface, #ffffff)',
                borderRadius: '12px',
                boxShadow: '0 8px 32px rgba(0,0,0,0.18)',
                padding: 'var(--space-6, 24px)',
                zIndex: 301,
                fontFamily: 'var(--font-family-base)',
              }
        }
      >
        <h2
          ref={headingRef}
          tabIndex={-1}
          style={{
            margin: '0 0 var(--space-1, 4px) 0',
            fontSize: 'var(--text-heading-size, 18px)',
            fontWeight: 600,
            lineHeight: 'var(--text-heading-line-height, 1.25)',
            color: 'var(--color-text-primary)',
            outline: 'none',
          }}
        >
          Reset password
        </h2>

        {/* Member subtitle */}
        <div
          style={{
            fontSize: 'var(--text-body-size, 15px)',
            fontWeight: 400,
            color: 'var(--color-text-secondary)',
            marginBottom: 'var(--space-6, 24px)',
          }}
        >
          {member.displayName ?? 'Member'}
        </div>

        {/* New password */}
        <div style={{ marginBottom: 'var(--space-3, 12px)' }}>
          <label
            htmlFor="reset-new-password"
            style={{
              display: 'block',
              fontSize: 'var(--text-label-size, 13px)',
              fontWeight: 600,
              color: 'var(--color-text-primary)',
              marginBottom: 'var(--space-1, 4px)',
            }}
          >
            New password
          </label>
          <input
            id="reset-new-password"
            type="password"
            autoComplete="new-password"
            value={newPassword}
            onChange={(e) => setNewPassword(e.target.value)}
            style={{
              width: '100%',
              boxSizing: 'border-box',
              padding: 'var(--space-3, 12px) var(--space-4, 16px)',
              border: `1px solid ${error ? 'var(--color-destructive)' : 'var(--color-border)'}`,
              borderRadius: 'var(--space-1, 4px)',
              fontSize: 'var(--text-body-size, 15px)',
              color: 'var(--color-text-primary)',
              background: 'var(--color-surface)',
              fontFamily: 'var(--font-family-base)',
              outline: 'none',
              minHeight: '44px',
            }}
          />
        </div>

        {/* Confirm new password */}
        <div style={{ marginBottom: 'var(--space-4, 16px)' }}>
          <label
            htmlFor="reset-confirm-password"
            style={{
              display: 'block',
              fontSize: 'var(--text-label-size, 13px)',
              fontWeight: 600,
              color: 'var(--color-text-primary)',
              marginBottom: 'var(--space-1, 4px)',
            }}
          >
            Confirm new password
          </label>
          <input
            id="reset-confirm-password"
            type="password"
            autoComplete="new-password"
            value={confirmPassword}
            onChange={(e) => setConfirmPassword(e.target.value)}
            aria-describedby={error ? 'reset-error' : undefined}
            style={{
              width: '100%',
              boxSizing: 'border-box',
              padding: 'var(--space-3, 12px) var(--space-4, 16px)',
              border: `1px solid ${error ? 'var(--color-destructive)' : 'var(--color-border)'}`,
              borderRadius: 'var(--space-1, 4px)',
              fontSize: 'var(--text-body-size, 15px)',
              color: 'var(--color-text-primary)',
              background: 'var(--color-surface)',
              fontFamily: 'var(--font-family-base)',
              outline: 'none',
              minHeight: '44px',
            }}
          />
        </div>

        {/* Inline error */}
        {error && (
          <div
            id="reset-error"
            style={{
              fontSize: 'var(--text-label-size, 13px)',
              fontWeight: 400,
              color: 'var(--color-destructive)',
              marginBottom: 'var(--space-4, 16px)',
            }}
          >
            {error}
          </div>
        )}

        {/* Action row */}
        <div
          style={{
            display: 'flex',
            justifyContent: 'flex-end',
            gap: 'var(--space-3, 12px)',
          }}
        >
          <button
            type="button"
            onClick={handleClose}
            disabled={isPending}
            style={{
              background: 'none',
              border: 'none',
              cursor: isPending ? 'default' : 'pointer',
              fontSize: 'var(--text-label-size, 13px)',
              fontWeight: 600,
              color: 'var(--color-text-secondary)',
              minHeight: '44px',
              minWidth: '44px',
              padding: '0 var(--space-4, 16px)',
              fontFamily: 'var(--font-family-base)',
              borderRadius: 'var(--space-1, 4px)',
            }}
          >
            Cancel
          </button>

          <button
            type="button"
            disabled={submitDisabled}
            onClick={() => {
              setError(null);
              resetMutation.mutate();
            }}
            style={{
              background: submitDisabled
                ? 'var(--color-border, #e2e4e9)'
                : 'var(--color-member-0, #4a90d9)',
              color: '#ffffff',
              border: 'none',
              cursor: submitDisabled ? 'default' : 'pointer',
              fontSize: 'var(--text-label-size, 13px)',
              fontWeight: 600,
              minHeight: '44px',
              minWidth: '44px',
              padding: '0 var(--space-4, 16px)',
              borderRadius: 'var(--space-1, 4px)',
              fontFamily: 'var(--font-family-base)',
              transition: 'background 0.15s ease',
            }}
          >
            Reset password
          </button>
        </div>
      </div>
    </>
  );
}

// ── EmptyCalendarsState ─────────────────────────────────────────────────────

function EmptyCalendarsState() {
  return (
    <div
      style={{
        padding: 'var(--space-8, 32px) 0',
        textAlign: 'center',
      }}
    >
      <div
        style={{
          fontSize: 'var(--text-body-size, 15px)',
          fontWeight: 600,
          color: 'var(--color-text-primary)',
          marginBottom: 'var(--space-2, 8px)',
        }}
      >
        No calendars synced yet
      </div>
      <div
        style={{
          fontSize: 'var(--text-label-size, 13px)',
          color: 'var(--color-text-secondary)',
          lineHeight: 1.5,
        }}
      >
        Calendars sync automatically. Check back after the first sync completes.
      </div>
    </div>
  );
}
