# AmroUI meeting

Installable AmroUI product component source.

## Installation

```bash
npx shadcn@latest add https://amro-ui.vercel.app/r/amro-ui-meeting.json
```

[Registry JSON](https://amro-ui.vercel.app/r/amro-ui-meeting.json)

## Source

### source/components/meeting.tsx

```tsx
import {
  AlertTriangle,
  ArrowRight,
  Bot,
  CalendarCheck,
  CalendarDays,
  Check,
  ChevronRight,
  Clock3,
  Code2,
  Copy,
  ExternalLink,
  Globe2,
  Headphones,
  Mail,
  MessageSquare,
  Phone,
  RefreshCw,
  ShieldCheck,
  Sparkles,
  UserRound,
  Users,
  Video,
  Wifi,
  X
} from "lucide-react";
import { useState, type ButtonHTMLAttributes, type HTMLAttributes, type ReactNode } from "react";
import { cn } from "../lib/cn.js";
import { Avatar } from "./avatar.js";
import { Badge } from "./badge.js";
import { Button } from "./button.js";
import { Card } from "./card.js";
import { FeaturePanel, StatusIndicator } from "./feature-primitives.js";
import { DateInput, Input, Select, Textarea, TimeInput } from "./input.js";

function getAccessibleText(value: ReactNode, fallback: string): string {
  return typeof value === "string" || typeof value === "number" ? String(value) : fallback;
}

export type HostAvailability = "available" | "busy" | "away" | "offline";
export interface InstantMeetCTAProps extends HTMLAttributes<HTMLDivElement> {
  host?: ReactNode;
  availability?: HostAvailability;
  waitMinutes?: number;
  duration?: ReactNode;
  onStart?: () => void;
  onSchedule?: () => void;
}
export function InstantMeetCTA({
  availability = "available",
  className,
  duration = "Up to 30 minutes",
  host = "Amro specialist",
  onSchedule,
  onStart,
  waitMinutes = 0,
  ...props
}: InstantMeetCTAProps) {
  const available = availability === "available";
  return (
    <div className={cn("amro-instant-meet", className)} {...props}>
      <span className="amro-instant-meet__icon">
        <Video aria-hidden="true" />
        <i aria-hidden="true" />
      </span>
      <span>
        <span className="amro-eyebrow">Live conversation</span>
        <strong>
          {available
            ? `${getAccessibleText(host, "Host")} is available now`
            : `${getAccessibleText(host, "Host")} is ${availability}`}
        </strong>
        <small>
          {available
            ? `${getAccessibleText(duration, "Flexible duration")} · no login required`
            : waitMinutes
              ? `Expected in ${waitMinutes} minutes`
              : "Choose a scheduled time instead"}
        </small>
      </span>
      <StatusIndicator
        label={available ? "Available now" : availability}
        status={available ? "online" : availability === "busy" ? "warning" : "offline"}
      />
      <div>
        {available && onStart && (
          <Button onClick={onStart}>
            Talk now
            <Video aria-hidden="true" />
          </Button>
        )}
        {onSchedule && (
          <Button onClick={onSchedule} variant={available ? "tertiary" : "primary"}>
            Schedule instead
          </Button>
        )}
      </div>
    </div>
  );
}

export interface GuestDetails {
  name: string;
  email: string;
  reason: string;
}
export interface NoLoginGuestFormProps extends Omit<
  HTMLAttributes<HTMLFormElement>,
  "onSubmit" | "defaultValue"
> {
  defaultValue?: Partial<GuestDetails>;
  privacyLabel?: ReactNode;
  onSubmit?: (details: GuestDetails) => void;
}
export function NoLoginGuestForm({
  className,
  defaultValue,
  onSubmit,
  privacyLabel = "Used only for this meeting and its calendar invitation.",
  ...props
}: NoLoginGuestFormProps) {
  const [details, setDetails] = useState<GuestDetails>({
    name: defaultValue?.name ?? "",
    email: defaultValue?.email ?? "",
    reason: defaultValue?.reason ?? ""
  });
  const update = (key: keyof GuestDetails, value: string) =>
    setDetails((current) => ({ ...current, [key]: value }));
  const valid =
    details.name.trim().length > 1 &&
    /^\S+@\S+\.\S+$/.test(details.email) &&
    details.reason.trim().length > 3;
  return (
    <form
      className={cn("amro-guest-form", className)}
      onSubmit={(event) => {
        event.preventDefault();
        if (valid) onSubmit?.(details);
      }}
      {...props}
    >
      <FeaturePanel
        eyebrow="No account needed"
        title="Tell the host who is joining"
        description="Three details, then AmroMeet creates the room."
      >
        <div>
          <Input
            autoComplete="name"
            label="Name"
            value={details.name}
            onChange={(event) => update("name", event.target.value)}
          />
          <Input
            autoComplete="email"
            label="Email"
            type="email"
            value={details.email}
            onChange={(event) => update("email", event.target.value)}
          />
          <label>
            <span>What would you like to discuss?</span>
            <Textarea
              onChange={(event) => update("reason", event.target.value)}
              value={details.reason}
            />
          </label>
        </div>
        <footer>
          <span>
            <Check aria-hidden="true" />
            {privacyLabel}
          </span>
          <Button disabled={!valid} type="submit">
            Continue to meeting
            <ArrowRight aria-hidden="true" />
          </Button>
        </footer>
      </FeaturePanel>
    </form>
  );
}

export interface LiveMeetReadyCardProps extends Omit<HTMLAttributes<HTMLDivElement>, "onCopy"> {
  host?: ReactNode;
  joinUrl?: string;
  expiresAt?: ReactNode;
  emailStatus?: "sending" | "sent" | "failed";
  onCopy?: (url: string) => void;
  onJoin?: (url: string) => void;
}
export function LiveMeetReadyCard({
  className,
  emailStatus = "sent",
  expiresAt = "Link expires in 45 minutes",
  host = "Maya Chen",
  joinUrl = "https://meet.google.com/amro-live",
  onCopy,
  onJoin,
  ...props
}: LiveMeetReadyCardProps) {
  return (
    <FeaturePanel
      className={cn("amro-live-meet-ready", className)}
      eyebrow="Your meeting is ready"
      title={`Join ${getAccessibleText(host, "the host")} now`}
      description={expiresAt}
      status={<StatusIndicator label="Room live" status="online" />}
      {...props}
    >
      <div className="amro-live-meet-ready__link">
        <Video aria-hidden="true" />
        <code>{joinUrl}</code>
        <Button
          aria-label="Copy meeting link"
          onClick={() => onCopy?.(joinUrl)}
          size="sm"
          variant="secondary"
        >
          <Copy aria-hidden="true" />
        </Button>
      </div>
      <div className="amro-live-meet-ready__delivery">
        <StatusIndicator
          label={`Invitation ${emailStatus}`}
          status={
            emailStatus === "sent" ? "success" : emailStatus === "sending" ? "loading" : "error"
          }
        />
        <span>Calendar details include the secure joining link.</span>
      </div>
      {onJoin && (
        <Button onClick={() => onJoin(joinUrl)} size="lg">
          Join Google Meet
          <ExternalLink aria-hidden="true" />
        </Button>
      )}
    </FeaturePanel>
  );
}

export interface HostPresenceIndicatorProps extends HTMLAttributes<HTMLDivElement> {
  host?: string;
  avatarUrl?: string;
  availability?: HostAvailability;
  currentActivity?: ReactNode;
  nextAvailable?: ReactNode;
  compact?: boolean;
}
export function HostPresenceIndicator({
  availability = "available",
  avatarUrl,
  className,
  compact = false,
  currentActivity,
  host = "Maya Chen",
  nextAvailable = "Today at 15:30",
  ...props
}: HostPresenceIndicatorProps) {
  return (
    <div
      className={cn("amro-host-presence", compact && "amro-host-presence--compact", className)}
      {...props}
    >
      <span>
        <Avatar name={host} {...(avatarUrl ? { src: avatarUrl } : {})} />
        <i data-state={availability} />
      </span>
      <span>
        <strong>{host}</strong>
        <small>
          {availability === "available"
            ? "Available for a live meeting"
            : (currentActivity ?? `Next available ${getAccessibleText(nextAvailable, "soon")}`)}
        </small>
      </span>
      <StatusIndicator
        label={availability}
        status={
          availability === "available" ? "online" : availability === "busy" ? "warning" : "offline"
        }
      />
    </div>
  );
}

export interface TalkNowFallbackProps extends HTMLAttributes<HTMLDivElement> {
  host?: ReactNode;
  reason?: ReactNode;
  nextSlot?: ReactNode;
  timezone?: ReactNode;
  onBookNext?: () => void;
  onSeeTimes?: () => void;
}
export function TalkNowFallback({
  className,
  host = "Maya",
  nextSlot = "Today, 15:30",
  onBookNext,
  onSeeTimes,
  reason = "Currently in another meeting",
  timezone = "Europe/Helsinki",
  ...props
}: TalkNowFallbackProps) {
  return (
    <FeaturePanel
      className={cn("amro-talk-fallback", className)}
      eyebrow="Live meeting unavailable"
      title={`${getAccessibleText(host, "The host")} cannot talk right now`}
      description={reason}
      status={<StatusIndicator label="Busy" status="warning" />}
      {...props}
    >
      <div>
        <CalendarDays aria-hidden="true" />
        <span>
          <small>Next bookable slot</small>
          <strong>{nextSlot}</strong>
          <small>{timezone}</small>
        </span>
      </div>
      <footer>
        {onSeeTimes && (
          <Button onClick={onSeeTimes} variant="secondary">
            See all times
          </Button>
        )}
        {onBookNext && <Button onClick={onBookNext}>Book next slot</Button>}
      </footer>
    </FeaturePanel>
  );
}

export type EventTypeId = "consultation" | "demo" | "support";
export interface MeetingEventType {
  id: EventTypeId;
  label: ReactNode;
  duration: number;
  channel: "video" | "phone";
  description?: ReactNode;
  inclusions?: readonly ReactNode[];
}
export interface EventTypeSwitcherProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
  events?: readonly MeetingEventType[];
  value?: EventTypeId;
  onChange?: (event: MeetingEventType) => void;
}
const eventTypes: readonly MeetingEventType[] = [
  {
    id: "consultation",
    label: "Consultation",
    duration: 30,
    channel: "video",
    description: "Explore your goal with an Amro specialist.",
    inclusions: ["Goal review", "Practical next steps"]
  },
  {
    id: "demo",
    label: "Product demo",
    duration: 45,
    channel: "video",
    description: "A tailored walkthrough using your use case.",
    inclusions: ["Live product tour", "Questions"]
  },
  {
    id: "support",
    label: "Support call",
    duration: 20,
    channel: "video",
    description: "Resolve a specific product question.",
    inclusions: ["Issue diagnosis", "Follow-up summary"]
  }
];
export function EventTypeSwitcher({
  className,
  events = eventTypes,
  onChange,
  value: controlled,
  ...props
}: EventTypeSwitcherProps) {
  const [internal, setInternal] = useState<EventTypeId>(events[0]?.id ?? "consultation");
  const value = controlled ?? internal;
  return (
    <div
      className={cn("amro-event-switcher", className)}
      role="tablist"
      aria-label="Meeting type"
      {...props}
    >
      {events.map((event) => (
        <button
          aria-selected={value === event.id}
          key={event.id}
          onClick={() => {
            setInternal(event.id);
            onChange?.(event);
          }}
          role="tab"
          type="button"
        >
          <span>
            {event.channel === "video" ? (
              <Video aria-hidden="true" />
            ) : (
              <Phone aria-hidden="true" />
            )}
          </span>
          <span>
            <strong>{event.label}</strong>
            <small>{event.duration} min</small>
          </span>
        </button>
      ))}
    </div>
  );
}

export interface EventTypeCardProps extends Omit<HTMLAttributes<HTMLDivElement>, "onSelect"> {
  event?: MeetingEventType;
  selected?: boolean;
  host?: ReactNode;
  onSelect?: (event: MeetingEventType) => void;
}
export function EventTypeCard({
  className,
  event = eventTypes[0]!,
  host = "Amro specialist",
  onSelect,
  selected = false,
  ...props
}: EventTypeCardProps) {
  return (
    <Card
      className={cn(
        "amro-event-type-card",
        selected && "amro-event-type-card--selected",
        className
      )}
      {...props}
    >
      <header>
        <span>
          {event.channel === "video" ? <Video aria-hidden="true" /> : <Phone aria-hidden="true" />}
        </span>
        <span>
          <strong>{event.label}</strong>
          <small>{event.description}</small>
        </span>
        {selected && (
          <Badge tone="success">
            <Check aria-hidden="true" />
            Selected
          </Badge>
        )}
      </header>
      <div>
        <Badge tone="neutral">
          <Clock3 aria-hidden="true" />
          {event.duration} minutes
        </Badge>
        <Badge tone="neutral">
          <UserRound aria-hidden="true" />
          {host}
        </Badge>
      </div>
      <ul>
        {event.inclusions?.map((item, index) => (
          <li key={index}>
            <Check aria-hidden="true" />
            {item}
          </li>
        ))}
      </ul>
      {onSelect && (
        <Button onClick={() => onSelect(event)} variant={selected ? "secondary" : "primary"}>
          {selected ? "Selected" : "Choose event"}
        </Button>
      )}
    </Card>
  );
}

export interface DateAvailabilityCellProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  date?: number;
  availability?: "none" | "low" | "medium" | "high";
  selected?: boolean;
  today?: boolean;
  outsideMonth?: boolean;
  slotCount?: number;
}
export function DateAvailabilityCell({
  availability = "high",
  className,
  date = 12,
  outsideMonth = false,
  selected = false,
  slotCount = 8,
  today = false,
  ...props
}: DateAvailabilityCellProps) {
  const disabled = props.disabled || availability === "none" || outsideMonth;
  return (
    <button
      aria-label={`${date}, ${slotCount} available slots`}
      aria-pressed={selected}
      className={cn("amro-date-cell", className)}
      data-availability={availability}
      data-today={today}
      disabled={disabled}
      type="button"
      {...props}
    >
      <span>{date}</span>
      {!outsideMonth && availability !== "none" && <i aria-hidden="true" />}
      {slotCount > 0 && !disabled && <small>{slotCount}</small>}
    </button>
  );
}

export interface MeetingSlot {
  id: string;
  label: ReactNode;
  period: "morning" | "afternoon" | "evening";
  timezone?: ReactNode;
  available?: boolean;
  recommended?: boolean;
}
export interface TimeSlotListProps extends Omit<HTMLAttributes<HTMLDivElement>, "onSelect"> {
  slots?: readonly MeetingSlot[];
  value?: string;
  timezone?: ReactNode;
  onSelect?: (slot: MeetingSlot) => void;
}
const meetingSlots: readonly MeetingSlot[] = [
  { id: "0930", label: "09:30", period: "morning", available: true },
  { id: "1030", label: "10:30", period: "morning", available: true, recommended: true },
  { id: "1130", label: "11:30", period: "morning", available: false },
  { id: "1400", label: "14:00", period: "afternoon", available: true },
  { id: "1530", label: "15:30", period: "afternoon", available: true },
  { id: "1700", label: "17:00", period: "evening", available: true }
];
export function TimeSlotList({
  className,
  onSelect,
  slots = meetingSlots,
  timezone = "Europe/Helsinki",
  value,
  ...props
}: TimeSlotListProps) {
  return (
    <div className={cn("amro-time-slots", className)} {...props}>
      <header>
        <span>
          <strong>Available times</strong>
          <small>{timezone}</small>
        </span>
        <Badge tone="neutral">
          {slots.filter((slot) => slot.available !== false).length} slots
        </Badge>
      </header>
      {(["morning", "afternoon", "evening"] as const).map((period) => {
        const group = slots.filter((slot) => slot.period === period);
        return group.length ? (
          <section key={period}>
            <h3>{period}</h3>
            <div>
              {group.map((slot) => (
                <button
                  aria-pressed={value === slot.id}
                  disabled={slot.available === false}
                  key={slot.id}
                  onClick={() => onSelect?.(slot)}
                  type="button"
                >
                  <strong>{slot.label}</strong>
                  {slot.recommended && <small>Best fit</small>}
                </button>
              ))}
            </div>
          </section>
        ) : null;
      })}
    </div>
  );
}

export interface TimezonePillProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
  timezone?: string;
  detected?: boolean;
  options?: readonly string[];
  onChange?: (timezone: string) => void;
}
export function TimezonePill({
  className,
  detected = true,
  onChange,
  options = ["Europe/Helsinki", "Europe/London", "America/New_York", "Asia/Kolkata"],
  timezone = "Europe/Helsinki",
  ...props
}: TimezonePillProps) {
  const [editing, setEditing] = useState(false);
  return (
    <div className={cn("amro-timezone-pill", className)} {...props}>
      <Globe2 aria-hidden="true" />
      {editing ? (
        <Select
          aria-label="Timezone"
          autoFocus
          onBlur={() => setEditing(false)}
          onChange={(event) => {
            onChange?.(event.target.value);
            setEditing(false);
          }}
          value={timezone}
        >
          {options.map((option) => (
            <option key={option}>{option}</option>
          ))}
        </Select>
      ) : (
        <button onClick={() => setEditing(true)} type="button">
          <span>{timezone}</span>
          <small>{detected ? "Automatically detected" : "Custom timezone"}</small>
          <ChevronRight aria-hidden="true" />
        </button>
      )}
    </div>
  );
}

export type BookingStep = "event" | "date" | "time" | "details" | "confirmed";
export interface BookingStepHeaderProps extends HTMLAttributes<HTMLOListElement> {
  current?: BookingStep;
  completed?: readonly BookingStep[];
  onStepSelect?: (step: BookingStep) => void;
}
const bookingSteps: readonly { id: BookingStep; label: string }[] = [
  { id: "event", label: "Event" },
  { id: "date", label: "Date" },
  { id: "time", label: "Time" },
  { id: "details", label: "Details" },
  { id: "confirmed", label: "Confirmed" }
];
export function BookingStepHeader({
  className,
  completed = ["event", "date"],
  current = "time",
  onStepSelect,
  ...props
}: BookingStepHeaderProps) {
  return (
    <ol className={cn("amro-booking-steps", className)} {...props}>
      {bookingSteps.map((step, index) => {
        const done = completed.includes(step.id);
        const active = current === step.id;
        return (
          <li data-active={active} data-complete={done} key={step.id}>
            <button
              aria-current={active ? "step" : undefined}
              disabled={!done && !active}
              onClick={() => onStepSelect?.(step.id)}
              type="button"
            >
              <span>{done ? <Check aria-hidden="true" /> : index + 1}</span>
              <strong>{step.label}</strong>
            </button>
          </li>
        );
      })}
    </ol>
  );
}

export interface BookingDetails {
  name: string;
  email: string;
  organisation: string;
  reason: string;
  consent: boolean;
}
export interface BookingDetailsFormProps extends Omit<
  HTMLAttributes<HTMLFormElement>,
  "onSubmit" | "defaultValue"
> {
  defaultValue?: Partial<BookingDetails>;
  onSubmit?: (details: BookingDetails) => void;
}
export function BookingDetailsForm({
  className,
  defaultValue,
  onSubmit,
  ...props
}: BookingDetailsFormProps) {
  const [details, setDetails] = useState<BookingDetails>({
    name: defaultValue?.name ?? "",
    email: defaultValue?.email ?? "",
    organisation: defaultValue?.organisation ?? "",
    reason: defaultValue?.reason ?? "",
    consent: defaultValue?.consent ?? false
  });
  const update = <K extends keyof BookingDetails>(key: K, value: BookingDetails[K]) =>
    setDetails((current) => ({ ...current, [key]: value }));
  const valid =
    details.name.trim().length > 1 &&
    /^\S+@\S+\.\S+$/.test(details.email) &&
    details.reason.trim().length > 3;
  return (
    <form
      className={cn("amro-booking-details", className)}
      onSubmit={(event) => {
        event.preventDefault();
        if (valid) onSubmit?.(details);
      }}
      {...props}
    >
      <FeaturePanel
        eyebrow="Your details"
        title="Help the host prepare"
        description="No account is created."
      >
        <div>
          <Input
            label="Name"
            value={details.name}
            onChange={(event) => update("name", event.target.value)}
          />
          <Input
            label="Work email"
            type="email"
            value={details.email}
            onChange={(event) => update("email", event.target.value)}
          />
          <Input
            label="Organisation (optional)"
            value={details.organisation}
            onChange={(event) => update("organisation", event.target.value)}
          />
          <label>
            <span>Meeting reason</span>
            <Textarea
              onChange={(event) => update("reason", event.target.value)}
              value={details.reason}
            />
          </label>
        </div>
        <label className="amro-booking-details__consent">
          <input
            checked={details.consent}
            onChange={(event) => update("consent", event.target.checked)}
            type="checkbox"
          />
          Send me the calendar invitation and meeting follow-up.
        </label>
        <footer>
          <span>
            <ShieldCheck aria-hidden="true" />
            Your reason is shared only with the assigned host.
          </span>
          <Button disabled={!valid} type="submit">
            Review booking
            <ArrowRight aria-hidden="true" />
          </Button>
        </footer>
      </FeaturePanel>
    </form>
  );
}

export interface MeetingReason {
  intent: ReactNode;
  outcome?: ReactNode;
  urgency?: "today" | "this-week" | "flexible";
  topics?: readonly ReactNode[];
  notes?: ReactNode;
}
export interface MeetingReasonCardProps extends HTMLAttributes<HTMLDivElement> {
  reason?: MeetingReason;
  onEdit?: () => void;
}
const sampleMeetingReason: MeetingReason = {
  intent: "Evaluate AmroAgents for customer operations",
  outcome: "Agree whether a pilot is a fit",
  urgency: "this-week",
  topics: ["Human approval", "CRM integration", "Outcome measurement"],
  notes: "Team of 35 support specialists."
};
export function MeetingReasonCard({
  className,
  onEdit,
  reason = sampleMeetingReason,
  ...props
}: MeetingReasonCardProps) {
  return (
    <Card className={cn("amro-meeting-reason", className)} {...props}>
      <header>
        <span>
          <MessageSquare aria-hidden="true" />
        </span>
        <span>
          <span className="amro-eyebrow">Meeting intent</span>
          <strong>{reason.intent}</strong>
          <small>Urgency: {reason.urgency}</small>
        </span>
        {onEdit && (
          <Button onClick={onEdit} size="sm" variant="tertiary">
            Edit
          </Button>
        )}
      </header>
      {reason.outcome && (
        <div>
          <small>Desired outcome</small>
          <strong>{reason.outcome}</strong>
        </div>
      )}
      <div className="amro-meeting-reason__topics">
        {reason.topics?.map((topic, index) => (
          <Badge key={index} tone="neutral">
            {topic}
          </Badge>
        ))}
      </div>
      {reason.notes && <p>{reason.notes}</p>}
    </Card>
  );
}

export type SourceProduct = "agents" | "gen" | "pilot" | "academy" | "vision" | "direct";
export interface SourceContextChipProps extends HTMLAttributes<HTMLSpanElement> {
  source?: SourceProduct;
  label?: ReactNode;
  contextCount?: number;
}
const sourceLabels: Record<SourceProduct, string> = {
  agents: "AmroAgents",
  gen: "AmroGen",
  pilot: "AmroPilot",
  academy: "AmroAcademy",
  vision: "AmroVisionAI",
  direct: "Direct booking"
};
export function SourceContextChip({
  className,
  contextCount = 3,
  label,
  source = "agents",
  ...props
}: SourceContextChipProps) {
  return (
    <span
      className={cn("amro-source-context", `amro-source-context--${source}`, className)}
      {...props}
    >
      <Bot aria-hidden="true" />
      <span>
        <strong>{label ?? sourceLabels[source]}</strong>
        <small>{contextCount} context items carried in</small>
      </span>
    </span>
  );
}

export interface BriefEvidence {
  id: string;
  label: ReactNode;
  value: ReactNode;
  source?: ReactNode;
}
export interface AIMeetingBriefProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
  title?: ReactNode;
  guest?: ReactNode;
  summary?: ReactNode;
  goals?: readonly ReactNode[];
  evidence?: readonly BriefEvidence[];
  risks?: readonly ReactNode[];
  source?: SourceProduct;
  onRefresh?: () => void;
}
export function AIMeetingBrief({
  className,
  evidence = [
    { id: "team", label: "Team", value: "35 support specialists", source: "Guest reason" },
    { id: "stack", label: "CRM", value: "HubSpot", source: "Agent conversation" },
    { id: "priority", label: "Priority", value: "Human approval", source: "Selected topic" }
  ],
  goals = ["Assess pilot fit", "Validate CRM workflow", "Define a measurable first outcome"],
  guest = "Alex Morgan · Northstar Labs",
  onRefresh,
  risks = ["Needs legal review for outbound automation"],
  source = "agents",
  summary = "Northstar is evaluating governed AI support automation for a 35-person team. The guest wants a practical pilot path with visible human controls.",
  title = "AI pre-call brief",
  ...props
}: AIMeetingBriefProps) {
  return (
    <FeaturePanel
      className={cn("amro-ai-meeting-brief", className)}
      eyebrow="Prepared for the host"
      title={title}
      description={guest}
      status={<SourceContextChip contextCount={evidence.length} source={source} />}
      actions={
        onRefresh && (
          <Button onClick={onRefresh} size="sm" variant="tertiary">
            <RefreshCw aria-hidden="true" />
            Refresh
          </Button>
        )
      }
      {...props}
    >
      <section>
        <Sparkles aria-hidden="true" />
        <p>{summary}</p>
      </section>
      <div className="amro-ai-meeting-brief__columns">
        <div>
          <strong>Meeting goals</strong>
          <ol>
            {goals.map((goal, index) => (
              <li key={index}>
                <span>{index + 1}</span>
                {goal}
              </li>
            ))}
          </ol>
        </div>
        <div>
          <strong>Known context</strong>
          <dl>
            {evidence.map((item) => (
              <div key={item.id}>
                <dt>{item.label}</dt>
                <dd>
                  {item.value}
                  <small>{item.source}</small>
                </dd>
              </div>
            ))}
          </dl>
        </div>
      </div>
      {risks.length > 0 && (
        <footer>
          {risks.map((risk, index) => (
            <span key={index}>
              <AlertTriangle aria-hidden="true" />
              {risk}
            </span>
          ))}
        </footer>
      )}
    </FeaturePanel>
  );
}

export interface BookingSummaryCardProps extends HTMLAttributes<HTMLDivElement> {
  event?: MeetingEventType;
  host?: string;
  hostAvatarUrl?: string;
  date?: ReactNode;
  timezone?: ReactNode;
  method?: ReactNode;
  guest?: ReactNode;
  onEdit?: (section: "event" | "date" | "guest") => void;
}
export function BookingSummaryCard({
  className,
  date = "Tuesday, 12 August · 15:30–16:00",
  event = eventTypes[0]!,
  guest = "alex@northstar.example",
  host = "Maya Chen",
  hostAvatarUrl,
  method = "Google Meet",
  onEdit,
  timezone = "Europe/Helsinki",
  ...props
}: BookingSummaryCardProps) {
  return (
    <FeaturePanel
      className={cn("amro-booking-summary", className)}
      eyebrow="Review booking"
      title={event.label}
      description={event.description}
      status={
        <Badge tone="neutral">
          <Clock3 aria-hidden="true" />
          {event.duration} min
        </Badge>
      }
      {...props}
    >
      <dl>
        <div>
          <dt>
            <UserRound aria-hidden="true" />
            Host
          </dt>
          <dd>
            <Avatar name={host} {...(hostAvatarUrl ? { src: hostAvatarUrl } : {})} />
            <span>
              <strong>{host}</strong>
              <small>{method}</small>
            </span>
          </dd>
        </div>
        <div>
          <dt>
            <CalendarDays aria-hidden="true" />
            Date and time
          </dt>
          <dd>
            <span>
              <strong>{date}</strong>
              <small>{timezone}</small>
            </span>
            {onEdit && (
              <Button onClick={() => onEdit("date")} size="sm" variant="tertiary">
                Change
              </Button>
            )}
          </dd>
        </div>
        <div>
          <dt>
            <Mail aria-hidden="true" />
            Guest
          </dt>
          <dd>
            <span>
              <strong>{guest}</strong>
              <small>Calendar invitation recipient</small>
            </span>
            {onEdit && (
              <Button onClick={() => onEdit("guest")} size="sm" variant="tertiary">
                Edit
              </Button>
            )}
          </dd>
        </div>
      </dl>
    </FeaturePanel>
  );
}

export interface ConfirmationCardProps extends Omit<HTMLAttributes<HTMLDivElement>, "onCopy"> {
  event?: ReactNode;
  host?: ReactNode;
  date?: ReactNode;
  timezone?: ReactNode;
  joinUrl?: string;
  calendarStatus?: "adding" | "added" | "failed";
  emailStatus?: "sending" | "sent" | "failed";
  onJoin?: () => void;
  onCopy?: (url: string) => void;
  onReschedule?: () => void;
  onCancel?: () => void;
}
export function ConfirmationCard({
  calendarStatus = "added",
  className,
  date = "Tuesday, 12 August · 15:30–16:00",
  emailStatus = "sent",
  event = "Consultation",
  host = "Maya Chen",
  joinUrl = "https://meet.google.com/amro-booking",
  onCancel,
  onCopy,
  onJoin,
  onReschedule,
  timezone = "Europe/Helsinki",
  ...props
}: ConfirmationCardProps) {
  return (
    <div className={cn("amro-confirmation-card", className)} {...props}>
      <header>
        <span>
          <Check aria-hidden="true" />
        </span>
        <span>
          <span className="amro-eyebrow">Booking confirmed</span>
          <h2>
            {event} with {host}
          </h2>
          <p>
            {date} · {timezone}
          </p>
        </span>
      </header>
      <div className="amro-confirmation-card__link">
        <Video aria-hidden="true" />
        <code>{joinUrl}</code>
        <Button
          aria-label="Copy meeting link"
          onClick={() => onCopy?.(joinUrl)}
          size="sm"
          variant="secondary"
        >
          <Copy aria-hidden="true" />
        </Button>
      </div>
      <div className="amro-confirmation-card__status">
        <StatusIndicator
          label={`Calendar ${calendarStatus}`}
          status={
            calendarStatus === "added"
              ? "success"
              : calendarStatus === "adding"
                ? "loading"
                : "error"
          }
        />
        <StatusIndicator
          label={`Email ${emailStatus}`}
          status={
            emailStatus === "sent" ? "success" : emailStatus === "sending" ? "loading" : "error"
          }
        />
      </div>
      <footer>
        {onJoin && (
          <Button onClick={onJoin}>
            Join meeting
            <ExternalLink aria-hidden="true" />
          </Button>
        )}
        {onReschedule && (
          <Button onClick={onReschedule} variant="secondary">
            Reschedule
          </Button>
        )}
        {onCancel && (
          <Button onClick={onCancel} variant="tertiary">
            Cancel booking
          </Button>
        )}
      </footer>
    </div>
  );
}

export interface DeliveryStep {
  id: "host-event" | "guest-invite" | "meet-link";
  label: ReactNode;
  status: "pending" | "working" | "complete" | "error";
  detail?: ReactNode;
}
export interface CalendarDeliveryStatusProps extends HTMLAttributes<HTMLDivElement> {
  steps?: readonly DeliveryStep[];
  onRetry?: (step: DeliveryStep) => void;
}
const deliverySteps: readonly DeliveryStep[] = [
  {
    id: "host-event",
    label: "Host calendar event created",
    status: "complete",
    detail: "Google Calendar"
  },
  {
    id: "guest-invite",
    label: "Guest invitation sent",
    status: "complete",
    detail: "alex@northstar.example"
  },
  {
    id: "meet-link",
    label: "Google Meet attached",
    status: "complete",
    detail: "Secure joining link"
  }
];
export function CalendarDeliveryStatus({
  className,
  onRetry,
  steps = deliverySteps,
  ...props
}: CalendarDeliveryStatusProps) {
  const ready = steps.every((step) => step.status === "complete");
  return (
    <FeaturePanel
      className={cn("amro-delivery-status", className)}
      eyebrow="Calendar delivery"
      title={ready ? "Everything is ready" : "Preparing your invitation"}
      status={
        <StatusIndicator
          label={ready ? "Complete" : "In progress"}
          status={ready ? "success" : "loading"}
        />
      }
      {...props}
    >
      <ol>
        {steps.map((step) => (
          <li key={step.id}>
            <span>
              {step.status === "complete" ? (
                <Check aria-hidden="true" />
              ) : step.status === "error" ? (
                <X aria-hidden="true" />
              ) : (
                <RefreshCw aria-hidden="true" />
              )}
            </span>
            <span>
              <strong>{step.label}</strong>
              <small>{step.detail}</small>
            </span>
            <StatusIndicator
              label={step.status}
              status={step.status === "working" ? "loading" : step.status}
            />
            {step.status === "error" && onRetry && (
              <Button onClick={() => onRetry(step)} size="sm" variant="secondary">
                Retry
              </Button>
            )}
          </li>
        ))}
      </ol>
    </FeaturePanel>
  );
}

export interface ReschedulePanelProps extends Omit<HTMLAttributes<HTMLDivElement>, "onSelect"> {
  event?: ReactNode;
  host?: ReactNode;
  currentSlot?: ReactNode;
  timezone?: ReactNode;
  newSlots?: readonly MeetingSlot[];
  value?: string;
  preserveContext?: boolean;
  onSelect?: (slot: MeetingSlot) => void;
  onConfirm?: (slot: MeetingSlot) => void;
  onClose?: () => void;
}
export function ReschedulePanel({
  className,
  currentSlot = "12 Aug · 15:30",
  event = "Consultation",
  host = "Maya Chen",
  newSlots = meetingSlots.filter((slot) => slot.available !== false).slice(0, 4),
  onClose,
  onConfirm,
  onSelect,
  preserveContext = true,
  timezone = "Europe/Helsinki",
  value: controlled,
  ...props
}: ReschedulePanelProps) {
  const [internal, setInternal] = useState<string>();
  const value = controlled ?? internal;
  const selected = newSlots.find((slot) => slot.id === value);
  return (
    <div className={cn("amro-reschedule-panel", className)} {...props}>
      <header>
        <span>
          <strong>Reschedule meeting</strong>
          <small>
            {event} with {host}
          </small>
        </span>
        {onClose && (
          <button aria-label="Close reschedule panel" onClick={onClose} type="button">
            <X aria-hidden="true" />
          </button>
        )}
      </header>
      <div className="amro-reschedule-panel__current">
        <small>Current time</small>
        <del>{currentSlot}</del>
        <Badge tone="neutral">{timezone}</Badge>
      </div>
      <section>
        <strong>Choose a new time</strong>
        <div>
          {newSlots.map((slot) => (
            <button
              aria-pressed={slot.id === value}
              key={slot.id}
              onClick={() => {
                setInternal(slot.id);
                onSelect?.(slot);
              }}
              type="button"
            >
              <span>{slot.label}</span>
              <small>{slot.period}</small>
            </button>
          ))}
        </div>
      </section>
      {preserveContext && (
        <p>
          <Check aria-hidden="true" />
          Guest details, meeting reason, source context, and host stay unchanged.
        </p>
      )}
      <footer>
        <Button disabled={!selected} onClick={() => selected && onConfirm?.(selected)}>
          Confirm new time
        </Button>
      </footer>
    </div>
  );
}

export interface CancellationReasonDialogProps extends Omit<
  HTMLAttributes<HTMLDivElement>,
  "onSubmit"
> {
  open?: boolean;
  reasons?: readonly string[];
  onSubmit?: (reason: string, note: string) => void;
  onClose?: () => void;
  onReschedule?: () => void;
}
export function CancellationReasonDialog({
  className,
  onClose,
  onReschedule,
  onSubmit,
  open = true,
  reasons = [
    "No longer needed",
    "Schedule conflict",
    "Booked by mistake",
    "Need a different event type",
    "Other"
  ],
  ...props
}: CancellationReasonDialogProps) {
  const [reason, setReason] = useState("");
  const [note, setNote] = useState("");
  if (!open) return null;
  return (
    <div
      className={cn("amro-cancel-dialog", className)}
      role="dialog"
      aria-modal="true"
      aria-label="Cancel meeting"
      {...props}
    >
      <header>
        <span>
          <strong>Cancel this meeting?</strong>
          <small>Your reason helps the host improve availability.</small>
        </span>
        {onClose && (
          <button aria-label="Close cancellation dialog" onClick={onClose} type="button">
            <X aria-hidden="true" />
          </button>
        )}
      </header>
      <fieldset>
        <legend>Reason</legend>
        {reasons.map((item) => (
          <label key={item}>
            <input
              checked={reason === item}
              name="cancel-reason"
              onChange={() => setReason(item)}
              type="radio"
            />
            {item}
          </label>
        ))}
      </fieldset>
      {reason === "Other" && (
        <Input
          label="Optional note"
          onChange={(event) => setNote(event.target.value)}
          value={note}
        />
      )}
      <div>
        <CalendarDays aria-hidden="true" />
        <span>
          <strong>Could another time work?</strong>
          <small>Rescheduling keeps all meeting context.</small>
        </span>
        {onReschedule && (
          <Button onClick={onReschedule} size="sm" variant="secondary">
            Reschedule instead
          </Button>
        )}
      </div>
      <footer>
        <Button onClick={onClose} variant="secondary">
          Keep meeting
        </Button>
        <Button disabled={!reason} onClick={() => onSubmit?.(reason, note)}>
          Cancel meeting
        </Button>
      </footer>
    </div>
  );
}

export interface AvailabilityRule {
  day: string;
  enabled: boolean;
  start: string;
  end: string;
  breaks: readonly { start: string; end: string }[];
}
export interface AvailabilityOverride {
  date: string;
  available: boolean;
  start?: string;
  end?: string;
}
export interface AvailabilityRulesEditorProps extends Omit<
  HTMLAttributes<HTMLDivElement>,
  "onChange"
> {
  rules?: readonly AvailabilityRule[];
  overrides?: readonly AvailabilityOverride[];
  onChange?: (rules: readonly AvailabilityRule[]) => void;
  onOverrideChange?: (overrides: readonly AvailabilityOverride[]) => void;
}
const availabilityRules: readonly AvailabilityRule[] = [
  "Monday",
  "Tuesday",
  "Wednesday",
  "Thursday",
  "Friday",
  "Saturday",
  "Sunday"
].map((day, index) => ({
  day,
  enabled: index < 5,
  start: "09:00",
  end: "17:00",
  breaks: index < 5 ? [{ start: "12:00", end: "13:00" }] : []
}));
export function AvailabilityRulesEditor({
  className,
  onChange,
  onOverrideChange,
  overrides = [{ date: "2026-08-15", available: false }],
  rules = availabilityRules,
  ...props
}: AvailabilityRulesEditorProps) {
  const [items, setItems] = useState([...rules]);
  const [special, setSpecial] = useState([...overrides]);
  const update = (index: number, patch: Partial<AvailabilityRule>) => {
    const next = items.map((item, itemIndex) =>
      itemIndex === index ? { ...item, ...patch } : item
    );
    setItems(next);
    onChange?.(next);
  };
  return (
    <div className={cn("amro-rules-editor", className)} {...props}>
      <header>
        <span>
          <strong>Availability rules</strong>
          <small>Working hours, breaks, and date overrides</small>
        </span>
        <Badge tone="success">{items.filter((item) => item.enabled).length} active days</Badge>
      </header>
      <div>
        {items.map((rule, index) => (
          <article data-enabled={rule.enabled} key={rule.day}>
            <label>
              <input
                checked={rule.enabled}
                onChange={(event) => update(index, { enabled: event.target.checked })}
                type="checkbox"
              />
              <strong>{rule.day}</strong>
            </label>
            <input
              aria-label={`${rule.day} start`}
              disabled={!rule.enabled}
              onChange={(event) => update(index, { start: event.target.value })}
              value={rule.start}
            />
            <span>to</span>
            <TimeInput
              aria-label={`${rule.day} end`}
              disabled={!rule.enabled}
              onChange={(event) => update(index, { end: event.target.value })}
              value={rule.end}
            />
            <small>{rule.breaks.length ? `${rule.breaks.length} break` : "No breaks"}</small>
          </article>
        ))}
      </div>
      <section>
        <header>
          <strong>Date overrides</strong>
          <small>Specific dates take priority</small>
        </header>
        {special.map((override, index) => (
          <div key={override.date}>
            <DateInput
              aria-label="Override date"
              onChange={(event) => {
                const next = special.map((item, itemIndex) =>
                  itemIndex === index ? { ...item, date: event.target.value } : item
                );
                setSpecial(next);
                onOverrideChange?.(next);
              }}
              value={override.date}
            />
            <Select
              aria-label="Override availability"
              onChange={(event) => {
                const next = special.map((item, itemIndex) =>
                  itemIndex === index
                    ? { ...item, available: event.target.value === "available" }
                    : item
                );
                setSpecial(next);
                onOverrideChange?.(next);
              }}
              value={override.available ? "available" : "unavailable"}
            >
              <option value="available">Available</option>
              <option value="unavailable">Unavailable</option>
            </Select>
          </div>
        ))}
      </section>
    </div>
  );
}

export interface ConflictIndicatorProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
  type?: "calendar" | "buffer" | "limit" | "outside-hours";
  title?: ReactNode;
  detail?: ReactNode;
  calendar?: ReactNode;
  conflictingEvent?: ReactNode;
  onViewAlternative?: () => void;
}
export function ConflictIndicator({
  calendar = "Work calendar",
  className,
  conflictingEvent = "Customer review",
  detail = "14:00–14:30 overlaps an existing event and its 10-minute buffer.",
  onViewAlternative,
  title = "This time is unavailable",
  type = "calendar",
  ...props
}: ConflictIndicatorProps) {
  return (
    <div className={cn("amro-conflict-indicator", className)} role="status" {...props}>
      <AlertTriangle aria-hidden="true" />
      <span>
        <span className="amro-eyebrow">{type} conflict</span>
        <strong>{title}</strong>
        <p>{detail}</p>
        {type === "calendar" && (
          <span>
            <Badge tone="neutral">{calendar}</Badge>
            <span>{conflictingEvent}</span>
          </span>
        )}
      </span>
      {onViewAlternative && (
        <Button onClick={onViewAlternative} size="sm" variant="secondary">
          Find next available
        </Button>
      )}
    </div>
  );
}

export interface BufferTimeControlProps extends Omit<
  HTMLAttributes<HTMLFieldSetElement>,
  "onChange"
> {
  before?: number;
  after?: number;
  options?: readonly number[];
  onChange?: (value: { before: number; after: number }) => void;
}
export function BufferTimeControl({
  after: initialAfter = 10,
  before: initialBefore = 10,
  className,
  onChange,
  options = [0, 5, 10, 15, 30],
  ...props
}: BufferTimeControlProps) {
  const [before, setBefore] = useState(initialBefore);
  const [after, setAfter] = useState(initialAfter);
  const update = (key: "before" | "after", value: number) => {
    if (key === "before") setBefore(value);
    else setAfter(value);
    onChange?.({
      before: key === "before" ? value : before,
      after: key === "after" ? value : after
    });
  };
  return (
    <fieldset className={cn("amro-buffer-control", className)} {...props}>
      <legend>Protect time around meetings</legend>
      <label>
        <span>
          <strong>Before meeting</strong>
          <small>Preparation and transition</small>
        </span>
        <Select onChange={(event) => update("before", Number(event.target.value))} value={before}>
          {options.map((value) => (
            <option key={value} value={value}>
              {value} min
            </option>
          ))}
        </Select>
      </label>
      <div aria-hidden="true">
        <span>{before}m</span>
        <Video aria-hidden="true" />
        <span>{after}m</span>
      </div>
      <label>
        <span>
          <strong>After meeting</strong>
          <small>Notes and recovery</small>
        </span>
        <Select onChange={(event) => update("after", Number(event.target.value))} value={after}>
          {options.map((value) => (
            <option key={value} value={value}>
              {value} min
            </option>
          ))}
        </Select>
      </label>
    </fieldset>
  );
}

export interface MeetingLimitCardProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
  dailyLimit?: number;
  weeklyLimit?: number;
  bookedToday?: number;
  bookedThisWeek?: number;
  onChange?: (limits: { daily: number; weekly: number }) => void;
}
export function MeetingLimitCard({
  bookedThisWeek = 12,
  bookedToday = 3,
  className,
  dailyLimit: initialDaily = 5,
  onChange,
  weeklyLimit: initialWeekly = 20,
  ...props
}: MeetingLimitCardProps) {
  const [daily, setDaily] = useState(initialDaily);
  const [weekly, setWeekly] = useState(initialWeekly);
  return (
    <FeaturePanel
      className={cn("amro-meeting-limit", className)}
      eyebrow="Booking limits"
      title="Protect host capacity"
      description="Slots close automatically when a cap is reached."
      {...props}
    >
      <div>
        <section>
          <span>
            <strong>
              {bookedToday}/{daily}
            </strong>
            <small>Meetings today</small>
          </span>
          <progress max={daily} value={bookedToday} />
          <label>
            Daily cap
            <input
              min="1"
              onChange={(event) => {
                const value = Number(event.target.value);
                setDaily(value);
                onChange?.({ daily: value, weekly });
              }}
              type="number"
              value={daily}
            />
          </label>
        </section>
        <section>
          <span>
            <strong>
              {bookedThisWeek}/{weekly}
            </strong>
            <small>Meetings this week</small>
          </span>
          <progress max={weekly} value={bookedThisWeek} />
          <label>
            Weekly cap
            <input
              min="1"
              onChange={(event) => {
                const value = Number(event.target.value);
                setWeekly(value);
                onChange?.({ daily, weekly: value });
              }}
              type="number"
              value={weekly}
            />
          </label>
        </section>
      </div>
    </FeaturePanel>
  );
}

export interface BookingPagePreviewProps extends HTMLAttributes<HTMLDivElement> {
  event?: MeetingEventType;
  host?: string;
  theme?: "light" | "dark";
  accent?: "teal" | "cyan";
  compact?: boolean;
  onOpen?: () => void;
}
export function BookingPagePreview({
  accent = "teal",
  className,
  compact = false,
  event = eventTypes[0]!,
  host = "Maya Chen",
  onOpen,
  theme = "dark",
  ...props
}: BookingPagePreviewProps) {
  return (
    <div
      className={cn("amro-booking-preview", compact && "amro-booking-preview--compact", className)}
      data-accent={accent}
      data-preview-theme={theme}
      {...props}
    >
      <header>
        <span>
          <strong>Public booking preview</strong>
          <small>Live representation of the guest experience</small>
        </span>
        {onOpen && (
          <Button onClick={onOpen} size="sm" variant="secondary">
            Open page
            <ExternalLink aria-hidden="true" />
          </Button>
        )}
      </header>
      <div>
        <aside>
          <Avatar name={host} />
          <strong>{host}</strong>
          <small>{event.label}</small>
          <p>{event.description}</p>
          <span>
            <Clock3 aria-hidden="true" />
            {event.duration} min
          </span>
          <span>
            <Video aria-hidden="true" />
            Google Meet
          </span>
        </aside>
        <main>
          <BookingStepHeader current="date" completed={["event"]} />
          <div className="amro-booking-preview__calendar">
            <CalendarDays aria-hidden="true" />
            <strong>August 2026</strong>
            <p>Choose an available date to continue.</p>
            <div>
              {[10, 11, 12, 13, 14].map((date, index) => (
                <DateAvailabilityCell
                  availability={index === 1 ? "low" : index === 3 ? "medium" : "high"}
                  date={date}
                  key={date}
                  slotCount={index + 3}
                />
              ))}
            </div>
          </div>
        </main>
      </div>
    </div>
  );
}

export interface EmbedSettings {
  theme: "light" | "dark" | "auto";
  eventId: EventTypeId;
  height: number;
  hideHeader: boolean;
}
export interface EmbedConfiguratorProps extends Omit<
  HTMLAttributes<HTMLDivElement>,
  "onChange" | "onCopy"
> {
  value?: EmbedSettings;
  onChange?: (settings: EmbedSettings) => void;
  onCopy?: (code: string) => void;
}
export function EmbedConfigurator({
  className,
  onChange,
  onCopy,
  value = { theme: "auto", eventId: "consultation", height: 680, hideHeader: false },
  ...props
}: EmbedConfiguratorProps) {
  const [settings, setSettings] = useState(value);
  const update = <K extends keyof EmbedSettings>(key: K, next: EmbedSettings[K]) => {
    const updated = { ...settings, [key]: next };
    setSettings(updated);
    onChange?.(updated);
  };
  const code = `<iframe src="https://amro-ui.vercel.app/embed/${settings.eventId}?theme=${settings.theme}${settings.hideHeader ? "&header=0" : ""}" width="100%" height="${settings.height}" loading="lazy"></iframe>`;
  return (
    <div className={cn("amro-embed-config", className)} {...props}>
      <header>
        <span>
          <Code2 aria-hidden="true" />
          <span>
            <strong>Embed configurator</strong>
            <small>Generate a responsive AmroMeet embed</small>
          </span>
        </span>
        <Button onClick={() => onCopy?.(code)} size="sm" variant="secondary">
          <Copy aria-hidden="true" />
          Copy code
        </Button>
      </header>
      <div className="amro-embed-config__controls">
        <label>
          Theme
          <Select
            onChange={(event) => update("theme", event.target.value as EmbedSettings["theme"])}
            value={settings.theme}
          >
            <option value="auto">Match system</option>
            <option value="light">Light</option>
            <option value="dark">Dark</option>
          </Select>
        </label>
        <label>
          Event
          <Select
            onChange={(event) => update("eventId", event.target.value as EventTypeId)}
            value={settings.eventId}
          >
            {eventTypes.map((event) => (
              <option key={event.id} value={event.id}>
                {event.label}
              </option>
            ))}
          </Select>
        </label>
        <label>
          Height
          <input
            min="480"
            onChange={(event) => update("height", Number(event.target.value))}
            type="number"
            value={settings.height}
          />
        </label>
        <label>
          <input
            checked={settings.hideHeader}
            onChange={(event) => update("hideHeader", event.target.checked)}
            type="checkbox"
          />
          Hide product header
        </label>
      </div>
      <pre>
        <code>{code}</code>
      </pre>
    </div>
  );
}

export interface CalendarConnectionCardProps extends HTMLAttributes<HTMLDivElement> {
  provider?: ReactNode;
  account?: ReactNode;
  status?: "healthy" | "syncing" | "attention" | "disconnected";
  lastSynced?: ReactNode;
  calendars?: number;
  onReconnect?: () => void;
  onManage?: () => void;
}
export function CalendarConnectionCard({
  account = "maya@northstar.example",
  calendars = 3,
  className,
  lastSynced = "Synced 2 minutes ago",
  onManage,
  onReconnect,
  provider = "Google Calendar",
  status = "healthy",
  ...props
}: CalendarConnectionCardProps) {
  return (
    <Card className={cn("amro-calendar-connection", className)} {...props}>
      <header>
        <span>
          <CalendarCheck aria-hidden="true" />
        </span>
        <span>
          <strong>{provider}</strong>
          <small>{account}</small>
        </span>
        <StatusIndicator
          label={status}
          status={
            status === "healthy"
              ? "online"
              : status === "syncing"
                ? "loading"
                : status === "attention"
                  ? "warning"
                  : "offline"
          }
        />
      </header>
      <div>
        <span>
          <Wifi aria-hidden="true" />
          <strong>{calendars}</strong>
          <small>calendars checked for conflicts</small>
        </span>
        <span>
          <RefreshCw aria-hidden="true" />
          <strong>{lastSynced}</strong>
          <small>two-way availability sync</small>
        </span>
      </div>
      <footer>
        {status === "disconnected" && onReconnect ? (
          <Button onClick={onReconnect}>Reconnect calendar</Button>
        ) : (
          onManage && (
            <Button onClick={onManage} variant="secondary">
              Manage calendars
            </Button>
          )
        )}
      </footer>
    </Card>
  );
}

export type RoutingMode = "individual" | "round-robin" | "specialist";
export interface RoutingHost {
  id: string;
  name: string;
  role?: ReactNode;
  availability?: HostAvailability;
  weight?: number;
}
export interface HostRoutingCardProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
  mode?: RoutingMode;
  hosts?: readonly RoutingHost[];
  onChange?: (mode: RoutingMode) => void;
  onManageHosts?: () => void;
}
const routingHosts: readonly RoutingHost[] = [
  { id: "maya", name: "Maya Chen", role: "AI operations", availability: "available", weight: 1 },
  { id: "alex", name: "Alex Morgan", role: "Product demos", availability: "busy", weight: 1 },
  { id: "sam", name: "Sam Wilson", role: "Technical support", availability: "available", weight: 1 }
];
export function HostRoutingCard({
  className,
  hosts = routingHosts,
  mode: controlled,
  onChange,
  onManageHosts,
  ...props
}: HostRoutingCardProps) {
  const [internal, setInternal] = useState<RoutingMode>("round-robin");
  const mode = controlled ?? internal;
  const modes: readonly { id: RoutingMode; label: string; detail: string }[] = [
    { id: "individual", label: "Individual", detail: "Always route to one host" },
    { id: "round-robin", label: "Round robin", detail: "Balance meetings across available hosts" },
    { id: "specialist", label: "Specialist", detail: "Route by meeting reason and expertise" }
  ];
  return (
    <FeaturePanel
      className={cn("amro-host-routing", className)}
      eyebrow="Host routing"
      title="Who should receive this meeting?"
      status={<Badge tone="neutral">{hosts.length} hosts</Badge>}
      {...props}
    >
      <div role="radiogroup" aria-label="Routing mode">
        {modes.map((item) => (
          <button
            aria-checked={mode === item.id}
            key={item.id}
            onClick={() => {
              setInternal(item.id);
              onChange?.(item.id);
            }}
            role="radio"
            type="button"
          >
            <span>
              {item.id === "individual" ? (
                <UserRound aria-hidden="true" />
              ) : item.id === "round-robin" ? (
                <Users aria-hidden="true" />
              ) : (
                <Headphones aria-hidden="true" />
              )}
            </span>
            <span>
              <strong>{item.label}</strong>
              <small>{item.detail}</small>
            </span>
          </button>
        ))}
      </div>
      <ul>
        {hosts.map((host) => (
          <li key={host.id}>
            <Avatar name={host.name} />
            <span>
              <strong>{host.name}</strong>
              <small>{host.role}</small>
            </span>
            <StatusIndicator
              label={host.availability ?? "available"}
              status={
                host.availability === "available"
                  ? "online"
                  : host.availability === "busy"
                    ? "warning"
                    : "offline"
              }
            />
          </li>
        ))}
      </ul>
      {onManageHosts && (
        <Button onClick={onManageHosts} variant="secondary">
          Manage routing hosts
        </Button>
      )}
    </FeaturePanel>
  );
}

export interface AgentHandoffContext {
  source: SourceProduct;
  conversationId?: string;
  summary: ReactNode;
  intent: ReactNode;
  facts?: readonly ReactNode[];
  transcriptMessages?: number;
}
export interface AgentToMeetingHandoffProps extends HTMLAttributes<HTMLDivElement> {
  context?: AgentHandoffContext;
  host?: ReactNode;
  event?: MeetingEventType;
  onBook?: (context: AgentHandoffContext) => void;
  onRemoveContext?: () => void;
}
const handoffContext: AgentHandoffContext = {
  source: "agents",
  conversationId: "conv_1842",
  summary:
    "Alex is evaluating governed AI support automation for a 35-person team and wants a practical pilot path.",
  intent: "Assess pilot fit and CRM workflow",
  facts: ["Uses HubSpot", "Human approval is required", "Target launch in Q4"],
  transcriptMessages: 18
};
export function AgentToMeetingHandoff({
  className,
  context = handoffContext,
  event = eventTypes[0]!,
  host = "Maya Chen",
  onBook,
  onRemoveContext,
  ...props
}: AgentToMeetingHandoffProps) {
  return (
    <FeaturePanel
      className={cn("amro-agent-handoff", className)}
      eyebrow="Conversation to meeting"
      title="Carry the useful context forward"
      description="The guest will not need to repeat the conversation."
      status={
        <SourceContextChip
          contextCount={(context.facts?.length ?? 0) + 1}
          source={context.source}
        />
      }
      {...props}
    >
      <section>
        <Bot aria-hidden="true" />
        <span>
          <strong>Conversation summary</strong>
          <p>{context.summary}</p>
          <small>
            {context.transcriptMessages ?? 0} transcript messages · {context.conversationId}
          </small>
        </span>
      </section>
      <div>
        <strong>Meeting intent</strong>
        <p>{context.intent}</p>
        {context.facts?.map((fact, index) => (
          <Badge key={index} tone="neutral">
            {fact}
          </Badge>
        ))}
      </div>
      <footer>
        <span>
          <Avatar name={getAccessibleText(host, "Suggested host")} />
          <span>
            <small>Suggested host and event</small>
            <strong>
              {host} · {event.label}
            </strong>
          </span>
        </span>
        <div>
          {onRemoveContext && (
            <Button onClick={onRemoveContext} variant="tertiary">
              Book without context
            </Button>
          )}
          {onBook && (
            <Button onClick={() => onBook(context)}>
              Continue to times
              <CalendarDays aria-hidden="true" />
            </Button>
          )}
        </div>
      </footer>
    </FeaturePanel>
  );
}
```



## Usage

Shared product source installed automatically by AmroUI component entries.

