/* Settings, the rest of it. Six screens, and each states its own commit model
   at the top of its function, because they are not all the same:

   · /settings/channels      INSTANT — connecting or reconnecting applies at once;
                             disconnecting is irreversible for the token, so it
                             opens a confirm dialog.
   · /settings/sending       INSTANT — a re-check is an action, not a form.
   · /settings/notifications INSTANT — every row is a Switch, so it applies as it
                             is flipped, with an undo toast.
   · /settings/billing       READ, plus two actions that open dialogs.
   · /settings/api           INSTANT — creating a key shows it once; revoking is
                             irreversible and confirms.
   · /me/profile             STAGED — a form with Save and Cancel; nothing applies
                             until Save.

   Every figure names its array, and the two that quote the plan read
   MD_WORKSPACE.contacts and BILLING.sendsThisPeriod rather than a second copy. */
(() => {
  const {
    Table, TableHeader, TableBody, TableRow, TableHead, TableCell,
    Field, FieldLabel, FieldDescription, Input, Select, Switch, Avatar, Badge, Button, Alert, AlertTitle, AlertDescription, Separator,
  } = window.OriginDesignSystem_7c998b;

  /* ── /settings/channels ─────────────────────────────────────────────────── */
  function ChannelsSettingsScreen({ data, now, onChannelAction }) {
    const channels = data.channels;
    const connected = channels.filter((c) => c.state === "connected");
    const attention = channels.filter((c) => c.state === "needs_reauth");
    const available = channels.filter((c) => c.state === "available");

    return (
      <>
        <PageHeader path="/settings/channels" title="Connected channels" />
        <Tiles>
          <Tile span="5" tone="hero" title="What this workspace is plugged into"
            note="Delivery, contact sources, notifications and exports. A channel that needs reconnecting has stopped doing its job — it is not a warning about the future.">
            <StatRow>
              <StatTile label="Working" value={formatCount(connected.length)} qualifier={connected.map((c) => c.name).join(", ")} />
              <StatTile label="Needs you" value={formatCount(attention.length)} tone={attention.length ? "warning" : undefined}
                qualifier={attention.length ? attention.map((c) => c.name + " — " + c.detail).join("; ") : "Every connected channel is authenticating"} />
            </StatRow>
            <PillMeter label={connected.length + " of " + (channels.length - available.length) + " connected channels healthy"}
              parts={[
                { key: "ok", percent: (connected.length / (channels.length - available.length)) * 100, tone: "success" },
                { key: "bad", percent: (attention.length / (channels.length - available.length)) * 100, tone: "warning" },
              ]} />
          </Tile>

          <Tile span="7" title="Connected" note="Who connected it and when. Disconnecting revokes the token — the connection has to be made again from scratch.">
            <div className="md-queue">
              {connected.concat(attention).map((channel) => (
                <div className="md-queue__row" key={channel.id}>
                  <div className="md-queue__main">
                    <span className="md-person__name">{channel.name}</span>
                    <span className="md-footnote">{channel.kind} · {channel.detail}</span>
                    <span className="md-footnote">Connected by {memberName(data.members, channel.byId)} {formatWhen(channel.connectedAt, now)}</span>
                  </div>
                  <div className="md-queue__side">
                    <StatusBadge values={CHANNEL_STATE} value={channel.state} />
                    {channel.state === "needs_reauth"
                      ? <Button size="sm" onClick={() => onChannelAction(channel, "reconnect")}>Reconnect</Button>
                      : null}
                    <Button variant="destructive-outline" size="sm" onClick={() => onChannelAction(channel, "disconnect")}>Disconnect</Button>
                  </div>
                </div>
              ))}
            </div>
          </Tile>

          <Tile span="12" tone="muted" title="Available" note="Not connected. Nothing changes in the product until one is.">
            <div className="md-queue">
              {available.map((channel) => (
                <div className="md-queue__row" key={channel.id}>
                  <div className="md-queue__main">
                    <span className="md-person__name">{channel.name}</span>
                    <span className="md-footnote">{channel.kind} · {channel.detail}</span>
                  </div>
                  <div className="md-queue__side">
                    <Button variant="outline" size="sm" onClick={() => onChannelAction(channel, "connect")}>Connect</Button>
                  </div>
                </div>
              ))}
            </div>
          </Tile>
        </Tiles>
      </>
    );
  }

  /* ── /settings/sending ──────────────────────────────────────────────────── */
  function SendingSettingsScreen({ data, workspace, now, onNavigate, onRecheck, onAddDomain }) {
    const domains = data.domains;
    const ok = authenticatedDomains(domains);
    const failing = domains.filter((d) => !domainIsAuthenticated(d));

    return (
      <>
        <PageHeader path="/settings/sending" title="Sending domains" actions={
          <Button size="sm" onClick={onAddDomain}><Icon name="plus" size={14} />Add sending domain</Button>
        } />
        <Tiles>
          <Tile span="5" tone="hero" title="Authentication"
            note="A domain is authenticated when SPF, DKIM and DMARC all pass — derived from the three checks, never stored as a state.">
            <StatRow>
              <StatTile label="Fully authenticated" value={ok.length + " of " + domains.length}
                qualifier={ok.length ? ok.map((d) => d.domain).join(", ") : "None of the three domains passes all three checks"} />
              <StatTile label="Default sending domain" value={domainName(domains, workspace.sendingDomainId)}
                qualifier="Everything sends from here unless a broadcast names another" />
            </StatRow>
            <Gauge percent={(ok.length / domains.length) * 100} value={ok.length + "/" + domains.length} label="domains passing"
              tone={failing.length ? "warning" : "success"}
              note="Each failing domain is a real delivery risk, not a score." />
          </Tile>

          <Tile span="7" flush title="The domains" note="The full matrix, with per-check detail, is on Deliverability.">
            <div className="md-tablewrap">
              <Table>
                <TableHeader>
                  <TableRow>
                    <TableHead>Domain</TableHead>
                    <TableHead>SPF</TableHead>
                    <TableHead>DKIM</TableHead>
                    <TableHead>DMARC</TableHead>
                    <TableHead>Checked</TableHead>
                    <TableHead />
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {domains.map((domain) => (
                    <TableRow key={domain.id}>
                      <TableCell>
                        <span className="md-person__name">{domain.domain}</span>
                        <span className="md-footnote">{domain.purpose}</span>
                      </TableCell>
                      {["spf", "dkim", "dmarc"].map((check) => (
                        <TableCell key={check}><StatusBadge values={DOMAIN_CHECKS} value={domain[check]} /></TableCell>
                      ))}
                      <TableCell><span className="md-muted" title={formatAbsolute(domain.checkedAt)}>{formatWhen(domain.checkedAt, now)}</span></TableCell>
                      <TableCell><Button variant="outline" size="sm" onClick={() => onRecheck(domain)}>Re-check</Button></TableCell>
                    </TableRow>
                  ))}
                </TableBody>
              </Table>
            </div>
            <div className="md-tile__foot">
              <span>Failure sources, by record, live on Deliverability.</span>
              <Button variant="ghost" size="sm" onClick={() => onNavigate("/deliverability")}>Open deliverability<Icon name="arrow-right" size={14} /></Button>
            </div>
          </Tile>
        </Tiles>
      </>
    );
  }

  /* ── /settings/notifications ────────────────────────────────────────────── */
  function NotificationsSettingsScreen({ data, prefs, viewer, onToggle, onQuietHours }) {
    const on = (channel) => prefs.rows.filter((row) => row[channel]).length;
    const slackChannel = data.channels.find((c) => c.id === "chn_slack");

    return (
      <>
        <PageHeader path="/settings/notifications" title="Notifications" />
        <Tiles>
          <Tile span="5" tone="hero" distribute="spread" title={"What " + viewer.name.split(" ")[0] + " is told about"}
            note="Yours alone — every person in the workspace has their own set. Each switch applies as you flip it; there is no Save on this screen."
            foot={
              <>
                <span>Quiet hours are {prefs.quietHours.from}–{prefs.quietHours.to} in {viewer.timezone}.</span>
                <Button variant="outline" size="sm" onClick={onQuietHours}>Change quiet hours</Button>
              </>
            }>
            <StatRow>
              <StatTile label="By email" value={on("email") + " of " + prefs.rows.length} qualifier={prefs.rows.filter((r) => r.email).map((r) => r.label).slice(0, 2).join("; ") + (on("email") > 2 ? " and more" : "")} />
              <StatTile label="In Slack" value={on("slack") + " of " + prefs.rows.length} qualifier={slackChannel ? "Posted to " + slackChannel.detail.split(" — ")[0] : "Slack is not connected"} />
              <StatTile label="In the app" value={on("inApp") + " of " + prefs.rows.length} qualifier="Shown in the bell in the pane header" />
            </StatRow>
            <KeyValues rows={[
              { label: "Events you are told about", value: prefs.rows.filter((r) => r.email || r.slack || r.inApp).length + " of " + prefs.rows.length },
              { label: "Events you hear nothing about", value: String(prefs.rows.filter((r) => !r.email && !r.slack && !r.inApp).length) },
              { label: "Timezone they follow", value: viewer.timezone },
            ]} />
          </Tile>

          <Tile span="7" flush title="Per event" note="Email and Slack are sent out of the product; in-app is the bell. A failed send always reaches you somewhere — that row cannot be turned off everywhere.">
            <div className="md-tablewrap">
              <Table>
                <TableHeader>
                  <TableRow>
                    <TableHead>Event</TableHead>
                    <TableHead>Email</TableHead>
                    <TableHead>Slack</TableHead>
                    <TableHead>In app</TableHead>
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {prefs.rows.map((row) => {
                    const last = row.id === "send_failed" && [row.email, row.slack, row.inApp].filter(Boolean).length === 1;
                    return (
                      <TableRow key={row.id}>
                        <TableCell>{row.label}</TableCell>
                        {["email", "slack", "inApp"].map((channel) => {
                          const only = last && row[channel];
                          const control = (
                            <Switch checked={row[channel]} disabled={only || (channel === "slack" && !slackChannel)}
                              onCheckedChange={(value) => onToggle(row, channel, value)} />
                          );
                          return (
                            <TableCell key={channel}>
                              {only
                                ? <FloatingTip side="top" label="A failed send has to reach you somewhere — turn another channel on first.">{control}</FloatingTip>
                                : channel === "slack" && !slackChannel
                                  ? <FloatingTip side="top" label="Slack is not connected to this workspace.">{control}</FloatingTip>
                                  : control}
                            </TableCell>
                          );
                        })}
                      </TableRow>
                    );
                  })}
                </TableBody>
              </Table>
            </div>
          </Tile>
        </Tiles>
      </>
    );
  }

  /* ── /settings/billing ─────────────────────────────────────────────────── */
  function BillingScreen({ data, workspace, billing, now, onChangePlan, onUpdateCard }) {
    const contactUse = rate(workspace.contacts, billing.contactLimit);
    const sendUse = rate(billing.sendsThisPeriod, billing.sendLimit);
    const seatsUsed = data.members.length;

    return (
      <>
        <PageHeader path="/settings/billing" title="Billing and plan" actions={
          <Button size="sm" onClick={onChangePlan}>Change plan</Button>
        } />
        <Tiles>
          <Tile span="5" tone="hero" title={billing.plan + " · " + formatMoney(billing.priceMinor) + " a month"}
            note={"Renews " + formatAbsolute(billing.renewsAt).split(",")[0] + ". The contact figure below is the workspace's own count, not a second number."}>
            <StatRow>
              <StatTile label="Contacts" value={formatCount(workspace.contacts)} tone={contactUse > 0.9 ? "warning" : undefined}
                qualifier={formatPercent(contactUse) + " of the " + formatCount(billing.contactLimit) + " this plan includes"} />
              <StatTile label="Sends this period" value={formatCount(billing.sendsThisPeriod)}
                qualifier={formatPercent(sendUse) + " of " + formatCount(billing.sendLimit) + " · period began " + formatWhen(billing.periodStartAt, now)} />
            </StatRow>
            <PillMeter label="Contacts against the plan"
              parts={[{ key: "used", percent: contactUse * 100, tone: contactUse > 0.9 ? "warning" : "success" }]}
              foot={formatCount(billing.contactLimit - workspace.contacts) + " contacts of headroom before the plan has to change."} />
            <PillMeter label="Sends against the plan"
              parts={[{ key: "used", percent: sendUse * 100 }]}
              foot={formatCount(billing.sendLimit - billing.sendsThisPeriod) + " sends left in this period."} />
          </Tile>

          <Tile span="4" distribute="spread" title="Seats"
            note={"This plan includes " + billing.seatsIncluded + ". Seats are people in the workspace, so the count is the People directory."}
            foot={
              <>
                <span>{seatsUsed} of {billing.seatsIncluded} taken</span>
                <FaceStack people={data.members} max={6} size={26} />
              </>
            }>
            <div className="md-donutrow">
              <Donut slices={[{ key: "used", value: seatsUsed }, { key: "free", value: billing.seatsIncluded - seatsUsed }]}
                centreValue={seatsUsed + "/" + billing.seatsIncluded} centreLabel="seats used" />
              <DonutKey slices={[
                { key: "used", label: "In use", display: String(seatsUsed) },
                { key: "free", label: "Unused", display: String(billing.seatsIncluded - seatsUsed) },
              ]} />
            </div>
          </Tile>

          <Tile span="3" distribute="spread" title="Payment" note="The card the renewal will be taken from."
            foot={
              <>
                <span>Charged automatically.</span>
                <Button variant="outline" size="sm" onClick={onUpdateCard}>Update card</Button>
              </>
            }>
            <KeyValues rows={[
              { label: "Card", value: billing.card.brand + " ···· " + billing.card.last4 },
              { label: "Expires", value: billing.card.expiry },
              { label: "Next charge", value: formatMoney(billing.priceMinor) },
              { label: "On", value: formatAbsolute(billing.renewsAt).split(",")[0] },
            ]} />
          </Tile>

          <Tile span="12" flush title="Invoices" note="Three months of them; older ones are in the email receipts.">
            <div className="md-tablewrap">
              <Table>
                <TableHeader>
                  <TableRow>
                    <TableHead>Invoice</TableHead>
                    <TableHead>Date</TableHead>
                    <TableHead>Amount</TableHead>
                    <TableHead>State</TableHead>
                    <TableHead />
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {billing.invoices.map((invoice) => (
                    <TableRow key={invoice.id}>
                      <TableCell>{invoice.id.replace("inv_", "").replace("_", "/")}</TableCell>
                      <TableCell>{formatAbsolute(invoice.at).split(",")[0]}</TableCell>
                      <TableCell>{formatMoney(invoice.amountMinor)}</TableCell>
                      <TableCell><StatusBadge values={INVOICE_STATE} value={invoice.state} /></TableCell>
                      <TableCell><Button variant="outline" size="sm"><Icon name="download" size={14} />Download</Button></TableCell>
                    </TableRow>
                  ))}
                </TableBody>
              </Table>
            </div>
          </Tile>
        </Tiles>
      </>
    );
  }

  /* ── /settings/api ─────────────────────────────────────────────────────── */
  function ApiSettingsScreen({ data, now, onCreateKey, onRevokeKey }) {
    const keys = data.keys;
    const hooks = data.webhooks;
    const stale = keys.filter((k) => now - k.lastUsedAt > 90 * 864e5);
    const failing = hooks.filter((h) => h.state === "failing");

    return (
      <>
        <PageHeader path="/settings/api" title="API keys and webhooks" actions={
          <Button size="sm" onClick={onCreateKey}><Icon name="plus" size={14} />New key</Button>
        } />
        <Tiles>
          <Tile span="4" tone="hero" title="Keys" note="A key is shown once, when it is made. Revoking one is immediate and cannot be undone.">
            <StatRow>
              <StatTile label="Live keys" value={formatCount(keys.length)} qualifier={keys.filter((k) => k.scope === "write").length + " can write · " + keys.filter((k) => k.scope === "read").length + " read only"} />
              <StatTile label="Unused for 90 days" value={formatCount(stale.length)} tone={stale.length ? "warning" : undefined}
                qualifier={stale.length ? stale.map((k) => k.label).join(", ") : "Every key has been used this quarter"} />
            </StatRow>
          </Tile>

          <Tile span="8" flush title="The keys">
            <div className="md-tablewrap">
              <Table>
                <TableHeader>
                  <TableRow>
                    <TableHead>Key</TableHead>
                    <TableHead>Scope</TableHead>
                    <TableHead>Created</TableHead>
                    <TableHead>Last used</TableHead>
                    <TableHead />
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {keys.map((key) => (
                    <TableRow key={key.id}>
                      <TableCell>
                        <span className="md-person__name">{key.label}</span>
                        <span className="md-footnote"><code>{key.prefix}···</code> · made by {memberName(data.members, key.byId)}</span>
                      </TableCell>
                      <TableCell><Badge size="sm" variant={key.scope === "write" ? "warning" : "secondary"}>{labelOf(KEY_SCOPES, key.scope)}</Badge></TableCell>
                      <TableCell><span className="md-muted">{formatWhen(key.createdAt, now)}</span></TableCell>
                      <TableCell>
                        <span className={now - key.lastUsedAt > 90 * 864e5 ? "md-clock md-clock--warning" : "md-muted"} title={formatAbsolute(key.lastUsedAt)}>
                          {formatWhen(key.lastUsedAt, now)}
                        </span>
                      </TableCell>
                      <TableCell><Button variant="destructive-outline" size="sm" onClick={() => onRevokeKey(key)}>Revoke</Button></TableCell>
                    </TableRow>
                  ))}
                </TableBody>
              </Table>
            </div>
          </Tile>

          <Tile span="12" title="Webhooks" note="Where this workspace pushes events. A failing endpoint is dropping them on the floor right now.">
            <div className="md-queue">
              {hooks.map((hook) => (
                <div className="md-queue__row" key={hook.id}>
                  <div className="md-queue__main">
                    <span className="md-person__name"><code>{hook.url}</code></span>
                    <span className="md-footnote">{hook.events.join(", ")}{hook.detail ? " · " + hook.detail : ""}</span>
                  </div>
                  <div className="md-queue__side">
                    <StatusBadge values={WEBHOOK_STATE} value={hook.state} />
                    <span className="md-muted md-footnote" title={formatAbsolute(hook.lastAt)}>last {formatWhen(hook.lastAt, now)}</span>
                  </div>
                </div>
              ))}
            </div>
          </Tile>
        </Tiles>
      </>
    );
  }

  /* ── /me/profile — STAGED ──────────────────────────────────────────────── */
  function MyProfileScreen({ data, viewer, now, prefs, onSave, onNavigate }) {
    const initial = { name: viewer.name, role: viewer.role, focus: viewer.focus, timezone: viewer.timezone, location: viewer.location, avatar: viewer.avatar };
    const [draft, setDraft] = React.useState(initial);
    const [photoError, setPhotoError] = React.useState(null);
    const fileRef = React.useRef(null);
    const dirty = Object.keys(initial).some((key) => draft[key] !== initial[key]);
    const valid = draft.name.trim().length > 1 && draft.role.trim().length > 1;
    const load = personLoad(data, viewer.id, now);
    const set = (key, value) => setDraft((d) => Object.assign({}, d, { [key]: value }));

    /* The photo stages like every other field on this screen: choosing one only
       changes the preview, and nothing reaches the workspace record until Save.
       Read as a data URL so the staged value is the image itself — an object URL
       would be revoked out from under the draft if the user cancelled. */
    const readPhoto = (file) => {
      setPhotoError(null);
      if (!file) return;
      if (!/^image\//.test(file.type)) { setPhotoError("That is not an image. Use a JPEG, PNG or WebP."); return; }
      if (file.size > 2 * 1024 * 1024) { setPhotoError("That file is " + Math.round(file.size / 1024 / 102.4) / 10 + "MB. The limit is 2MB — a square around 400px is plenty."); return; }
      const reader = new FileReader();
      reader.onload = () => set("avatar", reader.result);
      reader.onerror = () => setPhotoError("That file could not be read. Try another.");
      reader.readAsDataURL(file);
    };

    return (
      <>
        <PageHeader path="/me/profile" title="Your profile" actions={
          <Button variant="outline" size="sm" onClick={() => onNavigate("/people/" + viewer.id)}>View as the team sees it<Icon name="arrow-right" size={14} /></Button>
        } />
        <Tiles>
          <Tile span="7" title="How you appear to the team" note="Saved to your workspace record, so it changes every note, face stack and profile at once.">
            <div className="md-hero">
              <span className="md-photo">
                <Avatar className="md-hero__avatar" size={72} src={draft.avatar} alt="" fallback={initialsOf(draft.name || viewer.name)} />
                <button type="button" className="md-photo__btn" onClick={() => fileRef.current && fileRef.current.click()}
                  aria-label={draft.avatar ? "Replace your profile photo" : "Add a profile photo"}>
                  <Icon name="camera" size={14} />
                </button>
              </span>
              <div className="md-hero__text">
                <span className="md-hero__name">{draft.name || viewer.name}</span>
                <span className="md-hero__line">{draft.role} · {draft.location}</span>
                <div className="md-chips">
                  <input ref={fileRef} type="file" accept="image/*" hidden
                    onChange={(e) => { readPhoto(e.target.files && e.target.files[0]); e.target.value = ""; }} />
                  <Button variant="outline" size="sm" onClick={() => fileRef.current && fileRef.current.click()}>
                    <Icon name="upload" size={14} />{draft.avatar ? "Replace photo" : "Upload photo"}
                  </Button>
                  {draft.avatar ? (
                    <Button variant="ghost" size="sm" onClick={() => { set("avatar", null); setPhotoError(null); }}>Remove</Button>
                  ) : null}
                  {draft.avatar !== initial.avatar ? (
                    <Button variant="ghost" size="sm" onClick={() => { set("avatar", initial.avatar); setPhotoError(null); }}>Undo change</Button>
                  ) : null}
                </div>
                <span className={photoError ? "md-error" : "md-footnote"}>
                  {photoError
                    || (draft.avatar === initial.avatar
                      ? (draft.avatar ? "Square images read best — this is shown at 26px on a note and 92px on your profile." : "With no photo your initials are used instead.")
                      : (draft.avatar ? "New photo staged. It replaces your face everywhere when you save." : "Photo will be removed on save; your initials are used instead."))}
                </span>
              </div>
            </div>
            <div className="md-fields">
              <Field>
                <FieldLabel htmlFor="md-me-name">Name</FieldLabel>
                <Input id="md-me-name" value={draft.name} onChange={(e) => set("name", e.target.value)} />
                <FieldDescription>Shown on every note you write and in the account menu.</FieldDescription>
              </Field>
              <Field>
                <FieldLabel htmlFor="md-me-role">Role</FieldLabel>
                <Input id="md-me-role" value={draft.role} placeholder="Lifecycle marketing lead" onChange={(e) => set("role", e.target.value)} />
                <FieldDescription>What you do — not what you are entitled to do. Your entitlement is {labelOf(TEAM_ROLES, viewer.teamRole)}, changed on Team and roles.</FieldDescription>
              </Field>
              <Field>
                <FieldLabel htmlFor="md-me-focus">What you are answerable for</FieldLabel>
                <Input id="md-me-focus" value={draft.focus} onChange={(e) => set("focus", e.target.value)} />
                <FieldDescription>One line. It sits under your name on your profile.</FieldDescription>
              </Field>
              <Field>
                <FieldLabel htmlFor="md-me-location">Where you are</FieldLabel>
                <Input id="md-me-location" value={draft.location} onChange={(e) => set("location", e.target.value)} />
              </Field>
              <Field>
                <FieldLabel>Timezone</FieldLabel>
                <Select items={TIMEZONES} value={draft.timezone} aria-label="Timezone" onValueChange={(v) => set("timezone", v)} />
                <FieldDescription>Every clock in the product, and your quiet hours, follow this.</FieldDescription>
              </Field>
            </div>
          </Tile>

          <Tile span="5" title="What you are carrying" note="Read off the records that name you — the same figures your profile shows the team.">
            <StatRow>
              <StatTile label="Journeys" value={formatCount(load.journeys.length)} qualifier={load.liveJourneys.length + " of them live"} onClick={() => onNavigate("/journeys")} />
              <StatTile label="Broadcasts" value={formatCount(load.broadcasts.length)} qualifier={load.submitted.length + " in review, submitted by you"} onClick={() => onNavigate("/broadcasts")} />
              <StatTile label="Owed replies" value={formatCount(load.owed.length)} tone={load.owed.length ? "warning" : undefined}
                qualifier={load.owed.length ? "Mentions of you with no answer since" : "Nothing is waiting on you"} onClick={() => onNavigate("/inbox")} />
            </StatRow>
            <Separator />
            <KeyValues rows={[
              { label: "Entitlement", value: labelOf(TEAM_ROLES, viewer.teamRole) },
              { label: "Email", value: viewer.email },
              { label: "Joined", value: formatAbsolute(viewer.joinedAt).split(",")[0] },
              { label: "Notifications on", value: prefs.rows.filter((r) => r.email || r.slack || r.inApp).length + " of " + prefs.rows.length + " events" },
            ]} />
            <Button variant="outline" size="sm" onClick={() => onNavigate("/settings/notifications")}>Notification settings</Button>
          </Tile>
        </Tiles>

        <FormBar dirty={dirty} label="Save profile"
          note={dirty
            ? (valid ? "Saving changes how you appear on every note, panel and menu." : "A name and a role are needed.")
            : "Nothing has changed yet."}
          onCancel={() => setDraft(initial)}
          onSave={() => { if (valid) onSave(draft); }} />
      </>
    );
  }

  Object.assign(window, { ChannelsSettingsScreen, SendingSettingsScreen, NotificationsSettingsScreen, BillingScreen, ApiSettingsScreen, MyProfileScreen });
})();
