style(app): normalize tailwind utility syntax

This commit is contained in:
2026-07-13 13:17:16 +08:00
parent aff7f80206
commit b89bde1b55
37 changed files with 143 additions and 143 deletions
@@ -29,7 +29,7 @@ describe("chat Tailwind components", () => {
);
expect(aiHtml).toContain('aria-label="AI avatar"');
expect(aiHtml).toContain("size-[var(--chat-avatar-size,43px)]");
expect(aiHtml).toContain("size-(--chat-avatar-size,43px)");
expect(aiHtml).toContain("size-full object-cover");
expect(aiHtml).toContain("%2Fimages%2Fchat%2Fpic-chat-elio.png");
expect(userHtml).toContain('aria-label="User avatar"');
@@ -97,7 +97,7 @@ describe("chat Tailwind components", () => {
);
expect(activeHtml).toContain('aria-label="Send message"');
expect(activeHtml).toContain("size-[var(--chat-send-button-size,40px)]");
expect(activeHtml).toContain("size-(--chat-send-button-size,40px)");
expect(activeHtml).toContain(
"bg-[linear-gradient(to_right,var(--color-button-gradient-start,#ff67e0),var(--color-button-gradient-end,#ff52a2))]",
);
@@ -124,7 +124,7 @@ describe("chat Tailwind components", () => {
expect(openableHtml).toContain("rounded-tl-none");
expect(openableHtml).toContain("block h-auto w-full object-cover");
expect(openableHtml).toContain("data:image/png;base64,/chat-image.png");
expect(paywalledHtml).toContain("scale-[1.04] blur-[8px]");
expect(paywalledHtml).toContain("scale-104 blur-sm");
expect(paywalledHtml).toContain(
"bg-[linear-gradient(180deg,rgba(255,255,255,0.04)_0%,rgba(255,255,255,0.14)_100%)]",
);
@@ -149,7 +149,7 @@ describe("chat Tailwind components", () => {
expect(html).toContain("Credits keep the conversation going.");
expect(html).toContain('aria-label="Top up"');
expect(html).toContain("bg-[linear-gradient(135deg,#f96ade_0%,#f657a0_100%)]");
expect(html).toContain("active:scale-[0.98]");
expect(html).toContain("active:scale-98");
});
it("renders ChatHeader guest and member branches", () => {
@@ -159,12 +159,12 @@ describe("chat Tailwind components", () => {
);
expect(guestHtml).toContain('aria-label="Sign up to unlock more features"');
expect(guestHtml).toContain("bg-[var(--color-accent,#f84d96)]");
expect(guestHtml).toContain("size-[var(--icon-size-sm,16px)]");
expect(guestHtml).toContain("bg-(--color-accent,#f84d96)");
expect(guestHtml).toContain("size-(--icon-size-sm,16px)");
expect(memberHtml).toContain("Offer");
expect(memberHtml).toContain('aria-label="Menu"');
expect(memberHtml).toContain("bg-[rgba(13,11,20,0.7)]");
expect(memberHtml).toContain("size-[var(--icon-size-xl,32px)]");
expect(memberHtml).toContain("size-(--icon-size-xl,32px)");
});
it("renders ChatInputTextField with textarea utilities", () => {
@@ -179,10 +179,10 @@ describe("chat Tailwind components", () => {
expect(html).toContain("<textarea");
expect(html).toContain('aria-label="Message"');
expect(html).toContain("min-h-[var(--chat-send-button-size,40px)]");
expect(html).toContain("min-h-(--chat-send-button-size,40px)");
expect(html).toContain("max-h-[min(30vh,120px)]");
expect(html).toContain("caret-[var(--color-accent,#f84d96)]");
expect(html).toContain("placeholder:text-[var(--color-text-hint,#757575)]");
expect(html).toContain("caret-(--color-accent,#f84d96)");
expect(html).toContain("placeholder:text-(--color-text-hint,#757575)");
expect(html).toContain("Message Elio");
expect(html).toContain("Hello");
});
@@ -202,8 +202,8 @@ describe("chat Tailwind components", () => {
expect(html).toContain('aria-expanded="true"');
expect(html).toContain('aria-label="Open elsewhere. Better experience"');
expect(html).toContain("grid-cols-[auto_minmax(0,1fr)]");
expect(html).toContain("backdrop-blur-[12px]");
expect(html).toContain("hover:opacity-[0.94]");
expect(html).toContain("backdrop-blur-md");
expect(html).toContain("hover:opacity-94");
expect(html).toContain("Open elsewhere");
expect(html).toContain("Better experience");
});
@@ -217,10 +217,10 @@ describe("chat Tailwind components", () => {
expect(html).toContain('aria-modal="true"');
expect(html).toContain('aria-labelledby="pwa-dialog-title"');
expect(html).toContain("fixed inset-0");
expect(html).toContain("max-w-[var(--pwa-install-dialog-max-width,360px)]");
expect(html).toContain("size-[var(--icon-size-86,86px)]");
expect(html).toContain("min-h-[var(--pwa-button-height,44px)]");
expect(html).toContain("text-[var(--color-pwa-button-text,#ffffff)]");
expect(html).toContain("max-w-(--pwa-install-dialog-max-width,360px)");
expect(html).toContain("size-(--icon-size-86,86px)");
expect(html).toContain("min-h-(--pwa-button-height,44px)");
expect(html).toContain("text-(--color-pwa-button-text,#ffffff)");
expect(html).toContain("%2Fimages%2Ficons%2Ficon-add-to-home.png");
expect(html).toContain("Add to Home Screen");
expect(html).toContain("Cancel");
@@ -251,12 +251,12 @@ describe("chat Tailwind components", () => {
);
expect(html).toContain('aria-labelledby="history-unlock-title"');
expect(html).toContain("z-[200]");
expect(html).toContain("z-200");
expect(html).toContain("Unlock previous messages?");
expect(html).toContain("We found 3 locked messages");
expect(html).toContain("Could not unlock history.");
expect(html).toContain("Unlocking...");
expect(html).toContain("text-[var(--color-pwa-button-text,#ffffff)]");
expect(html).toContain("text-(--color-pwa-button-text,#ffffff)");
expect(html).toContain("disabled");
});
@@ -286,12 +286,12 @@ describe("chat Tailwind components", () => {
);
expect(html).toContain('aria-labelledby="insufficient-credits-title"');
expect(html).toContain("z-[220]");
expect(html).toContain("z-220");
expect(html).toContain("Not enough credits");
expect(html).toContain("Balance");
expect(html).toContain("Required");
expect(html).toContain("Still needed");
expect(html).toContain("text-[var(--color-pwa-button-text,#ffffff)]");
expect(html).toContain("text-(--color-pwa-button-text,#ffffff)");
expect(html).toContain("bg-[linear-gradient(90deg,#ff67e0_0%,#ff52a2_100%)]");
expect(html).toContain("Top up now");
});
@@ -318,7 +318,7 @@ describe("chat Tailwind components", () => {
);
expect(html).toContain('aria-labelledby="voice-unlock-options-title"');
expect(html).toContain("z-[72]");
expect(html).toContain("z-72");
expect(html).toContain("Unlock voice message");
expect(html).toContain("购买语音包");
expect(html).toContain("开通 VIP");
@@ -30,7 +30,7 @@ const DEFAULT_TITLE = "Open in external browser";
const DEFAULT_DESCRIPTION = "Find me more easily";
const DEFAULT_AUTO_COLLAPSE_DELAY_MS = 3500;
const OVERLAY_BASE_CLASS_NAME =
"pointer-events-auto absolute right-[var(--spacing-2,8px)] top-[var(--spacing-2,8px)] z-20 box-border min-h-12 max-w-[min(74vw,260px)] cursor-pointer border border-[rgba(255,255,255,0.18)] bg-[linear-gradient(135deg,rgba(255,255,255,0.14),rgba(255,255,255,0.04)),var(--color-blur-background,rgba(13,11,20,0.86))] text-white shadow-[0_12px_28px_rgba(0,0,0,0.24),inset_0_1px_0_rgba(255,255,255,0.16)] backdrop-blur-[12px] backdrop-saturate-[1.2] transition-[border-color,box-shadow,opacity,padding,transform] duration-[180ms] [-webkit-tap-highlight-color:transparent] hover:-translate-y-px hover:border-[rgba(255,255,255,0.28)] hover:opacity-[0.94] hover:shadow-[0_14px_32px_rgba(0,0,0,0.28),inset_0_1px_0_rgba(255,255,255,0.18)] active:translate-y-px active:scale-[0.99] active:opacity-[0.88] focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-[rgba(255,255,255,0.88)]";
"pointer-events-auto absolute right-(--spacing-2,8px) top-(--spacing-2,8px) z-20 box-border min-h-12 max-w-[min(74vw,260px)] cursor-pointer border border-[rgba(255,255,255,0.18)] bg-[linear-gradient(135deg,rgba(255,255,255,0.14),rgba(255,255,255,0.04)),var(--color-blur-background,rgba(13,11,20,0.86))] text-white shadow-[0_12px_28px_rgba(0,0,0,0.24),inset_0_1px_0_rgba(255,255,255,0.16)] backdrop-blur-md backdrop-saturate-120 transition-[border-color,box-shadow,opacity,padding,transform] duration-180 [-webkit-tap-highlight-color:transparent] hover:-translate-y-px hover:border-[rgba(255,255,255,0.28)] hover:opacity-94 hover:shadow-[0_14px_32px_rgba(0,0,0,0.28),inset_0_1px_0_rgba(255,255,255,0.18)] active:translate-y-px active:scale-99 active:opacity-88 focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-[rgba(255,255,255,0.88)]";
const OVERLAY_EXPANDED_CLASS_NAME =
"grid w-fit grid-cols-[auto_minmax(0,1fr)] items-center gap-2 rounded-[18px] px-2.5 py-2";
const OVERLAY_COLLAPSED_CLASS_NAME =
@@ -90,7 +90,7 @@ export function BrowserHintOverlay({
onClick={handleClick}
>
<span
className="inline-flex size-[30px] items-center justify-center rounded-full bg-[rgba(255,255,255,0.15)] text-white shadow-[inset_0_1px_0_rgba(255,255,255,0.16)] transition-[background,transform] duration-[180ms]"
className="inline-flex size-[30px] items-center justify-center rounded-full bg-[rgba(255,255,255,0.15)] text-white shadow-[inset_0_1px_0_rgba(255,255,255,0.16)] transition-[background,transform] duration-180"
aria-hidden="true"
>
<ExternalLink size={15} strokeWidth={2.4} />
@@ -103,7 +103,7 @@ export function BrowserHintOverlay({
}
aria-hidden={!isExpanded}
>
<span className="break-words text-[var(--font-size-sm,12px)] font-extrabold leading-[1.25] text-white">
<span className="break-words text-(length:--font-size-sm,12px) font-extrabold leading-[1.25] text-white">
{title}
</span>
<span className="break-words text-[11px] font-semibold leading-[1.2] text-[rgba(255,255,255,0.72)]">
+5 -5
View File
@@ -24,12 +24,12 @@ export function ChatHeader({ isGuest, offerBanner }: ChatHeaderProps) {
<header className="flex shrink-0 flex-col items-stretch bg-transparent p-0">
<button
type="button"
className="flex w-full cursor-pointer items-center justify-center gap-[var(--spacing-sm,8px)] border-0 bg-[var(--color-accent,#f84d96)] px-[var(--spacing-md,12px)] py-[var(--spacing-sm,8px)] text-center text-[var(--responsive-caption,var(--font-size-sm,12px))] font-medium text-white"
className="flex w-full cursor-pointer items-center justify-center gap-(--spacing-sm,8px) border-0 bg-(--color-accent,#f84d96) px-(--spacing-md,12px) py-(--spacing-sm,8px) text-center text-(length:--responsive-caption,var(--font-size-sm,12px)) font-medium text-white"
onClick={() => navigator.openAuth(ROUTES.chat)}
aria-label="Sign up to unlock more features"
>
<Lock
className="size-[var(--icon-size-sm,16px)]"
className="size-(--icon-size-sm,16px)"
size={16}
aria-hidden="true"
/>
@@ -43,16 +43,16 @@ export function ChatHeader({ isGuest, offerBanner }: ChatHeaderProps) {
<header className="flex shrink-0 flex-col items-stretch bg-transparent p-0">
{offerBanner}
<div className="ml-[var(--spacing-md,12px)] flex items-center justify-between px-[var(--spacing-md,12px)] py-[var(--spacing-sm,8px)]">
<div className="ml-(--spacing-md,12px) flex items-center justify-between px-(--spacing-md,12px) py-(--spacing-sm,8px)">
{/* 菜单按钮(点击 → push 到 /sidebar,与 Dart MenuButton 一致) */}
<button
type="button"
className="flex cursor-pointer items-center justify-center rounded-[var(--radius-full,999px)] border-0 bg-[rgba(13,11,20,0.7)] p-[var(--spacing-sm,8px)] text-[var(--color-text-primary,#fff)]"
className="flex cursor-pointer items-center justify-center rounded-(--radius-full,999px) border-0 bg-[rgba(13,11,20,0.7)] p-(--spacing-sm,8px) text-(--color-text-primary,#fff)"
onClick={() => navigator.push(ROUTES.sidebar)}
aria-label="Menu"
>
<Menu
className="size-[var(--icon-size-xl,32px)]"
className="size-(--icon-size-xl,32px)"
size={24}
aria-hidden="true"
/>
@@ -63,7 +63,7 @@ export const ChatInputTextField = forwardRef<
<div className="flex min-w-0 flex-auto items-center rounded-full bg-white px-(--spacing-lg,16px)">
<textarea
ref={innerRef}
className="min-h-[var(--chat-send-button-size,40px)] max-h-[min(30vh,120px)] w-full min-w-0 flex-auto resize-none border-0 bg-transparent pb-0 pl-0 pr-0 pt-[clamp(5px,1.111vw,6px)] font-[inherit] text-[16px] leading-[clamp(22px,4.444vw,24px)] text-[var(--color-text-foreground,#000)] caret-[var(--color-accent,#f84d96)] outline-none placeholder:text-[var(--color-text-hint,#757575)]"
className="min-h-(--chat-send-button-size,40px) max-h-[min(30vh,120px)] w-full min-w-0 flex-auto resize-none border-0 bg-transparent pb-0 pl-0 pr-0 pt-[clamp(5px,1.111vw,6px)] font-[inherit] text-[16px] leading-[clamp(22px,4.444vw,24px)] text-(--color-text-foreground,#000) caret-(--color-accent,#f84d96) outline-none placeholder:text-(--color-text-hint,#757575)"
value={value}
onChange={(e: FormEvent<HTMLTextAreaElement>) =>
onChange(e.currentTarget.value)
@@ -49,12 +49,12 @@ export function ChatInsufficientCreditsBanner({
return (
<div
className="mx-[var(--chat-inline-padding,16px)] mb-[var(--page-section-gap,16px)] shrink-0 rounded-[var(--responsive-card-radius-sm,24px)] bg-[linear-gradient(135deg,#f96ade_0%,#f657a0_100%)] px-[var(--responsive-card-padding,16px)] py-[var(--page-section-gap-lg,24px)] text-center shadow-[0_4px_16px_rgba(248,77,150,0.25)]"
className="mx-(--chat-inline-padding,16px) mb-(--page-section-gap,16px) shrink-0 rounded-(--responsive-card-radius-sm,24px) bg-[linear-gradient(135deg,#f96ade_0%,#f657a0_100%)] px-(--responsive-card-padding,16px) py-(--page-section-gap-lg,24px) text-center shadow-[0_4px_16px_rgba(248,77,150,0.25)]"
role="status"
aria-live="polite"
data-testid="chat-insufficient-credits-banner"
>
<p className="m-0 mb-[var(--spacing-sm,8px)] text-[var(--responsive-card-title,18px)] font-semibold leading-[1.4] text-white opacity-95">
<p className="m-0 mb-(--spacing-sm,8px) text-(length:--responsive-card-title,18px) font-semibold leading-[1.4] text-white opacity-95">
{titleLines.map((line, index) => (
<span key={`${line}-${index}`}>
{line}
@@ -63,13 +63,13 @@ export function ChatInsufficientCreditsBanner({
))}
</p>
{description ? (
<p className="mx-auto mb-[var(--spacing-lg,16px)] mt-0 max-w-[min(100%,360px)] text-[var(--responsive-caption,14px)] font-semibold leading-[1.4] text-[rgba(255,255,255,0.88)]">
<p className="mx-auto mb-(--spacing-lg,16px) mt-0 max-w-[min(100%,360px)] text-(length:--responsive-caption,14px) font-semibold leading-[1.4] text-[rgba(255,255,255,0.88)]">
{description}
</p>
) : null}
<button
type="button"
className="min-h-[var(--responsive-control-height,48px)] w-full cursor-pointer rounded-full border-0 bg-[linear-gradient(135deg,#ffb8e0_0%,#f57ec0_100%)] px-[var(--responsive-card-padding-lg,24px)] text-[var(--responsive-card-title,18px)] font-bold text-white shadow-[0_2px_8px_rgba(0,0,0,0.15)] transition-[box-shadow,transform] duration-150 hover:shadow-[0_4px_12px_rgba(0,0,0,0.2)] active:scale-[0.98]"
className="min-h-(--responsive-control-height,48px) w-full cursor-pointer rounded-full border-0 bg-[linear-gradient(135deg,#ffb8e0_0%,#f57ec0_100%)] px-(--responsive-card-padding-lg,24px) text-(length:--responsive-card-title,18px) font-bold text-white shadow-[0_2px_8px_rgba(0,0,0,0.15)] transition-[box-shadow,transform] duration-150 hover:shadow-[0_4px_12px_rgba(0,0,0,0.2)] active:scale-98"
onClick={handleClick}
aria-label={ctaLabel}
>
+2 -2
View File
@@ -30,7 +30,7 @@ export function ChatSendButton({
<button
type="button"
className={[
"flex aspect-square size-[var(--chat-send-button-size,40px)] shrink-0 cursor-pointer items-center justify-center rounded-full border-0 bg-[var(--color-button-gradient-end,#fc69df)] text-white transition-[background,transform] duration-200 disabled:cursor-not-allowed disabled:opacity-40 focus-visible:bg-[linear-gradient(to_right,var(--color-button-gradient-start,#ff67e0),var(--color-button-gradient-end,#ff52a2))]",
"flex aspect-square size-(--chat-send-button-size,40px) shrink-0 cursor-pointer items-center justify-center rounded-full border-0 bg-(--color-button-gradient-end,#fc69df) text-white transition-[background,transform] duration-200 disabled:cursor-not-allowed disabled:opacity-40 focus-visible:bg-[linear-gradient(to_right,var(--color-button-gradient-start,#ff67e0),var(--color-button-gradient-end,#ff52a2))]",
isActive
? "bg-[linear-gradient(to_right,var(--color-button-gradient-start,#ff67e0),var(--color-button-gradient-end,#ff52a2))]"
: "bg-[#f8a8ce] text-[rgba(255,255,255,0.88)] shadow-none",
@@ -49,7 +49,7 @@ export function ChatSendButton({
aria-label="Send message"
>
<ArrowUp
className="size-[var(--icon-size-xl,24px)] text-[length:var(--icon-size-xl,24px)] leading-none"
className="size-(--icon-size-xl,24px) text-(length:--icon-size-xl,24px) leading-none"
size={24}
aria-hidden="true"
/>
@@ -21,31 +21,31 @@ export function HistoryUnlockDialog({
return (
<div
className="fixed inset-0 z-[200] flex items-center justify-center bg-[rgba(0,0,0,0.5)] pb-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-bottom,0px))] pl-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-left,0px))] pr-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-right,0px))] pt-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-top,0px))]"
className="fixed inset-0 z-200 flex items-center justify-center bg-[rgba(0,0,0,0.5)] pb-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-bottom,0px))] pl-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-left,0px))] pr-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-right,0px))] pt-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-top,0px))]"
role="dialog"
aria-modal="true"
aria-labelledby="history-unlock-title"
>
<div className="w-full max-w-[var(--pwa-install-dialog-max-width,360px)] rounded-[var(--responsive-card-radius,40px)] bg-[var(--color-page-background,#ffffff)] px-[var(--responsive-card-padding,16px)] pb-[var(--responsive-card-padding,16px)] pt-[var(--responsive-card-padding-lg,24px)] text-center shadow-[0_10px_20px_rgba(0,0,0,0.1)]">
<div className="w-full max-w-(--pwa-install-dialog-max-width,360px) rounded-(--responsive-card-radius,40px) bg-(--color-page-background,#ffffff) px-(--responsive-card-padding,16px) pb-(--responsive-card-padding,16px) pt-(--responsive-card-padding-lg,24px) text-center shadow-[0_10px_20px_rgba(0,0,0,0.1)]">
<h2
id="history-unlock-title"
className="m-0 mb-2.5 text-[var(--responsive-page-title,var(--font-size-22,22px))] font-bold leading-[1.2] text-[var(--color-text-foreground,#171717)]"
className="m-0 mb-2.5 text-(length:--responsive-page-title,var(--font-size-22,22px)) font-bold leading-[1.2] text-(--color-text-foreground,#171717)"
>
Unlock previous messages?
</h2>
<p className="mx-[var(--responsive-card-padding-lg,24px)] mb-[var(--spacing-lg,16px)] mt-0 text-left text-[var(--responsive-body,var(--font-size-lg,16px))] leading-[1.5] text-[#393939]">
<p className="mx-(--responsive-card-padding-lg,24px) mb-(--spacing-lg,16px) mt-0 text-left text-(length:--responsive-body,var(--font-size-lg,16px)) leading-[1.5] text-[#393939]">
We found {lockedCount} locked messages in your chat history. You can
unlock them now to continue the conversation with full context.
</p>
{errorMessage ? (
<p className="mx-[var(--responsive-card-padding-lg,24px)] mb-[var(--spacing-lg,16px)] mt-0 text-left text-[var(--responsive-caption,var(--font-size-md,14px))] leading-[1.4] text-[#c0364c]">
<p className="mx-(--responsive-card-padding-lg,24px) mb-(--spacing-lg,16px) mt-0 text-left text-(length:--responsive-caption,var(--font-size-md,14px)) leading-[1.4] text-[#c0364c]">
{errorMessage}
</p>
) : null}
<div className="flex w-full gap-[var(--spacing-md,12px)]">
<div className="flex w-full gap-(--spacing-md,12px)">
<button
type="button"
className="flex min-h-[var(--pwa-button-height,44px)] flex-auto cursor-pointer items-center justify-center rounded-[var(--radius-bottom-sheet,28px)] border-0 bg-[var(--color-text-secondary,#9e9e9e)] text-[var(--responsive-body,var(--font-size-lg,16px))] font-semibold text-[var(--color-pwa-button-text,#ffffff)] disabled:cursor-not-allowed disabled:opacity-72"
className="flex min-h-(--pwa-button-height,44px) flex-auto cursor-pointer items-center justify-center rounded-(--radius-bottom-sheet,28px) border-0 bg-(--color-text-secondary,#9e9e9e) text-(length:--responsive-body,var(--font-size-lg,16px)) font-semibold text-(--color-pwa-button-text,#ffffff) disabled:cursor-not-allowed disabled:opacity-72"
onClick={onClose}
disabled={isLoading}
>
@@ -53,7 +53,7 @@ export function HistoryUnlockDialog({
</button>
<button
type="button"
className="flex min-h-[var(--pwa-button-height,44px)] flex-auto cursor-pointer items-center justify-center rounded-[var(--radius-bottom-sheet,28px)] border-0 bg-[linear-gradient(var(--color-button-gradient-start,#ff67e0),var(--color-button-gradient-end,#ff52a2))] text-[var(--responsive-body,var(--font-size-lg,16px))] font-semibold text-[var(--color-pwa-button-text,#ffffff)] disabled:cursor-not-allowed disabled:opacity-72"
className="flex min-h-(--pwa-button-height,44px) flex-auto cursor-pointer items-center justify-center rounded-(--radius-bottom-sheet,28px) border-0 bg-[linear-gradient(var(--color-button-gradient-start,#ff67e0),var(--color-button-gradient-end,#ff52a2))] text-(length:--responsive-body,var(--font-size-lg,16px)) font-semibold text-(--color-pwa-button-text,#ffffff) disabled:cursor-not-allowed disabled:opacity-72"
onClick={onConfirm}
disabled={isLoading}
>
+3 -3
View File
@@ -26,7 +26,7 @@ export function ImageBubble({
const canOpen = Boolean(messageId && onOpenImage);
const imageClassName = [
"block h-auto w-full object-cover",
imagePaywalled ? "scale-[1.04] blur-[8px]" : "",
imagePaywalled ? "scale-104 blur-sm" : "",
]
.filter(Boolean)
.join(" ");
@@ -38,7 +38,7 @@ export function ImageBubble({
return (
<div
className="relative max-h-[var(--chat-media-size,220px)] max-w-[var(--chat-media-size,220px)] cursor-pointer overflow-hidden rounded-[var(--radius-lg,12px)] rounded-tl-none bg-[var(--color-bubble-background,#fff)]"
className="relative max-h-(--chat-media-size,220px) max-w-(--chat-media-size,220px) cursor-pointer overflow-hidden rounded-(--radius-lg,12px) rounded-tl-none bg-(--color-bubble-background,#fff)"
onClick={openImage}
role={canOpen ? "button" : undefined}
tabIndex={canOpen ? 0 : undefined}
@@ -55,7 +55,7 @@ export function ImageBubble({
messageId={messageId}
remoteUrl={imageUrl}
className={imageClassName}
errorClassName="flex size-[var(--chat-media-size,220px)] items-center justify-center bg-[var(--color-bubble-background,#fff)] text-[var(--icon-size-xl,24px)] text-[var(--color-text-secondary,#9e9e9e)]"
errorClassName="flex size-(--chat-media-size,220px) items-center justify-center bg-(--color-bubble-background,#fff) text-(length:--icon-size-xl,24px) text-(--color-text-secondary,#9e9e9e)"
width={240}
height={240}
>
@@ -23,56 +23,56 @@ export function InsufficientCreditsDialog({
return (
<div
className="fixed inset-0 z-[220] flex items-center justify-center bg-[rgba(0,0,0,0.52)] pb-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-bottom,0px))] pl-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-left,0px))] pr-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-right,0px))] pt-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-top,0px))]"
className="fixed inset-0 z-220 flex items-center justify-center bg-[rgba(0,0,0,0.52)] pb-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-bottom,0px))] pl-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-left,0px))] pr-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-right,0px))] pt-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-top,0px))]"
role="dialog"
aria-modal="true"
aria-labelledby="insufficient-credits-title"
>
<div className="w-full max-w-[var(--pwa-install-dialog-max-width,360px)] rounded-[var(--responsive-card-radius,36px)] border border-[rgba(246,87,160,0.16)] bg-[var(--color-page-background,#ffffff)] px-[var(--responsive-card-padding,18px)] pb-[var(--responsive-card-padding,18px)] pt-[var(--responsive-card-padding-lg,24px)] text-center shadow-[0_18px_46px_rgba(38,18,31,0.18)]">
<div className="w-full max-w-(--pwa-install-dialog-max-width,360px) rounded-(--responsive-card-radius,36px) border border-[rgba(246,87,160,0.16)] bg-(--color-page-background,#ffffff) px-(--responsive-card-padding,18px) pb-(--responsive-card-padding,18px) pt-(--responsive-card-padding-lg,24px) text-center shadow-[0_18px_46px_rgba(38,18,31,0.18)]">
<h2
id="insufficient-credits-title"
className="m-0 mb-2.5 text-[var(--responsive-page-title,var(--font-size-22,22px))] font-bold leading-[1.2] text-[var(--color-text-foreground,#171717)]"
className="m-0 mb-2.5 text-(length:--responsive-page-title,var(--font-size-22,22px)) font-bold leading-[1.2] text-(--color-text-foreground,#171717)"
>
Not enough credits
</h2>
<p className="mx-[var(--spacing-md,12px)] mb-[var(--page-section-gap,18px)] mt-0 text-[var(--responsive-body,var(--font-size-lg,16px))] leading-[1.5] text-[#393939]">
<p className="mx-(--spacing-md,12px) mb-(--page-section-gap,18px) mt-0 text-(length:--responsive-body,var(--font-size-lg,16px)) leading-[1.5] text-[#393939]">
Top up your credits to unlock this message and keep the moment going.
</p>
{showCreditDetail ? (
<dl className="mb-[var(--page-section-gap,18px)] grid gap-[var(--spacing-sm,8px)] rounded-[var(--responsive-card-radius-sm,22px)] bg-[#fff4f9] p-[var(--spacing-md,12px)]">
<div className="flex items-center justify-between gap-[var(--spacing-md,12px)] text-[var(--responsive-caption,var(--font-size-md,14px))] leading-[1.3] text-[#4a3b43]">
<dl className="mb-(--page-section-gap,18px) grid gap-(--spacing-sm,8px) rounded-(--responsive-card-radius-sm,22px) bg-[#fff4f9] p-(--spacing-md,12px)">
<div className="flex items-center justify-between gap-(--spacing-md,12px) text-(length:--responsive-caption,var(--font-size-md,14px)) leading-[1.3] text-[#4a3b43]">
<dt className="m-0 font-medium">Balance</dt>
<dd className="m-0 text-[var(--responsive-body,var(--font-size-lg,16px))] font-extrabold text-[#f657a0]">
<dd className="m-0 text-(length:--responsive-body,var(--font-size-lg,16px)) font-extrabold text-[#f657a0]">
{creditBalance}
</dd>
</div>
<div className="flex items-center justify-between gap-[var(--spacing-md,12px)] text-[var(--responsive-caption,var(--font-size-md,14px))] leading-[1.3] text-[#4a3b43]">
<div className="flex items-center justify-between gap-(--spacing-md,12px) text-(length:--responsive-caption,var(--font-size-md,14px)) leading-[1.3] text-[#4a3b43]">
<dt className="m-0 font-medium">Required</dt>
<dd className="m-0 text-[var(--responsive-body,var(--font-size-lg,16px))] font-extrabold text-[#f657a0]">
<dd className="m-0 text-(length:--responsive-body,var(--font-size-lg,16px)) font-extrabold text-[#f657a0]">
{requiredCredits}
</dd>
</div>
<div className="flex items-center justify-between gap-[var(--spacing-md,12px)] text-[var(--responsive-caption,var(--font-size-md,14px))] leading-[1.3] text-[#4a3b43]">
<div className="flex items-center justify-between gap-(--spacing-md,12px) text-(length:--responsive-caption,var(--font-size-md,14px)) leading-[1.3] text-[#4a3b43]">
<dt className="m-0 font-medium">Still needed</dt>
<dd className="m-0 text-[var(--responsive-body,var(--font-size-lg,16px))] font-extrabold text-[#f657a0]">
<dd className="m-0 text-(length:--responsive-body,var(--font-size-lg,16px)) font-extrabold text-[#f657a0]">
{shortfallCredits}
</dd>
</div>
</dl>
) : null}
<div className="flex w-full gap-[var(--spacing-md,12px)]">
<div className="flex w-full gap-(--spacing-md,12px)">
<button
type="button"
className="flex min-h-[var(--pwa-button-height,44px)] flex-auto cursor-pointer items-center justify-center rounded-[var(--radius-bottom-sheet,28px)] border-0 bg-[var(--color-text-secondary,#9e9e9e)] text-[var(--responsive-body,var(--font-size-lg,16px))] font-bold text-[var(--color-pwa-button-text,#ffffff)] focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#f657a0]"
className="flex min-h-(--pwa-button-height,44px) flex-auto cursor-pointer items-center justify-center rounded-(--radius-bottom-sheet,28px) border-0 bg-(--color-text-secondary,#9e9e9e) text-(length:--responsive-body,var(--font-size-lg,16px)) font-bold text-(--color-pwa-button-text,#ffffff) focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#f657a0]"
onClick={onClose}
>
Later
</button>
<button
type="button"
className="flex min-h-[var(--pwa-button-height,44px)] flex-auto cursor-pointer items-center justify-center rounded-[var(--radius-bottom-sheet,28px)] border-0 bg-[linear-gradient(90deg,#ff67e0_0%,#ff52a2_100%)] text-[var(--responsive-body,var(--font-size-lg,16px))] font-bold text-[var(--color-pwa-button-text,#ffffff)] shadow-[0_6px_14px_rgba(246,87,160,0.28)] focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#f657a0]"
className="flex min-h-(--pwa-button-height,44px) flex-auto cursor-pointer items-center justify-center rounded-(--radius-bottom-sheet,28px) border-0 bg-[linear-gradient(90deg,#ff67e0_0%,#ff52a2_100%)] text-(length:--responsive-body,var(--font-size-lg,16px)) font-bold text-(--color-pwa-button-text,#ffffff) shadow-[0_6px_14px_rgba(246,87,160,0.28)] focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#f657a0]"
onClick={onConfirm}
>
Top up now
@@ -15,7 +15,7 @@ export function LockedImageMessageCard({
}: LockedImageMessageCardProps) {
return (
<div
className="w-[min(72vw,280px)] overflow-hidden rounded-[var(--responsive-card-radius-sm,18px)] rounded-tl-none border border-[rgba(246,87,160,0.2)] bg-white shadow-[0_4px_14px_rgba(246,87,160,0.14)]"
className="w-[min(72vw,280px)] overflow-hidden rounded-(--responsive-card-radius-sm,18px) rounded-tl-none border border-[rgba(246,87,160,0.2)] bg-white shadow-[0_4px_14px_rgba(246,87,160,0.14)]"
role="group"
aria-label="Locked private image"
>
@@ -25,15 +25,15 @@ export function LockedImageMessageCard({
<LockKeyhole size={19} aria-hidden="true" />
</span>
</div>
<div className="p-[var(--responsive-card-padding,14px)]">
<p className="m-0 text-[var(--responsive-body,14px)] leading-[1.4] text-[#3c3b3b]">
<div className="p-(--responsive-card-padding,14px)">
<p className="m-0 text-(length:--responsive-body,14px) leading-[1.4] text-[#3c3b3b]">
{hint && hint.length > 0
? hint
: "Elio sent you a locked image."}
</p>
<button
type="button"
className="mt-[var(--spacing-md,12px)] w-full cursor-pointer rounded-full border-0 bg-[linear-gradient(90deg,#ff67e0,#ff52a2)] px-[var(--spacing-md,12px)] py-[clamp(9px,1.852vw,10px)] text-[var(--responsive-body,14px)] font-bold text-white disabled:cursor-not-allowed disabled:opacity-65 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#f657a0]"
className="mt-(--spacing-md,12px) w-full cursor-pointer rounded-full border-0 bg-[linear-gradient(90deg,#ff67e0,#ff52a2)] px-(--spacing-md,12px) py-[clamp(9px,1.852vw,10px)] text-(length:--responsive-body,14px) font-bold text-white disabled:cursor-not-allowed disabled:opacity-65 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#f657a0]"
disabled={isUnlocking || !onUnlock}
onClick={onUnlock}
>
+1 -1
View File
@@ -9,7 +9,7 @@ export interface MessageAvatarProps {
}
const AVATAR_CLASS_NAME =
"flex size-[var(--chat-avatar-size,43px)] shrink-0 items-center justify-center overflow-hidden rounded-full border-2 border-[var(--color-avatar-border,#fbf3f5)] bg-[var(--color-avatar-border,#fbf3f5)] shadow-[var(--shadow-input-box,0_1px_2px_rgba(0,0,0,0.1))]";
"flex size-(--chat-avatar-size,43px) shrink-0 items-center justify-center overflow-hidden rounded-full border-2 border-(--color-avatar-border,#fbf3f5) bg-(--color-avatar-border,#fbf3f5) shadow-(--shadow-input-box,0_1px_2px_rgba(0,0,0,0.1))";
export function MessageAvatar({ isFromAI, userAvatarUrl }: MessageAvatarProps) {
if (isFromAI) {
@@ -15,24 +15,24 @@ export function PrivateMessageCard({
}: PrivateMessageCardProps) {
return (
<div
className="max-w-[min(72vw,280px)] rounded-[var(--responsive-card-radius-sm,18px)] rounded-tl-none border border-[rgba(246,87,160,0.2)] bg-[linear-gradient(180deg,rgba(255,244,248,0.95),rgba(255,255,255,0.95)),#ffffff] p-[var(--responsive-card-padding,14px)] text-[#3c3b3b] shadow-[0_4px_12px_rgba(246,87,160,0.12)]"
className="max-w-[min(72vw,280px)] rounded-(--responsive-card-radius-sm,18px) rounded-tl-none border border-[rgba(246,87,160,0.2)] bg-[linear-gradient(180deg,rgba(255,244,248,0.95),rgba(255,255,255,0.95)),#ffffff] p-(--responsive-card-padding,14px) text-[#3c3b3b] shadow-[0_4px_12px_rgba(246,87,160,0.12)]"
role="group"
aria-label="Locked private message"
>
<div
className="mb-[clamp(8px,1.852vw,10px)] flex size-[var(--responsive-icon-button-size,42px)] items-center justify-center rounded-full bg-[linear-gradient(135deg,#ff8fc7_0%,#f657a0_100%)] text-white"
className="mb-[clamp(8px,1.852vw,10px)] flex size-(--responsive-icon-button-size,42px) items-center justify-center rounded-full bg-[linear-gradient(135deg,#ff8fc7_0%,#f657a0_100%)] text-white"
aria-hidden="true"
>
<LockKeyhole className="block" size={22} />
</div>
<p className="m-0 text-[var(--responsive-body,14px)] leading-[1.4] text-[#3c3b3b]">
<p className="m-0 text-(length:--responsive-body,14px) leading-[1.4] text-[#3c3b3b]">
{hint && hint.length > 0
? hint
: "Elio has a private message for you."}
</p>
<button
type="button"
className="mt-[var(--spacing-md,12px)] w-full cursor-pointer rounded-full border-0 bg-[linear-gradient(90deg,#ff67e0,#ff52a2)] px-[var(--spacing-md,12px)] py-[clamp(9px,1.852vw,10px)] text-[var(--responsive-body,14px)] font-bold text-white disabled:cursor-not-allowed disabled:opacity-65 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#f657a0]"
className="mt-(--spacing-md,12px) w-full cursor-pointer rounded-full border-0 bg-[linear-gradient(90deg,#ff67e0,#ff52a2)] px-(--spacing-md,12px) py-[clamp(9px,1.852vw,10px)] text-(length:--responsive-body,14px) font-bold text-white disabled:cursor-not-allowed disabled:opacity-65 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#f657a0]"
disabled={isUnlocking || !onUnlock}
onClick={onUnlock}
>
@@ -34,9 +34,9 @@ export function PwaInstallDialog({ onClose, onInstall }: PwaInstallDialogProps)
aria-modal="true"
aria-labelledby="pwa-dialog-title"
>
<div className="flex w-full max-w-[var(--pwa-install-dialog-max-width,360px)] flex-col items-center rounded-[var(--responsive-card-radius,40px)] bg-[var(--color-page-background,#ffffff)] px-[var(--responsive-card-padding,16px)] pb-[var(--responsive-card-padding,16px)] pt-[var(--responsive-card-padding-lg,20px)] text-center shadow-[0_10px_20px_rgba(0,0,0,0.1)]">
<div className="flex w-full max-w-(--pwa-install-dialog-max-width,360px) flex-col items-center rounded-(--responsive-card-radius,40px) bg-(--color-page-background,#ffffff) px-(--responsive-card-padding,16px) pb-(--responsive-card-padding,16px) pt-(--responsive-card-padding-lg,20px) text-center shadow-[0_10px_20px_rgba(0,0,0,0.1)]">
<Image
className="mb-[var(--spacing-lg,16px)] size-[var(--icon-size-86,86px)] object-contain"
className="mb-(--spacing-lg,16px) size-(--icon-size-86,86px) object-contain"
src="/images/icons/icon-add-to-home.png"
alt=""
width={86}
@@ -45,24 +45,24 @@ export function PwaInstallDialog({ onClose, onInstall }: PwaInstallDialogProps)
/>
<h2
id="pwa-dialog-title"
className="m-0 mb-[var(--spacing-sm,8px)] text-[var(--responsive-page-title,var(--font-size-22,22px))] font-bold leading-[1.2] text-[var(--color-text-foreground,#171717)]"
className="m-0 mb-(--spacing-sm,8px) text-(length:--responsive-page-title,var(--font-size-22,22px)) font-bold leading-[1.2] text-(--color-text-foreground,#171717)"
>
Add to Home Screen
</h2>
<p className="mx-[var(--responsive-card-padding-lg,36px)] mb-[var(--page-section-gap-lg,24px)] mt-0 text-[var(--responsive-body,var(--font-size-lg,16px))] leading-[1.5] text-[#393939]">
<p className="mx-(--responsive-card-padding-lg,36px) mb-(--page-section-gap-lg,24px) mt-0 text-(length:--responsive-body,var(--font-size-lg,16px)) leading-[1.5] text-[#393939]">
Add CozSweet to Home Screen{"\n"}for the best experience
</p>
<div className="flex w-full gap-[var(--spacing-md,12px)]">
<div className="flex w-full gap-(--spacing-md,12px)">
<button
type="button"
className="flex min-h-[var(--pwa-button-height,44px)] flex-auto cursor-pointer items-center justify-center rounded-[var(--radius-bottom-sheet,28px)] border-0 bg-[var(--color-text-secondary,#9e9e9e)] text-[var(--responsive-body,var(--font-size-lg,16px))] font-semibold text-[var(--color-pwa-button-text,#ffffff)]"
className="flex min-h-(--pwa-button-height,44px) flex-auto cursor-pointer items-center justify-center rounded-(--radius-bottom-sheet,28px) border-0 bg-(--color-text-secondary,#9e9e9e) text-(length:--responsive-body,var(--font-size-lg,16px)) font-semibold text-(--color-pwa-button-text,#ffffff)"
onClick={onClose}
>
Cancel
</button>
<button
type="button"
className="flex min-h-[var(--pwa-button-height,44px)] flex-auto cursor-pointer items-center justify-center rounded-[var(--radius-bottom-sheet,28px)] border-0 bg-[linear-gradient(var(--color-button-gradient-start,#ff67e0),var(--color-button-gradient-end,#ff52a2))] text-[var(--responsive-body,var(--font-size-lg,16px))] font-semibold text-[var(--color-pwa-button-text,#ffffff)]"
className="flex min-h-(--pwa-button-height,44px) flex-auto cursor-pointer items-center justify-center rounded-(--radius-bottom-sheet,28px) border-0 bg-[linear-gradient(var(--color-button-gradient-start,#ff67e0),var(--color-button-gradient-end,#ff52a2))] text-(length:--responsive-body,var(--font-size-lg,16px)) font-semibold text-(--color-pwa-button-text,#ffffff)"
onClick={handleInstall}
>
OK
+2 -2
View File
@@ -15,10 +15,10 @@ export interface TextBubbleProps {
}
const BASE_CLASS_NAME =
"w-full self-stretch whitespace-pre-wrap break-words rounded-[var(--radius-xxl,24px)] px-[var(--spacing-4,16px)] py-[var(--spacing-3,12px)] text-[length:var(--chat-message-font-size,var(--responsive-body,16px))] leading-[var(--chat-message-line-height,1.5)] shadow-[var(--shadow-input-box,0_1px_2px_rgba(0,0,0,0.1))]";
"w-full self-stretch whitespace-pre-wrap break-words rounded-(--radius-xxl,24px) px-(--spacing-4,16px) py-(--spacing-3,12px) text-(length:--chat-message-font-size,var(--responsive-body,16px)) leading-(--chat-message-line-height,1.5) shadow-(--shadow-input-box,0_1px_2px_rgba(0,0,0,0.1))";
const AI_CLASS_NAME =
"rounded-tl-none bg-white text-[var(--color-text-foreground,#000)]";
"rounded-tl-none bg-white text-(--color-text-foreground,#000)";
const USER_CLASS_NAME =
"rounded-tr-none bg-[linear-gradient(to_right,var(--color-button-gradient-start,#ff67e0),var(--color-button-gradient-end,#ff52a2))] text-white";
@@ -17,37 +17,37 @@ export function VoiceUnlockOptionsDialog({
return (
<div
className="fixed inset-0 z-[72] flex items-center justify-center bg-[rgba(0,0,0,0.5)] pb-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-bottom,0px))] pl-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-left,0px))] pr-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-right,0px))] pt-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-top,0px))]"
className="fixed inset-0 z-72 flex items-center justify-center bg-[rgba(0,0,0,0.5)] pb-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-bottom,0px))] pl-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-left,0px))] pr-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-right,0px))] pt-[calc(var(--dialog-safe-margin,16px)+var(--app-safe-top,0px))]"
role="dialog"
aria-modal="true"
aria-labelledby="voice-unlock-options-title"
>
<div className="w-full max-w-[var(--dialog-max-width,380px)] rounded-[var(--responsive-card-radius,36px)] bg-[var(--color-page-background,#ffffff)] px-[var(--responsive-card-padding,18px)] pb-[var(--responsive-card-padding,18px)] pt-[var(--responsive-card-padding-lg,24px)] shadow-[0_18px_40px_rgba(0,0,0,0.16)]">
<div className="w-full max-w-(--dialog-max-width,380px) rounded-(--responsive-card-radius,36px) bg-(--color-page-background,#ffffff) px-(--responsive-card-padding,18px) pb-(--responsive-card-padding,18px) pt-(--responsive-card-padding-lg,24px) shadow-[0_18px_40px_rgba(0,0,0,0.16)]">
<h2
id="voice-unlock-options-title"
className="m-0 mb-[clamp(7px,1.481vw,8px)] text-center text-[var(--responsive-page-title,var(--font-size-22,22px))] font-bold leading-[1.2] text-[var(--color-text-foreground,#171717)]"
className="m-0 mb-[clamp(7px,1.481vw,8px)] text-center text-(length:--responsive-page-title,var(--font-size-22,22px)) font-bold leading-[1.2] text-(--color-text-foreground,#171717)"
>
Unlock voice message
</h2>
<p className="mx-[var(--spacing-md,12px)] mb-[var(--page-section-gap-lg,20px)] mt-0 text-center text-[var(--responsive-body,var(--font-size-md,14px))] leading-[1.5] text-[#393939]">
<p className="mx-(--spacing-md,12px) mb-(--page-section-gap-lg,20px) mt-0 text-center text-(length:--responsive-body,var(--font-size-md,14px)) leading-[1.5] text-[#393939]">
Choose how you would like to unlock this voice message.
</p>
<div className="flex flex-col gap-[var(--spacing-md,12px)]">
<div className="flex flex-col gap-(--spacing-md,12px)">
<button
type="button"
className="flex min-h-[var(--responsive-control-height,48px)] w-full cursor-pointer flex-col items-center justify-center gap-[var(--responsive-inline-gap-xs,4px)] rounded-[var(--radius-bottom-sheet,28px)] border-0 bg-[linear-gradient(90deg,#ff67e0_0%,#ff52a2_100%)] px-[var(--responsive-card-padding,16px)] py-[clamp(9px,1.852vw,10px)] text-[var(--responsive-body,var(--font-size-lg,16px))] font-bold text-white shadow-[0_5px_7px_rgba(247,89,168,0.24)] focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#f657a0]"
className="flex min-h-(--responsive-control-height,48px) w-full cursor-pointer flex-col items-center justify-center gap-(--responsive-inline-gap-xs,4px) rounded-(--radius-bottom-sheet,28px) border-0 bg-[linear-gradient(90deg,#ff67e0_0%,#ff52a2_100%)] px-(--responsive-card-padding,16px) py-[clamp(9px,1.852vw,10px)] text-(length:--responsive-body,var(--font-size-lg,16px)) font-bold text-white shadow-[0_5px_7px_rgba(247,89,168,0.24)] focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#f657a0]"
onClick={onBuyVoicePackage}
>
</button>
<button
type="button"
className="flex min-h-[var(--responsive-control-height,48px)] w-full cursor-pointer flex-col items-center justify-center gap-[var(--responsive-inline-gap-xs,4px)] rounded-[var(--radius-bottom-sheet,28px)] border border-transparent bg-[linear-gradient(#ffffff,#ffffff)_padding-box,linear-gradient(90deg,#ff67e0,#ff52a2)_border-box] px-[var(--responsive-card-padding,16px)] py-[clamp(9px,1.852vw,10px)] text-[var(--responsive-body,var(--font-size-lg,16px))] font-bold text-[#f657a0] focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#f657a0]"
className="flex min-h-(--responsive-control-height,48px) w-full cursor-pointer flex-col items-center justify-center gap-(--responsive-inline-gap-xs,4px) rounded-(--radius-bottom-sheet,28px) border border-transparent bg-[linear-gradient(#ffffff,#ffffff)_padding-box,linear-gradient(90deg,#ff67e0,#ff52a2)_border-box] px-(--responsive-card-padding,16px) py-[clamp(9px,1.852vw,10px)] text-(length:--responsive-body,var(--font-size-lg,16px)) font-bold text-[#f657a0] focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#f657a0]"
onClick={onActivateVip}
>
<span className="leading-[1.2]"> VIP</span>
<span className="text-[var(--responsive-caption,13px)] font-medium leading-[1.25] text-[#3c3b3b]">
<span className="text-(length:--responsive-caption,13px) font-medium leading-[1.25] text-[#3c3b3b]">
10+
</span>
</button>
@@ -55,7 +55,7 @@ export function VoiceUnlockOptionsDialog({
<button
type="button"
className="mt-[clamp(12px,2.593vw,14px)] min-h-[var(--responsive-control-height,48px)] w-full cursor-pointer rounded-[var(--radius-bottom-sheet,28px)] border-0 bg-[var(--color-text-secondary,#9e9e9e)] text-[var(--responsive-body,var(--font-size-lg,16px))] font-bold text-white focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#f657a0]"
className="mt-[clamp(12px,2.593vw,14px)] min-h-(--responsive-control-height,48px) w-full cursor-pointer rounded-(--radius-bottom-sheet,28px) border-0 bg-(--color-text-secondary,#9e9e9e) text-(length:--responsive-body,var(--font-size-lg,16px)) font-bold text-white focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#f657a0]"
onClick={onClose}
>
Cancel