The inline script's style.setProperty values on <html> were being
stripped during Vite's CSS injection. Switch to injecting a <style>
tag with :root vars which persists in the DOM. Restore CSS defaults
as safety fallback. Update useTheme to sync both the style tag and
inline styles when settings load.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Guard useTheme effect to skip when settings are undefined, preventing
it from overwriting the inline script's cached color with cyan defaults.
Move CSS accent var defaults from index.css :root into the index.html
inline script so they are always set synchronously before paint.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Gate dashboard rendering on isLockResolved to prevent content flash
before lock state is known. Remove animate-fade-in from LockOverlay
so it renders instantly. Always write accent color to localStorage
(even default cyan) to prevent theme flash on reload. Resolve lock
state on auth query error to avoid permanent blank screen. Lift
mobileOpen state above lock gate to survive lock/unlock cycles.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Critical: Lock state was purely React useState — refreshing the page reset it.
Now persisted server-side via is_locked/locked_at columns on user_sessions.
POST /auth/lock sets the flag, /auth/verify-password clears it, and
GET /auth/status returns is_locked so the frontend initializes correctly.
UI: Cache accent color in localStorage and apply via inline script in
index.html before React hydrates to eliminate the cyan flash on load.
UI: Increase TanStack Query gcTime from 5min to 30min so page data
survives component unmount/remount across tab switches without skeleton.
UI: Move Projects nav onClick from the icon element to the full-width
container div so the entire row is clickable when the sidebar is collapsed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
S-02: Confirmed drift-3 is used by auth/AmbientBackground — not dead code.
S-03: Extracted noise SVG data URI to module-level NOISE_SVG constant.
S-04: Added will-change: transform to drift orbs for GPU layer promotion.
S-05: Documented the 9 AM snooze default in getMinutesUntilTomorrowMorning.
S-06: Made calendar toolbar bg-card/95 with backdrop-blur-md for better
readability over the transparent FullCalendar grid.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
W-02: Renamed layout/AmbientBackground → AppAmbientBackground to avoid
naming collision with auth/AmbientBackground (IDE auto-import confusion).
S-01: Added visibilitychange listener to re-sync clock after tab
sleep/resume. Previously the interval would drift after laptop sleep
or long tab backgrounding.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clock: Instead of starting a 60s interval from mount time (which drifts
from the system clock), calculate ms until the next :00 second mark,
setTimeout to that point, then setInterval every 60s from there.
Updated text: Replaced formatDistanceToNow (which flickered between
"less than a minute ago" / "a minute ago" / "2 minutes ago" on each
render) with a stable minute-based calculation derived from clockNow:
"just now" / "1 min ago" / "N min ago".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Displays current time before the date separated by a vertical bar:
"6:30 PM | Thursday, March 12, 2026". Updates every 60 seconds.
Uses tabular-nums for stable digit widths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Border was at 30% opacity — nearly invisible against the glassmorphic
card background. Restored to full border-border opacity for clear
section separation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Removed bg-card from sticky headers (was creating opaque bars against
glassmorphic card background)
- Reduced padding from pb-1.5 to py-0.5 for slimmer profile
- Added leading-none for proper vertical centering of chevron + text
- Softened border opacity to 30%, text to 70%, chevron to 60%
- Shrunk text from text-xs to text-[10px], chevron from h-3 to h-2.5
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The mobile view Select had md:hidden on the <select> element, but the
Select component wraps it in a <div> with an absolute ChevronDown icon
that remained visible. Moved md:hidden to a wrapper div so the entire
Select (including the chevron) is hidden on desktop.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The outline variant used bg-background (opaque near-black) which created
a visible dark rectangle against semi-transparent card toolbars. Changed
to bg-transparent so outline buttons blend with their parent container.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
FullCalendar backgrounds were opaque while the toolbar used semi-transparent
glassmorphism cards, creating a patchy look. Now all FC elements match:
- Page background: transparent (ambient shows through grid)
- Column headers: semi-transparent (0.65 opacity)
- Neutral background: semi-transparent (0.65 opacity)
- More-popover: semi-transparent with backdrop blur
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Moved ambient from DashboardPage to AppLayout so all pages get the
drifting gradient effect, not just the dashboard
- Lightened card colors: --card 5% → 8%, --card-elevated 7% → 11%,
popover and FullCalendar backgrounds updated to match
- Renamed DashboardAmbient → AmbientBackground in layout/
- Glassmorphism class renamed dashboard-glass → ambient-glass,
applied at AppLayout content wrapper level
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drift animations translate orbs up to 80px, causing hard cutoff at
container edges. Giving orb layers inset: -100px provides enough
bleed room so the gradient edges are always beyond the overflow-hidden
boundary.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Orbs repositioned centrally with larger ellipses (90%/80%) and higher
opacity (0.45/0.35) so glow is visible through glassmorphism cards
- Vignette reduced from 0.45 to 0.30, transparent zone expanded to 50%
- Card opacity reduced from 0.80 to 0.65 to let more ambient bleed through
- Added overflow-hidden on ambient container to prevent black bar artifacts
during drift animations
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Blurred circle approach was invisible on near-black backgrounds.
Use radial-gradient orbs at 25%/15% opacity instead, with semi-transparent
cards (backdrop-filter: blur) so the ambient effect shows through.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Negative z-index (-z-10) placed orbs behind the body's opaque background,
making them invisible. Moved all ambient layers (orbs, noise texture,
vignette) into the DashboardAmbient component as absolute-positioned
children at z-0, with content at z-10. Boosted orb opacities to 12%/7%
for perceptible effect. Removed CSS pseudo-element approach in favor of
inline React elements for better stacking control.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three layered effects to make the dashboard feel alive:
1. DashboardAmbient: two accent-colored drifting orbs at very low opacity
(0.04/0.025) with 120px blur — subtle depth and movement
2. Noise texture + radial vignette via CSS pseudo-elements — breaks the
flat digital surface and draws focus to center content
3. Card breathe animation: data-driven 4s pulsing glow on CalendarWidget
(when event in progress) and TodoWidget (when overdue todos exist)
All effects respect prefers-reduced-motion, use accent CSS vars (works
with any user-chosen accent color), and are GPU-composited (transform +
opacity only) for negligible performance cost.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The type pill, time label, and priority pill were being removed on
hover and replaced with action buttons, causing layout reflow and
visible jitter. Now the labels stay rendered (invisible when hovered
for todos/reminders) to hold their space, and action buttons are
absolutely positioned on top. Events show no actions so their labels
stay visible on hover. Zero layout shift.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap TodoWidget in flex-1 container and add h-full to its Card so
the Upcoming Todos card stretches to fill remaining space in the
right column, keeping both columns visually aligned.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace fixed maxHeight 520px with h-full + overflow-hidden so the
card stretches to match the right column height in the grid row.
The flex chain (Card flex-col → CardContent flex-1 min-h-0 →
ScrollArea flex-1 min-h-0) ensures content scrolls internally
within the row-determined height instead of capping independently.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
C-01: Add 30-day lower bound on overdue todo/reminder queries to
prevent fetching entire history.
C-02: Remove dead include_past query param — past-event filtering
is handled client-side.
W-01: Add onError toast handlers to all three inline mutations.
W-02: Snooze dropdown opens upward (bottom-full) to avoid clipping
inside the ScrollArea overflow container.
S-06: Clamp getMinutesUntilTomorrowMorning() to max 1440 to stay
within ReminderSnooze schema bounds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: h-full on Card inside a flex-col parent with no explicit
height meant nothing constrained the card — ScrollArea max-h never
triggered overflow. Fix: Card uses maxHeight 520px as the outer cap,
flex-col layout with shrink-0 header, and min-h-0 on CardContent +
ScrollArea so the flex chain allows content to shrink and scroll.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The flex-col h-full layout caused the card to stretch to match the
grid row, pushing content beyond the ScrollArea max-height. Switched
to natural card height with max-h-[400px] on ScrollArea so the card
stays compact and scrolls internally without mismatching the right
column cards.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Restore max-h-[400px] on ScrollArea so the widget caps and scrolls
instead of growing unbounded and making cards uneven. All-day events
now show "All day" instead of the misleading "12:00 AM" time.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
/8 is not in Tailwind's default opacity scale so the classes were
purged. Use /[0.08] arbitrary value syntax instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Removes the always-visible 2px colored left border from each row.
On hover, the row background now glows with the type color at 8%
opacity (blue for todos, purple for events, orange for reminders).
Cleaner at rest, still provides type recognition on interaction.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend: Include overdue todos and snoozed reminders in /upcoming response,
add end_datetime/snoozed_until/is_overdue fields, widen snooze schema to
accept 1-1440 minutes for 1h/3h/tomorrow options.
Frontend: Full UpcomingWidget rewrite with sticky day separators (Today
highlighted in accent), collapsible groups, past-event toggle, focus mode
(Today + Tomorrow), color-coded left borders, compact type pills, relative
time for today's items, item count badge, and inline quick actions (complete
todo, snooze/dismiss reminder on hover). Card fills available height with
no dead space. DashboardPage always renders widget (no duplicate empty state).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace 895-line monolith with 5 focused tab components (Profile,
Appearance, Social, Security, Integrations) mirroring AdminPortal's
tab pattern. URL deep linking via ?tab= search param. Conditional
rendering prevents unmounted tabs from firing API calls.
Reviewed by senior-code-reviewer, senior-ui-designer, and
security-penetration-tester agents — all findings actioned.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Nav: justify-evenly on mobile, justify-start on desktop
- Title: "Admin Portal" on desktop, "Admin" on mobile
- Restore mr-6 spacing on title group for desktop
- Tab labels: icon-only on mobile, icon+label on sm+
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- C-01: Simplify EntityTable sort dropdown to toggle-based (select
column, re-select to flip direction), add aria-label
- W-01: Convert CalendarPage mobile overlay to MobileDetailOverlay
- W-02: Use ref for onClose in MobileDetailOverlay to prevent
listener churn from inline arrow functions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- S-01/W-06/S-02/S-04: Extract MobileDetailOverlay shared component
with Escape key, body scroll lock, and ARIA dialog attributes.
Refactored Todos, Reminders, People, Locations, ProjectDetail.
- W-02: Add specificity contract comment to mobile-scale CSS
- W-03: Enforce 10px floor for text-[9px] on mobile
- W-05: Add sort dropdown to EntityTable mobile card view
- S-03: Export MOBILE/DESKTOP breakpoint constants from useMediaQuery,
updated all 8 consumer files to use constants
- S-06: Bump KanbanBoard TouchSensor tolerance from 5 to 8
- S-07: Hover state audit — no action needed, hoverOnlyWhenSupported
in Tailwind config already handles touch devices correctly
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace CSS-only panel hiding with isDesktop media query guard
in Todos, Reminders, People, Locations, ProjectDetail (W-01)
- Add touch-action: manipulation for mobile interactive elements (W-04)
- Bump FullCalendar more-link from 0.55rem to 0.625rem (W-07)
- Add aria-label on admin portal tab NavLinks (S-05)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Tab nav: scroll isolation, icon-only on mobile, accessible titles
- IAM table: hide 6 columns on mobile, responsive padding
- User detail: responsive grid (1→2→3 cols), role select sizing
- Dashboard: responsive stats grid, hide Actor/Target cols on mobile
- Audit log: responsive column hiding and padding
- Actions menu: role submenu repositions below trigger on mobile
- Config: narrower filter select on mobile
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reduce header gap to gap-2 on mobile, add min-w-0 so title can
shrink properly, hide status badge on small screens, and add
shrink-0 to action buttons to prevent them from compressing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hide verbose metadata columns (status badge, priority badge, date,
subtask count) on mobile and replace with compact priority dot +
overdue indicator. Reduce subtask indent and stack project summary
card vertically on small screens.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add pr-8 to mobile view Select to prevent text clipping under chevron.
Add min-w-0 flex-shrink to calendar title h2 to prevent nav arrow overlap.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add dark-themed FullCalendar "+more" popover with CSS X close button
(replaces broken font icon). Add pr-8 to all mobile Select dropdowns
to prevent text clipping under chevron. Normalize header gap to
gap-2 md:gap-4 across all page headers for tighter mobile layout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Scale down all content text on mobile via .mobile-scale CSS class (excludes
navbar/UMBRA title). Hide calendar event times in month view (Google Calendar
style). Restructure CategoryFilterBar so categories display on a separate row
when toggled instead of being hidden behind the search bar. Reduce dashboard
widget density with hidden badges and tighter spacing on small screens.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Page titles: text-xl on mobile, text-2xl on desktop (7 pages)
- Stat cards: reduce padding/gap on mobile, hide icons below sm (3 pages)
- TodoItem: two-line layout on mobile (title row + metadata row)
- ReminderItem: same two-line treatment
- FullCalendar: smaller event font/padding on mobile via CSS media query
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The import was added but the sensors config replacement failed silently
due to line ending mismatch. TouchSensor now properly registered with
200ms delay / 5px tolerance alongside PointerSensor.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3a. CalendarSidebar mobile collapse:
- Desktop sidebar + resize handle hidden below lg breakpoint
- Mobile Sheet overlay with PanelLeft toggle in toolbar
- Template selection closes mobile sidebar automatically
3b. KanbanBoard touch support:
- TouchSensor added alongside PointerSensor (200ms delay)
- Column min-width reduced on mobile (160px vs 200px)
- iOS smooth scroll enabled on horizontal container
3c. EntityTable mobile card view:
- mobileCardRender optional prop renders cards instead of table on mobile
- PeoplePage: card with name, category, email, phone
- LocationsPage: card with name, category, address
- TodosPage/RemindersPage use custom list components, not EntityTable
3d. DatePicker mobile bottom sheet:
- Renders as full-width bottom sheet on mobile (< 768px)
- Safe area inset padding for iOS home indicator
- Desktop positioned dropdown unchanged
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- EntityTable: add useMediaQuery hook, mobileCardRender prop, and mobile card path
that replaces the table on screens <768px when a renderer is provided
- PeoplePage: add mobileCardRender showing name, category, email, phone
- LocationsPage: add mobileCardRender showing name, category, address
Note: TodosPage and RemindersPage use custom list components (TodoList,
ReminderList), not EntityTable directly — no changes needed there.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>