diff --git a/src/widgets/space-focus-hud/ui/EndSessionConfirmModal.tsx b/src/widgets/space-focus-hud/ui/EndSessionConfirmModal.tsx
index eab2ef4..46b7a0b 100644
--- a/src/widgets/space-focus-hud/ui/EndSessionConfirmModal.tsx
+++ b/src/widgets/space-focus-hud/ui/EndSessionConfirmModal.tsx
@@ -36,21 +36,27 @@ export const EndSessionConfirmModal = ({
const endSessionCopy = copy.space.endSession;
const trimmedGoal = currentGoal.trim() || copy.space.focusHud.goalFallback;
+
const activeStage = completionResult
? completionResult.completionSource === 'manual-end'
? 'result-saved'
: 'result-success'
: stage;
+
const focusedMinutes = completionResult
? formatFocusedMinutes(completionResult.focusedSeconds)
: 0;
+
const hasThoughts = Boolean(completionResult && completionResult.thoughts.length > 0);
useEffect(() => {
if (!open) {
- setStage('decision');
- setIsSubmitting(false);
- return;
+ // Add a slight delay before resetting state so the fade-out animation can finish smoothly
+ const timer = setTimeout(() => {
+ setStage('decision');
+ setIsSubmitting(false);
+ }, 700);
+ return () => clearTimeout(timer);
}
const allowEscape = !isSubmitting && !completionResult;
@@ -67,10 +73,7 @@ export const EndSessionConfirmModal = ({
}, [completionResult, isSubmitting, onClose, open]);
const handleFinishHere = async () => {
- if (isSubmitting) {
- return;
- }
-
+ if (isSubmitting) return;
setIsSubmitting(true);
try {
await onFinishHere();
@@ -80,10 +83,7 @@ export const EndSessionConfirmModal = ({
};
const handleSaveAndReturn = async () => {
- if (isSubmitting) {
- return;
- }
-
+ if (isSubmitting) return;
setIsSubmitting(true);
try {
await onSaveAndReturn();
@@ -95,59 +95,70 @@ export const EndSessionConfirmModal = ({
return (
+ {/* Abyssal Backdrop: Direct filter animation to prevent WebKit blur pop-in */}
+ >
+
+
+
{activeStage === 'decision' ? (
-
-
+
+
{endSessionCopy.decision.eyebrow}
{endSessionCopy.decision.title}
-
-
+ {/* Volumetric Card */}
+
+
{endSessionCopy.decision.goalLabel}
-
{trimmedGoal}
+
{trimmedGoal}
+ {/* Primary CTA (The Pearl) */}
+
+ {/* Secondary CTA (Ghost Button) */}
@@ -157,7 +168,7 @@ export const EndSessionConfirmModal = ({
type="button"
onClick={onClose}
disabled={isSubmitting}
- className="mt-8 text-[12px] font-medium text-white/30 underline decoration-transparent underline-offset-4 transition-colors hover:text-white/70 hover:decoration-white/30 disabled:opacity-50"
+ className="mt-10 text-[11px] font-light uppercase tracking-widest text-white/20 transition-all duration-300 hover:text-white/60 disabled:opacity-50"
>
{endSessionCopy.decision.cancelButton}
@@ -165,26 +176,26 @@ export const EndSessionConfirmModal = ({
) : null}
{activeStage === 'unfinished-confirm' ? (
-
-
-
+
+
+
{endSessionCopy.unfinishedConfirm.eyebrow}
{endSessionCopy.unfinishedConfirm.title}
-
+
{endSessionCopy.unfinishedConfirm.description}
-
-
+
+
{endSessionCopy.decision.goalLabel}
-
{trimmedGoal}
+
{trimmedGoal}
@@ -192,7 +203,7 @@ export const EndSessionConfirmModal = ({
type="button"
onClick={onClose}
disabled={isSubmitting}
- className="flex-1 rounded-full border border-white/20 bg-black/40 px-6 py-4 text-[15px] font-medium text-white backdrop-blur-md transition-all hover:border-white/40 hover:bg-white/10 active:scale-95 disabled:opacity-50"
+ className="flex-1 rounded-full border border-white/10 bg-transparent px-6 py-4 text-[14px] font-light text-white/60 transition-all duration-500 hover:border-white/20 hover:bg-white/[0.03] hover:text-white/90 active:scale-[0.98] disabled:opacity-50"
>
{endSessionCopy.unfinishedConfirm.keepFocusingButton}
@@ -200,59 +211,85 @@ export const EndSessionConfirmModal = ({
type="button"
onClick={handleSaveAndReturn}
disabled={isSubmitting}
- className="flex-1 rounded-full bg-white px-6 py-4 text-[15px] font-semibold text-black shadow-[0_0_30px_rgba(255,255,255,0.18)] transition-all hover:scale-105 active:scale-95 disabled:opacity-50"
+ className="group relative flex-1 overflow-hidden rounded-full bg-white px-6 py-4 text-[14px] font-medium text-black transition-all duration-500 hover:scale-[1.02] hover:shadow-[0_0_40px_rgba(255,255,255,0.2)] active:scale-[0.98] disabled:opacity-50"
>
- {isSubmitting
- ? endSessionCopy.unfinishedConfirm.saveAndReturnPending
- : endSessionCopy.unfinishedConfirm.saveAndReturnButton}
+
+
+ {isSubmitting
+ ? endSessionCopy.unfinishedConfirm.saveAndReturnPending
+ : endSessionCopy.unfinishedConfirm.saveAndReturnButton}
+
) : null}
- {activeStage === 'result-success' && completionResult ? (
-
-
-
- {endSessionCopy.resultSuccess.eyebrow}
+ {(activeStage === 'result-success' || activeStage === 'result-saved') && completionResult ? (
+
+ {/* Stage dependent subtle glow */}
+
+
+
+ {activeStage === 'result-success' ? endSessionCopy.resultSuccess.eyebrow : endSessionCopy.resultSaved.eyebrow}
- {endSessionCopy.resultSuccess.title}
+ {activeStage === 'result-success' ? endSessionCopy.resultSuccess.title : endSessionCopy.resultSaved.title}
-
- {endSessionCopy.resultSuccess.description}
+
+ {activeStage === 'result-success' ? endSessionCopy.resultSuccess.description : endSessionCopy.resultSaved.description}
-
-
-
+
+ {/* The Hero Number */}
+
+
+
{endSessionCopy.resultSuccess.focusedLabel}
-
- {focusedMinutes}
- m
-
+
+
+
+ {focusedMinutes}
+ m
+
+
-
-
+ {activeStage === 'result-saved' && (
+
+
+ {endSessionCopy.resultSaved.goalStatusLabel}
+
+
+ {endSessionCopy.resultSaved.goalStatusValue}
+
+
+ )}
+
+
+
{endSessionCopy.resultSuccess.goalLabel}
-
+
{completionResult.goalText}
{hasThoughts ? (
-
-
-
+
+
+
{endSessionCopy.resultSuccess.thoughtsLabel}
-
+
{completionResult.thoughts.length}
@@ -260,9 +297,9 @@ export const EndSessionConfirmModal = ({
{completionResult.thoughts.map((thought) => (
@@ -272,94 +309,19 @@ export const EndSessionConfirmModal = ({
) : null}
-
-
- ) : null}
-
- {activeStage === 'result-saved' && completionResult ? (
-
-
-
- {endSessionCopy.resultSaved.eyebrow}
-
-
- {endSessionCopy.resultSaved.title}
-
-
- {endSessionCopy.resultSaved.description}
-
-
-
-
-
- {endSessionCopy.resultSaved.focusedLabel}
-
-
- {focusedMinutes}
- m
-
-
-
-
-
- {endSessionCopy.resultSaved.goalStatusLabel}
-
-
- {endSessionCopy.resultSaved.goalStatusValue}
-
-
-
-
-
- {endSessionCopy.resultSaved.goalLabel}
-
-
- {completionResult.goalText}
-
-
-
- {hasThoughts ? (
-
-
-
- {endSessionCopy.resultSaved.thoughtsLabel}
-
-
- {completionResult.thoughts.length}
-
-
-
- {completionResult.thoughts.map((thought) => (
-
- ))}
-
-
- ) : null}
+
+
-
-
+
) : null}
diff --git a/src/widgets/space-focus-hud/ui/InlineMicrostep.tsx b/src/widgets/space-focus-hud/ui/InlineMicrostep.tsx
index 5cbdde4..16722f4 100644
--- a/src/widgets/space-focus-hud/ui/InlineMicrostep.tsx
+++ b/src/widgets/space-focus-hud/ui/InlineMicrostep.tsx
@@ -13,6 +13,7 @@ export const InlineMicrostep = ({ microStep, isBusy, onUpdate }: InlineMicrostep
const [isEditing, setIsEditing] = useState(false);
const [draft, setDraft] = useState('');
const [isCompleting, setIsCompleting] = useState(false);
+ const [isFocused, setIsFocused] = useState(false);
const inputRef = useRef
(null);
const normalizedStep = microStep?.trim() || null;
@@ -31,6 +32,7 @@ export const InlineMicrostep = ({ microStep, isBusy, onUpdate }: InlineMicrostep
const cancelEditing = () => {
setIsEditing(false);
+ setIsFocused(false);
setDraft('');
};
@@ -61,86 +63,110 @@ export const InlineMicrostep = ({ microStep, isBusy, onUpdate }: InlineMicrostep
if (isBusy || isCompleting || !normalizedStep) return;
setIsCompleting(true);
- // Visual delay for the strikethrough animation before actually clearing it
+ // Visual delay for the evaporating strikethrough animation (Sensory Closure)
setTimeout(async () => {
await onUpdate(null);
setIsCompleting(false);
- }, 400);
+ }, 600);
};
- if (isEditing) {
- return (
-
-
setDraft(e.target.value)}
- onKeyDown={handleKeyDown}
- onBlur={() => void submitDraft()}
- disabled={isBusy}
- placeholder="Enter next small step..."
- className="flex-1 bg-transparent text-[15px] font-medium text-white outline-none placeholder:text-white/30 disabled:opacity-50"
- />
-
-
- );
- }
-
- if (normalizedStep) {
- return (
-
-
-
-
- );
- }
+ // Determine the primary physical state for morphing
+ const state = isEditing ? 'editing' : (normalizedStep ? 'active' : 'idle');
return (
-
+
+
+
+ {/* Content Layer 1: Idle (Just one small step) */}
+
+ +
+ Start with one small win
+
+
+ {/* Content Layer 2: Active (Has Step) */}
+
+
+
+
+
+ {/* Content Layer 3: Editing (The 'First Move') */}
+
+
setDraft(e.target.value)}
+ onKeyDown={handleKeyDown}
+ onFocus={() => setIsFocused(true)}
+ onBlur={() => {
+ setIsFocused(false);
+ void submitDraft();
+ }}
+ disabled={isBusy}
+ placeholder="What's the very first move?"
+ className="flex-1 bg-transparent text-[15.5px] font-light tracking-wide text-white outline-none placeholder:text-white/30 disabled:opacity-50 min-w-0"
+ />
+
+
+
+
+
);
};