handoff ui improvement

This commit is contained in:
debu-2 2026-10-06 21:22:41 +05:30
parent f7a4753217
commit d4bb4df40d
4 changed files with 151 additions and 126 deletions

View file

@ -1,4 +1,4 @@
import { ArrowDown, ArrowUp, MoreHorizontal, Plus, UserRound } from "lucide-react"; import { MoreHorizontal, UserRound } from "lucide-react";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { import {
createHandoffRule, createHandoffRule,
@ -10,13 +10,13 @@ import {
} from "../api"; } from "../api";
import AgentSpark from "./AgentSpark"; import AgentSpark from "./AgentSpark";
import HumanHandoffDrawer from "./HumanHandoffDrawer"; import HumanHandoffDrawer from "./HumanHandoffDrawer";
import { buildRuleCardChips, EMAIL_TYPE_OPTIONS } from "../utils/handoffUtils"; import { buildRuleCardChips } from "../utils/handoffUtils";
function typeLabel(id) { function ruleScope(rule) {
return EMAIL_TYPE_OPTIONS.find((item) => item.id === id)?.title || id; return buildRuleCardChips(rule).filter((chip) => chip !== rule.name).join(" · ");
} }
export default function HandoffSettings() { export default function HandoffSettings({ createToken = 0 }) {
const token = sessionStorage.getItem("agmail_access_token"); const token = sessionStorage.getItem("agmail_access_token");
const [rules, setRules] = useState([]); const [rules, setRules] = useState([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@ -50,10 +50,11 @@ export default function HandoffSettings() {
loadRules(); loadRules();
}, [loadRules]); }, [loadRules]);
function openCreate() { useEffect(() => {
if (!createToken) return;
setEditingRule(null); setEditingRule(null);
setDrawerOpen(true); setDrawerOpen(true);
} }, [createToken]);
function openEdit(rule) { function openEdit(rule) {
setEditingRule(rule); setEditingRule(rule);
@ -145,10 +146,9 @@ export default function HandoffSettings() {
return ( return (
<div className="handoff-settings"> <div className="handoff-settings">
<div className="handoff-settings-toolbar"> <div className="handoff-section-head">
<button type="button" className="handoff-btn primary" onClick={openCreate}> <h2>Handoff rules</h2>
<Plus size={16} strokeWidth={1.75} /> Add handoff rule <p>Choose who can take over conversations from your Agent.</p>
</button>
</div> </div>
{error && <p className="form-error" role="alert">{error}</p>} {error && <p className="form-error" role="alert">{error}</p>}
@ -159,48 +159,32 @@ export default function HandoffSettings() {
) : rules.length === 0 ? ( ) : rules.length === 0 ? (
<div className="human-handoff-empty"> <div className="human-handoff-empty">
<div className="human-handoff-empty-icon" aria-hidden="true"> <div className="human-handoff-empty-icon" aria-hidden="true">
<UserRound size={24} strokeWidth={1.75} /> <UserRound size={20} strokeWidth={1.75} />
</div> </div>
<h2>No handoff rules yet</h2> <h2>No handoff rules yet</h2>
<p>Configure when your Agent should involve a Human Owner.</p> <p>Add a human owner so your Agent knows who can take over a conversation.</p>
<button type="button" className="handoff-btn primary" onClick={openCreate}>
<Plus size={16} strokeWidth={1.75} /> Add handoff rule
</button>
</div> </div>
) : ( ) : (
<div className="human-handoff-list"> <div className="human-handoff-list">
{rules.map((rule) => ( {rules.map((rule, index) => (
<article key={rule.id} className="human-handoff-card"> <article key={rule.id} className="human-handoff-card">
<div className="human-handoff-card-body"> <div className="human-handoff-card-body">
<span className="human-handoff-card-avatar" aria-hidden="true"> <span className="human-handoff-card-avatar" aria-hidden="true">
<UserRound size={17} strokeWidth={1.75} /> <UserRound size={15} strokeWidth={1.75} />
</span> </span>
<div className="human-handoff-card-identity">
<strong className="human-handoff-card-name">{rule.name}</strong> <strong className="human-handoff-card-name">{rule.name}</strong>
<small className="human-handoff-card-email">
{index === 0 ? "Primary human owner" : "Human owner"}
{" · "}
{rule.owner_email}
</small>
</div>
<p className="human-handoff-card-scope">{ruleScope(rule)}</p>
<div className="human-handoff-card-actions">
<span className={`human-handoff-status${rule.active ? " active" : ""}`}> <span className={`human-handoff-status${rule.active ? " active" : ""}`}>
{rule.active ? "Active" : "Paused"} {rule.active ? "Active" : "Paused"}
</span> </span>
<small className="human-handoff-card-email">
Priority {rule.priority} · {rule.owner_name} · {rule.owner_email}
</small>
<div className="human-handoff-card-actions">
<button
type="button"
className="handoff-btn icon quiet"
aria-label="Move rule up"
disabled={rules[0]?.id === rule.id}
onClick={() => moveRule(rule.id, -1)}
>
<ArrowUp size={15} strokeWidth={1.75} />
</button>
<button
type="button"
className="handoff-btn icon quiet"
aria-label="Move rule down"
disabled={rules[rules.length - 1]?.id === rule.id}
onClick={() => moveRule(rule.id, 1)}
>
<ArrowDown size={15} strokeWidth={1.75} />
</button>
<button type="button" className="handoff-btn subtle" onClick={() => openEdit(rule)}> <button type="button" className="handoff-btn subtle" onClick={() => openEdit(rule)}>
Edit Edit
</button> </button>
@ -215,6 +199,8 @@ export default function HandoffSettings() {
</button> </button>
{menuOpenId === rule.id && ( {menuOpenId === rule.id && (
<div className="handoff-card-menu"> <div className="handoff-card-menu">
<button type="button" disabled={index === 0} onClick={() => moveRule(rule.id, -1)}>Move up</button>
<button type="button" disabled={index === rules.length - 1} onClick={() => moveRule(rule.id, 1)}>Move down</button>
<button type="button" onClick={() => openTest({ <button type="button" onClick={() => openTest({
owner_email: rule.owner_email, owner_email: rule.owner_email,
labels: rule.labels, labels: rule.labels,
@ -227,11 +213,6 @@ export default function HandoffSettings() {
</div> </div>
</div> </div>
</div> </div>
<div className="human-handoff-card-chips">
{buildRuleCardChips(rule).map((chip) => (
<span key={chip} className="handoff-meta-chip">{chip}</span>
))}
</div>
</article> </article>
))} ))}
</div> </div>

View file

@ -1,3 +1,4 @@
import { ChevronDown, Plus } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import HandoffConversation from "./HandoffConversation"; import HandoffConversation from "./HandoffConversation";
import HandoffInbox from "./HandoffInbox"; import HandoffInbox from "./HandoffInbox";
@ -6,36 +7,65 @@ import HandoffSettings from "./HandoffSettings";
export default function HumanHandoff({ onOpenExternal }) { export default function HumanHandoff({ onOpenExternal }) {
const [tab, setTab] = useState("inbox"); const [tab, setTab] = useState("inbox");
const [selectedHandoffId, setSelectedHandoffId] = useState(null); const [selectedHandoffId, setSelectedHandoffId] = useState(null);
const [howOpen, setHowOpen] = useState(false);
const [createToken, setCreateToken] = useState(0);
function selectTab(value) { function selectTab(value) {
setTab(value); setTab(value);
setSelectedHandoffId(null); setSelectedHandoffId(null);
} }
function addRule() {
setTab("rules");
setSelectedHandoffId(null);
setCreateToken((token) => token + 1);
}
return ( return (
<section className="human-handoff-page"> <section className="human-handoff-page">
<header className="human-handoff-head"> <header className="human-handoff-head">
<div>
<h1 className="handoff-page-title">Human Handoff</h1> <h1 className="handoff-page-title">Human Handoff</h1>
<p className="handoff-page-lead"> <p className="handoff-page-lead">
Manage conversations that your Agent has handed over to a Human Owner. When a customer emails your Gentic address, your Agent can hand that conversation to a person.
The person replies from their own inbox. The customer still only sees mail from Gentic.
</p> </p>
<div className="handoff-start-help"> <button
<strong>Start the first email yourself</strong> type="button"
className={`handoff-how-toggle${howOpen ? " open" : ""}`}
aria-expanded={howOpen}
onClick={() => setHowOpen((open) => !open)}
>
How it works
<ChevronDown size={14} strokeWidth={1.75} />
</button>
{howOpen && (
<div className="handoff-how">
<section>
<h2>When a customer writes to you</h2>
<p> <p>
From the human mailbox, send a new email to your Gentic address. A customer emails your Gentic address. If one of your rules matches, Gentic sends that conversation to the human owner.
Put the word handoff first. Then add the customer’s name, their email address, and the subject they should see. The owner replies to that email. Gentic then sends the reply to the customer as a normal message from your Gentic address.
Separate those four parts with a dot · , a hyphen - , or a bar |. The customer never sees the human’s own email address.
</p>
</section>
<section>
<h2>When you write first</h2>
<p>
Only the human owner on an active rule can start a conversation. From that mailbox, send a new email to your Gentic address.
Put handoff first, then the customer’s name, their email, and the subject they should see.
Separate the parts with a middle dot (·), a hyphen (-), or a bar (|).
</p> </p>
<p className="handoff-start-example">handoff · Suyash Dixit · suyashdixit091@gmail.com · hello</p> <p className="handoff-start-example">handoff · Suyash Dixit · suyashdixit091@gmail.com · hello</p>
<p> <p>
Gentic sends that as a normal first email. The customer only sees “hello”, from your Gentic address. Gentic sends a normal first email. The customer only sees “hello”, from your Gentic address.
Your From address must be the human email saved on an active handoff rule. When they reply, it comes back to the human owner the same way as any other handoff.
When the customer replies to Gentic, that reply is mirrored back to you the same way as any other handoff.
</p> </p>
</section>
</div> </div>
</div> )}
</header> </header>
<div className="handoff-toolbar-row">
<nav className="human-handoff-tabs" aria-label="Human Handoff"> <nav className="human-handoff-tabs" aria-label="Human Handoff">
<button <button
type="button" type="button"
@ -59,8 +89,13 @@ export default function HumanHandoff({ onOpenExternal }) {
Rules Rules
</button> </button>
</nav> </nav>
<button type="button" className="handoff-btn primary" onClick={addRule}>
<Plus size={16} strokeWidth={1.75} /> Add handoff rule
</button>
</div>
{tab === "rules" ? ( {tab === "rules" ? (
<HandoffSettings /> <HandoffSettings createToken={createToken} />
) : selectedHandoffId ? ( ) : selectedHandoffId ? (
<HandoffConversation <HandoffConversation
handoffId={selectedHandoffId} handoffId={selectedHandoffId}

View file

@ -198,12 +198,14 @@
.human-handoff-page { max-width:none; margin:0; padding:0; } .human-handoff-page { max-width:none; margin:0; padding:0; }
.handoff-page-title, .handoff-page-title,
.human-handoff-head h1.handoff-page-title { white-space:normal; font-size:22px; } .human-handoff-head h1.handoff-page-title { white-space:normal; font-size:22px; }
.handoff-toolbar-row { align-items:stretch; flex-direction:column; }
.handoff-toolbar-row .handoff-btn.primary { align-self:flex-start; }
.human-handoff-card-body { .human-handoff-card-body {
grid-template-areas:"avatar name status" "avatar email email" "actions actions actions"; grid-template-areas:"avatar identity" "scope scope" "actions actions";
grid-template-columns:36px minmax(0,1fr) auto; grid-template-columns:32px minmax(0,1fr);
row-gap:8px;
} }
.human-handoff-card-actions { margin-top:8px; flex-wrap:wrap; justify-content:flex-end; } .human-handoff-card-actions { justify-self:start; }
.human-handoff-card-chips { padding-left:0; }
.handoff-drawer,.handoff-test-modal,.people-drawer { width:100%; max-width:100%; } .handoff-drawer,.handoff-test-modal,.people-drawer { width:100%; max-width:100%; }
.mailbox-workspace { gap:0; } .mailbox-workspace { gap:0; }

View file

@ -533,7 +533,7 @@ h1 em { color:var(--coral); font-style:normal; font-weight:500; }
.security-toggle span { position:absolute; inset:0; border-radius:999px; background:#D9D5E0; transition:background .2s; cursor:pointer; } .security-toggle span { position:absolute; inset:0; border-radius:999px; background:#D9D5E0; transition:background .2s; cursor:pointer; }
.security-toggle span::after { content:""; position:absolute; top:3px; left:3px; width:18px; height:18px; border-radius:50%; background:#fff; box-shadow:0 1px 3px rgba(20,18,30,0.18); transition:transform .2s; } .security-toggle span::after { content:""; position:absolute; top:3px; left:3px; width:18px; height:18px; border-radius:50%; background:#fff; box-shadow:0 1px 3px rgba(20,18,30,0.18); transition:transform .2s; }
.security-toggle input:checked + span { background:#7357F6; } .security-toggle input:checked + span { background:#7357F6; }
.human-handoff-page { max-width:900px; margin:0; padding:8px 4px 32px; } .human-handoff-page { max-width:960px; width:100%; margin:0; padding:4px 0 32px; }
.human-handoff-head { margin:0; } .human-handoff-head { margin:0; }
.handoff-page-title, .handoff-page-title,
.human-handoff-head h1.handoff-page-title { .human-handoff-head h1.handoff-page-title {
@ -553,55 +553,60 @@ h1 em { color:var(--coral); font-style:normal; font-weight:500; }
} }
.handoff-page-lead, .handoff-page-lead,
.human-handoff-head p.handoff-page-lead { .human-handoff-head p.handoff-page-lead {
margin:5px 0 0; margin:8px 0 0;
max-width:640px; max-width:none;
font-size:12px; font-size:13px;
line-height:1.45; line-height:1.5;
font-weight:400; font-weight:400;
color:#77758A; color:#77758A;
} }
.handoff-start-help { max-width:640px; margin-top:14px; padding:12px 14px; border:1px solid #e8e5ec; border-radius:12px; background:#fff; } .handoff-how-toggle { margin:8px 0 0; padding:0; display:inline-flex; align-items:center; gap:4px; border:0; background:transparent; color:#6d5bd0; font-size:12px; font-weight:650; }
.handoff-start-help strong { display:block; color:#171424; font-size:13px; } .handoff-how-toggle svg { transition:transform .15s ease; }
.handoff-start-help p { margin:6px 0 0; color:#77758a; font-size:12px; line-height:1.5; } .handoff-how-toggle.open svg { transform:rotate(180deg); }
.handoff-how { margin-top:10px; display:grid; gap:12px; }
.handoff-how section { padding:12px 14px; border:1px solid #E7E4EA; border-radius:10px; background:#fff; }
.handoff-how h2 { margin:0; font:650 13px/1.3 "DM Sans"; color:#171424; }
.handoff-how p { margin:4px 0 0; color:#77758a; font-size:12px; line-height:1.5; }
.handoff-start-example { color:#3d3948; font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:12px; } .handoff-start-example { color:#3d3948; font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:12px; }
.handoff-toolbar-row { margin-top:20px; display:flex; align-items:center; justify-content:space-between; gap:12px; }
.human-handoff-tabs { .human-handoff-tabs {
margin-top:24px; margin-top:0;
height:38px; height:36px;
padding:3px; padding:3px;
display:inline-flex; display:inline-flex;
align-items:center; align-items:center;
gap:0; gap:0;
border:1px solid #E6E2EA; border:1px solid #E7E4EA;
border-radius:9px; border-radius:9px;
background:#FAF9FB; background:#FAF9FB;
} }
.human-handoff-tabs button { .human-handoff-tabs button {
height:30px; height:28px;
padding:0 14px; padding:0 14px;
border:0; border:0;
border-radius:6px; border-radius:6px;
background:transparent; background:transparent;
color:#777286; color:#777286;
font-size:11px; font-size:12px;
font-weight:550; font-weight:550;
} }
.human-handoff-tabs button.active { .human-handoff-tabs button.active {
background:#FFFFFF; background:#FFFFFF;
color:#252130; color:#252130;
box-shadow:0 1px 2px rgba(20,18,30,.06); box-shadow:none;
} }
.handoff-inbox, .handoff-settings, .handoff-conversation { margin-top:22px; } .handoff-inbox, .handoff-settings, .handoff-conversation { margin-top:20px; }
.handoff-section-head { margin:0 0 12px; } .handoff-section-head { margin:0 0 12px; }
.handoff-section-head h2 { .handoff-section-head h2 {
margin:0; margin:0;
font-size:13px; font-size:14px;
font-weight:650; font-weight:650;
line-height:1.3; line-height:1.3;
color:#292536; color:#292536;
} }
.handoff-section-head p { .handoff-section-head p {
margin:3px 0 0; margin:2px 0 0;
font-size:10.5px; font-size:12px;
font-weight:400; font-weight:400;
line-height:1.4; line-height:1.4;
color:#8A8696; color:#8A8696;
@ -725,28 +730,30 @@ h1 em { color:var(--coral); font-style:normal; font-weight:500; }
.handoff-settings-notice p { margin:0; color:#77758a; font-size:11.5px; line-height:1.45; } .handoff-settings-notice p { margin:0; color:#77758a; font-size:11.5px; line-height:1.45; }
.handoff-flow-diagram { margin:14px 0 10px; padding:14px; display:grid; justify-items:center; gap:4px; border:1px dashed #ddd4ff; border-radius:10px; background:#fff; color:#4a4652; font-size:12px; font-weight:600; } .handoff-flow-diagram { margin:14px 0 10px; padding:14px; display:grid; justify-items:center; gap:4px; border:1px dashed #ddd4ff; border-radius:10px; background:#fff; color:#4a4652; font-size:12px; font-weight:600; }
.handoff-flow-arrow { color:#9a96a5; font-weight:400; } .handoff-flow-arrow { color:#9a96a5; font-weight:400; }
.handoff-settings-toolbar { margin:0 0 14px; display:flex; justify-content:flex-end; } .handoff-settings-toolbar { display:none; }
.human-handoff-empty,.human-handoff-list { margin-top:0; } .human-handoff-empty,.human-handoff-list { margin-top:0; }
.human-handoff-empty { padding:40px 28px; display:grid; justify-items:center; text-align:center; border:1px solid #e8e5ec; border-radius:12px; background:#fff; box-shadow:0 1px 2px rgba(20,18,30,.025); } .human-handoff-empty { padding:28px 20px; display:grid; justify-items:center; text-align:center; border:1px solid #E7E4EA; border-radius:10px; background:#fff; box-shadow:none; }
.human-handoff-empty-icon { width:52px; height:52px; margin-bottom:14px; display:grid; place-items:center; border-radius:14px; background:#f3f0fa; color:#6d5bd0; } .human-handoff-empty-icon { width:40px; height:40px; margin-bottom:10px; display:grid; place-items:center; border-radius:12px; background:#f6f4fb; color:#6d5bd0; }
.human-handoff-empty h2 { margin:0 0 8px; font:700 18px/1.2 "DM Sans"; } .human-handoff-empty h2 { margin:0 0 4px; font:650 15px/1.3 "DM Sans"; }
.human-handoff-empty p { margin:0 0 18px; max-width:420px; color:#77758a; font-size:12px; line-height:1.55; } .human-handoff-empty p { margin:0; max-width:420px; color:#77758a; font-size:12px; line-height:1.5; }
.human-handoff-list { display:grid; gap:10px; } .human-handoff-list { display:grid; gap:8px; }
.human-handoff-card { padding:14px 16px; border:1px solid #e8e5ec; border-radius:12px; background:#fff; box-shadow:0 1px 2px rgba(20,18,30,.025); } .human-handoff-card { padding:10px 12px; border:1px solid #E7E4EA; border-radius:10px; background:#fff; box-shadow:none; }
.human-handoff-card-body { display:grid; grid-template-areas:"avatar name status" "avatar email actions"; grid-template-columns:36px minmax(0,1fr) auto; column-gap:12px; row-gap:2px; align-items:center; } .human-handoff-card-body { display:grid; grid-template-areas:"avatar identity scope actions"; grid-template-columns:32px minmax(160px,1fr) minmax(140px,1.2fr) auto; column-gap:12px; align-items:center; }
.human-handoff-card-avatar { grid-area:avatar; width:36px; height:36px; display:grid; place-items:center; border-radius:50%; background:#f3f0fa; color:#5f4db8; align-self:center; } .human-handoff-card-avatar { grid-area:avatar; width:32px; height:32px; display:grid; place-items:center; border-radius:50%; background:#f6f4fb; color:#5f4db8; }
.human-handoff-card-name { grid-area:name; font:650 14px/1.25 "DM Sans"; color:#171424; } .human-handoff-card-identity { grid-area:identity; min-width:0; display:grid; gap:1px; }
.human-handoff-card-email { grid-area:email; color:#77758a; font-size:11px; line-height:1.3; } .human-handoff-card-name { font:650 13px/1.25 "DM Sans"; color:#171424; }
.human-handoff-status { grid-area:status; height:20px; padding:0 7px; display:inline-flex; align-items:center; justify-self:end; border-radius:999px; background:#f1eff3; color:#716d7a; font-size:9.5px; font-weight:600; } .human-handoff-card-email { color:#77758a; font-size:11px; line-height:1.3; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.human-handoff-card-scope { grid-area:scope; margin:0; color:#6f6b7c; font-size:12px; line-height:1.35; }
.human-handoff-status { height:22px; padding:0 8px; display:inline-flex; align-items:center; border-radius:999px; background:#f4f2f6; color:#716d7a; font-size:11px; font-weight:600; }
.human-handoff-status.active { background:#edf9f4; color:#11966a; } .human-handoff-status.active { background:#edf9f4; color:#11966a; }
.human-handoff-card-actions { grid-area:actions; display:flex; align-items:center; justify-self:end; gap:4px; } .human-handoff-card-actions { grid-area:actions; display:flex; align-items:center; justify-self:end; gap:6px; }
.human-handoff-card-chips { display:flex; flex-wrap:wrap; gap:6px; margin-top:10px; padding-left:48px; }
.handoff-meta-chip { height:21px; padding:0 8px; display:inline-flex; align-items:center; border-radius:6px; background:#f4f2f8; color:#6f6b7c; font-size:10.5px; font-weight:500; white-space:nowrap; } .handoff-meta-chip { height:21px; padding:0 8px; display:inline-flex; align-items:center; border-radius:6px; background:#f4f2f8; color:#6f6b7c; font-size:10.5px; font-weight:500; white-space:nowrap; }
.human-handoff-card-quote { margin:0 0 14px; padding-left:12px; border-left:2px solid #ece7ff; color:#4a4652; font-size:12px; line-height:1.55; } .human-handoff-card-quote { margin:0 0 14px; padding-left:12px; border-left:2px solid #ece7ff; color:#4a4652; font-size:12px; line-height:1.55; }
.handoff-card-menu-wrap { position:relative; } .handoff-card-menu-wrap { position:relative; }
.handoff-card-menu { position:absolute; right:0; top:calc(100% + 4px); min-width:160px; padding:6px; display:grid; gap:2px; border:1px solid #ece9f2; border-radius:10px; background:#fff; box-shadow:0 8px 24px rgba(20,18,30,0.12); z-index:5; } .handoff-card-menu { position:absolute; right:0; top:calc(100% + 4px); min-width:160px; padding:6px; display:grid; gap:2px; border:1px solid #ece9f2; border-radius:10px; background:#fff; box-shadow:0 8px 24px rgba(20,18,30,0.12); z-index:5; }
.handoff-card-menu button { width:100%; min-height:34px; padding:0 10px; border:0; border-radius:8px; background:transparent; text-align:left; font-size:12px; font-weight:600; color:#4a4652; } .handoff-card-menu button { width:100%; min-height:34px; padding:0 10px; border:0; border-radius:8px; background:transparent; text-align:left; font-size:12px; font-weight:600; color:#4a4652; }
.handoff-card-menu button:hover { background:#f7f5fa; } .handoff-card-menu button:hover { background:#f7f5fa; }
.handoff-card-menu button:disabled { color:#b7b3be; cursor:not-allowed; }
.handoff-btn { height:36px; padding:0 14px; display:inline-flex; align-items:center; justify-content:center; gap:7px; border:1px solid #dfdde5; border-radius:9px; background:#fff; color:#241f2b; font-size:12px; font-weight:600; cursor:pointer; transition:background .12s ease; } .handoff-btn { height:36px; padding:0 14px; display:inline-flex; align-items:center; justify-content:center; gap:7px; border:1px solid #dfdde5; border-radius:9px; background:#fff; color:#241f2b; font-size:12px; font-weight:600; cursor:pointer; transition:background .12s ease; }
.handoff-btn:hover:not(:disabled) { background:#f7f5fa; } .handoff-btn:hover:not(:disabled) { background:#f7f5fa; }
.handoff-btn.primary { border-color:#6d5bd0; background:#7357f6; color:#fff; } .handoff-btn.primary { border-color:#6d5bd0; background:#7357f6; color:#fff; }