chore: run Tailwind v4 upgrade tool (Phase 1)

- Upgraded tailwindcss v3 → v4.2.1, postcss plugin to @tailwindcss/postcss
- Deleted tailwind.config.js, migrated theme to CSS @theme block in index.css
- Replaced @tailwind directives with @import 'tailwindcss'
- Added @custom-variant dark, @utility blocks for custom utilities
- Updated class names across 128 files (shadow-sm → shadow-xs, etc.)
- Removed autoprefixer (built into v4)
- Added migration plan doc

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Michael Chihlas
2026-03-07 20:00:38 -05:00
parent 732ccba966
commit a332a9ebab
129 changed files with 2068 additions and 1536 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -43,6 +43,7 @@
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@tailwindcss/postcss": "^4.2.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
@@ -50,14 +51,13 @@
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"autoprefixer": "^10.4.23",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"jsdom": "^28.0.0",
"postcss": "^8.5.6",
"tailwindcss": "^3.4.19",
"tailwindcss": "^4.2.1",
"typescript": "~5.9.3",
"typescript-eslint": "^8.46.4",
"vite": "^7.2.4",

View File

@@ -1,6 +1,5 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
'@tailwindcss/postcss': {},
},
}

View File

@@ -2,7 +2,7 @@ import { Outlet } from 'react-router-dom'
export function AccountLayout() {
return (
<div className="container mx-auto max-w-screen-lg px-4 py-6">
<div className="container mx-auto max-w-(--breakpoint-lg) px-4 py-6">
<Outlet />
</div>
)

View File

@@ -56,7 +56,7 @@ export function DeleteAccountModal({ onClose }: Props) {
required
className={cn(
'mt-1 block w-full rounded-[10px] border border-border bg-card px-3 py-2',
'text-foreground focus:border-primary focus:outline-none'
'text-foreground focus:border-primary focus:outline-hidden'
)}
/>
</div>
@@ -69,7 +69,7 @@ export function DeleteAccountModal({ onClose }: Props) {
onClick={onClose}
className={cn(
'rounded-[10px] px-4 py-2 text-sm font-medium',
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-foreground'
'bg-[rgba(255,255,255,0.04)] border border-brand-border text-foreground'
)}
>
Cancel

View File

@@ -45,7 +45,7 @@ export function LeaveAccountModal({ accountName, onClose }: Props) {
onClick={onClose}
className={cn(
'rounded-[10px] px-4 py-2 text-sm font-medium',
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-foreground'
'bg-[rgba(255,255,255,0.04)] border border-brand-border text-foreground'
)}
>
Cancel

View File

@@ -60,7 +60,7 @@ export function TransferOwnershipModal({ members, onClose, onTransferred }: Prop
onChange={(e) => setTargetUserId(e.target.value)}
className={cn(
'mt-1 block w-full rounded-[10px] border border-border bg-card px-3 py-2',
'text-foreground focus:border-primary focus:outline-none'
'text-foreground focus:border-primary focus:outline-hidden'
)}
>
{nonOwnerMembers.map((m) => (
@@ -77,7 +77,7 @@ export function TransferOwnershipModal({ members, onClose, onTransferred }: Prop
required
className={cn(
'mt-1 block w-full rounded-[10px] border border-border bg-card px-3 py-2',
'text-foreground focus:border-primary focus:outline-none'
'text-foreground focus:border-primary focus:outline-hidden'
)}
/>
</div>
@@ -90,7 +90,7 @@ export function TransferOwnershipModal({ members, onClose, onTransferred }: Prop
onClick={onClose}
className={cn(
'rounded-[10px] px-4 py-2 text-sm font-medium',
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-foreground'
'bg-[rgba(255,255,255,0.04)] border border-brand-border text-foreground'
)}
>
Cancel
@@ -100,7 +100,7 @@ export function TransferOwnershipModal({ members, onClose, onTransferred }: Prop
disabled={isSubmitting || !password}
className={cn(
'rounded-[10px] px-4 py-2 text-sm font-semibold',
'bg-amber-500 text-[#101114] hover:bg-amber-400',
'bg-amber-500 text-brand-dark hover:bg-amber-400',
'disabled:opacity-50'
)}
>

View File

@@ -36,7 +36,7 @@ export function AdminLayout() {
return (
<div className="flex h-full">
{/* Desktop sidebar */}
<div className="hidden w-60 flex-shrink-0 border-r border-border bg-card md:block">
<div className="hidden w-60 shrink-0 border-r border-border bg-card md:block">
<AdminSidebar />
</div>
@@ -44,7 +44,7 @@ export function AdminLayout() {
{mobileOpen && (
<div className="fixed inset-0 z-40 md:hidden">
<div
className="absolute inset-0 bg-card/80 backdrop-blur-sm"
className="absolute inset-0 bg-card/80 backdrop-blur-xs"
onClick={() => setMobileOpen(false)}
/>
<div className="absolute inset-y-0 left-0 w-60 border-r border-border bg-card shadow-xl">
@@ -63,7 +63,7 @@ export function AdminLayout() {
{/* Content */}
<div className="flex-1 overflow-y-auto">
<div className="mx-auto max-w-screen-2xl p-6">
<div className="mx-auto max-w-(--breakpoint-2xl) p-6">
{/* Mobile menu button */}
<button
onClick={() => setMobileOpen(true)}

View File

@@ -58,7 +58,7 @@ export function CreateCategoryModal({
}
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm">
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs">
<div className="w-full max-w-md bg-card border border-border rounded-xl p-6 shadow-lg">
{/* Header */}
<div className="mb-4 flex items-center justify-between">
@@ -98,7 +98,7 @@ export function CreateCategoryModal({
className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50'
)}
/>
@@ -122,7 +122,7 @@ export function CreateCategoryModal({
className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50'
)}
/>

View File

@@ -67,7 +67,7 @@ export function EditCategoryModal({
}
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm">
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs">
<div className="w-full max-w-md bg-card border border-border rounded-xl p-6 shadow-lg">
{/* Header */}
<div className="mb-4 flex items-center justify-between">
@@ -107,7 +107,7 @@ export function EditCategoryModal({
className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50'
)}
/>
@@ -131,7 +131,7 @@ export function EditCategoryModal({
className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50'
)}
/>

View File

@@ -48,7 +48,7 @@ export function SearchInput({ value = '', onSearch, placeholder = 'Search...', c
placeholder={placeholder}
className={cn(
'h-9 w-full rounded-md border border-border bg-card pl-9 pr-8 text-sm text-foreground',
'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
)}
/>
{localValue && (

View File

@@ -77,7 +77,7 @@ export function FlowAnalyticsPanel({ treeId }: FlowAnalyticsPanelProps) {
<select
value={period}
onChange={(e) => setPeriod(e.target.value as AnalyticsPeriod)}
className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-ring"
className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:outline-hidden focus:ring-1 focus:ring-ring"
>
{PERIOD_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>

View File

@@ -29,7 +29,7 @@ export function ChatMessage({ role, content, suggestedFlows }: ChatMessageProps)
className={`rounded-2xl px-4 py-3 text-[0.875rem] leading-relaxed ${
role === 'user'
? 'bg-primary/15 text-foreground'
: 'bg-[rgba(255,255,255,0.04)] text-foreground border border-[rgba(255,255,255,0.06)]'
: 'bg-[rgba(255,255,255,0.04)] text-foreground border border-brand-border'
}`}
>
<MarkdownContent content={content} className="text-[0.875rem] leading-relaxed" />
@@ -38,7 +38,7 @@ export function ChatMessage({ role, content, suggestedFlows }: ChatMessageProps)
{/* Suggested flows (assistant only) */}
{role === 'assistant' && suggestedFlows && suggestedFlows.length > 0 && (
<div className="space-y-1.5">
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
<span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
Related Flows
</span>
{suggestedFlows.map(flow => (

View File

@@ -31,7 +31,7 @@ export function ChatSidebar({
<div className="px-4 py-3 border-b shrink-0" style={{ borderColor: 'var(--glass-border)' }}>
<button
onClick={onNewChat}
className="w-full flex items-center justify-center gap-2 bg-gradient-brand text-[#101114] font-semibold text-sm rounded-[10px] px-4 py-2.5 hover:opacity-90 active:scale-[0.97] transition-all"
className="w-full flex items-center justify-center gap-2 bg-gradient-brand text-brand-dark font-semibold text-sm rounded-[10px] px-4 py-2.5 hover:opacity-90 active:scale-[0.97] transition-all"
>
<Plus size={16} />
New Chat
@@ -42,7 +42,7 @@ export function ChatSidebar({
<div className="flex-1 overflow-y-auto py-2">
{pinnedChats.length > 0 && (
<div className="px-3 mb-1">
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
<span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
Pinned
</span>
</div>

View File

@@ -136,7 +136,7 @@ export function ConcludeSessionModal({
<div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */}
<div
className="absolute inset-0 bg-black/60 backdrop-blur-sm"
className="absolute inset-0 bg-black/60 backdrop-blur-xs"
onClick={onClose}
/>
@@ -169,7 +169,7 @@ export function ConcludeSessionModal({
</div>
<button
onClick={onClose}
className="p-2 rounded-lg hover:bg-[rgba(255,255,255,0.06)] text-muted-foreground hover:text-foreground transition-colors"
className="p-2 rounded-lg hover:bg-brand-border text-muted-foreground hover:text-foreground transition-colors"
>
<X size={18} />
</button>
@@ -188,7 +188,7 @@ export function ConcludeSessionModal({
'w-8 h-px',
step === s || (i === 1 && step === 'summary') || (i === 2 && step === 'summary')
? 'bg-primary/40'
: 'bg-[rgba(255,255,255,0.06)]'
: 'bg-brand-border'
)}
/>
)}
@@ -196,10 +196,10 @@ export function ConcludeSessionModal({
className={cn(
'w-6 h-6 rounded-full flex items-center justify-center text-[0.6875rem] font-label font-medium transition-colors',
step === s
? 'bg-gradient-brand text-[#101114]'
? 'bg-gradient-brand text-brand-dark'
: (i < ['select-outcome', 'add-notes', 'summary'].indexOf(step))
? 'bg-primary/20 text-primary'
: 'bg-[rgba(255,255,255,0.06)] text-muted-foreground'
: 'bg-brand-border text-muted-foreground'
)}
>
{i + 1}
@@ -233,7 +233,7 @@ export function ConcludeSessionModal({
className={cn(
'w-full flex items-center gap-4 p-4 rounded-xl border transition-all text-left',
'hover:scale-[1.01] active:scale-[0.99]',
'bg-[rgba(255,255,255,0.02)] border-[rgba(255,255,255,0.06)]',
'bg-[rgba(255,255,255,0.02)] border-brand-border',
'hover:border-[rgba(255,255,255,0.12)] hover:bg-[rgba(255,255,255,0.04)]'
)}
>
@@ -268,7 +268,7 @@ export function ConcludeSessionModal({
</div>
<div>
<label className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground block mb-2">
<label className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground block mb-2">
Additional Notes (optional)
</label>
<textarea
@@ -282,7 +282,7 @@ export function ConcludeSessionModal({
: 'What still needs to be done, where you left off...'
}
rows={4}
className="w-full resize-none rounded-xl border bg-card text-foreground text-sm placeholder:text-muted-foreground px-4 py-3 focus:outline-none focus:border-[rgba(6,182,212,0.3)]"
className="w-full resize-none rounded-xl border bg-card text-foreground text-sm placeholder:text-muted-foreground px-4 py-3 focus:outline-hidden focus:border-[rgba(6,182,212,0.3)]"
style={{ borderColor: 'var(--glass-border)' }}
/>
</div>
@@ -312,7 +312,7 @@ export function ConcludeSessionModal({
style={{ borderColor: 'var(--glass-border)' }}
>
<div className="flex items-center justify-between mb-3">
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground flex items-center gap-1.5">
<span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground flex items-center gap-1.5">
<Sparkles size={10} className="text-primary" />
Generated Ticket Notes
</span>
@@ -335,7 +335,7 @@ export function ConcludeSessionModal({
<div />
<button
onClick={onClose}
className="px-4 py-2 rounded-[10px] text-sm text-muted-foreground hover:text-foreground bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] hover:border-[rgba(255,255,255,0.12)] transition-all"
className="px-4 py-2 rounded-[10px] text-sm text-muted-foreground hover:text-foreground bg-[rgba(255,255,255,0.04)] border border-brand-border hover:border-[rgba(255,255,255,0.12)] transition-all"
>
Cancel
</button>
@@ -346,14 +346,14 @@ export function ConcludeSessionModal({
<>
<button
onClick={() => setStep('select-outcome')}
className="px-4 py-2 rounded-[10px] text-sm text-muted-foreground hover:text-foreground bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] hover:border-[rgba(255,255,255,0.12)] transition-all"
className="px-4 py-2 rounded-[10px] text-sm text-muted-foreground hover:text-foreground bg-[rgba(255,255,255,0.04)] border border-brand-border hover:border-[rgba(255,255,255,0.12)] transition-all"
>
Back
</button>
<button
onClick={handleGenerate}
disabled={generating}
className="flex items-center gap-2 bg-gradient-brand text-[#101114] font-semibold text-sm rounded-[10px] px-5 py-2.5 hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-50"
className="flex items-center gap-2 bg-gradient-brand text-brand-dark font-semibold text-sm rounded-[10px] px-5 py-2.5 hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-50"
>
{generating ? (
<>
@@ -390,7 +390,7 @@ export function ConcludeSessionModal({
'flex items-center gap-2 px-4 py-2.5 rounded-[10px] text-sm font-semibold transition-all',
copied
? 'bg-emerald-400/15 text-emerald-400 border border-emerald-400/30'
: 'bg-gradient-brand text-[#101114] hover:opacity-90 active:scale-[0.97]'
: 'bg-gradient-brand text-brand-dark hover:opacity-90 active:scale-[0.97]'
)}
>
{copied ? (
@@ -407,7 +407,7 @@ export function ConcludeSessionModal({
</button>
<button
onClick={onClose}
className="px-4 py-2.5 rounded-[10px] text-sm text-muted-foreground hover:text-foreground bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] hover:border-[rgba(255,255,255,0.12)] transition-all"
className="px-4 py-2.5 rounded-[10px] text-sm text-muted-foreground hover:text-foreground bg-[rgba(255,255,255,0.04)] border border-brand-border hover:border-[rgba(255,255,255,0.12)] transition-all"
>
Done
</button>

View File

@@ -87,7 +87,7 @@ export function ContextMenu({ position, items, onClose }: ContextMenuProps) {
'flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm transition-colors',
item.variant === 'danger'
? 'text-rose-400 hover:bg-rose-500/10'
: 'text-foreground hover:bg-[rgba(255,255,255,0.06)]'
: 'text-foreground hover:bg-brand-border'
)}
>
{item.icon && (

View File

@@ -74,7 +74,7 @@ export function CreateFlowDropdown({
{showMenu && (
<>
<div className="fixed inset-0 z-10" onClick={() => setShowMenu(false)} />
<div className="absolute right-0 z-20 mt-1 w-64 rounded-lg border border-border bg-card p-1 shadow-xl backdrop-blur-sm">
<div className="absolute right-0 z-20 mt-1 w-64 rounded-lg border border-border bg-card p-1 shadow-xl backdrop-blur-xs">
{/* Troubleshooting */}
<Link
to="/trees/new"

View File

@@ -125,7 +125,7 @@ export function Modal({ isOpen, onClose, title, children, footer, size = 'md', a
>
{/* Backdrop */}
<div
className="absolute inset-0 bg-black/80 backdrop-blur-sm"
className="absolute inset-0 bg-black/80 backdrop-blur-xs"
onClick={onClose}
aria-hidden="true"
/>
@@ -139,13 +139,13 @@ export function Modal({ isOpen, onClose, title, children, footer, size = 'md', a
isFullScreen
? 'fixed inset-4 max-w-none w-auto h-auto rounded-2xl'
: cn(
'max-h-[100vh] rounded-t-2xl sm:max-h-[85vh] sm:rounded-2xl',
'max-h-screen rounded-t-2xl sm:max-h-[85vh] sm:rounded-2xl',
sizeClasses[size]
)
)}
>
{/* Header - Fixed at top */}
<div className="flex flex-shrink-0 items-center justify-between border-b border-border px-4 py-3 sm:px-6 sm:py-4">
<div className="flex shrink-0 items-center justify-between border-b border-border px-4 py-3 sm:px-6 sm:py-4">
<h2 id="modal-title" className="text-lg font-semibold text-foreground">
{title}
</h2>
@@ -168,7 +168,7 @@ export function Modal({ isOpen, onClose, title, children, footer, size = 'md', a
className={cn(
'rounded-md p-1.5 text-muted-foreground transition-colors sm:p-1',
'hover:bg-accent hover:text-foreground',
'focus:outline-none focus:ring-2 focus:ring-primary/20'
'focus:outline-hidden focus:ring-2 focus:ring-primary/20'
)}
aria-label="Close modal"
>
@@ -184,7 +184,7 @@ export function Modal({ isOpen, onClose, title, children, footer, size = 'md', a
{/* Footer - Fixed at bottom */}
{footer && (
<div className="flex-shrink-0 border-t border-border px-4 py-3 sm:px-6 sm:py-4">
<div className="shrink-0 border-t border-border px-4 py-3 sm:px-6 sm:py-4">
{footer}
</div>
)}

View File

@@ -186,7 +186,7 @@ export function TagInput({
className={cn(
'flex-1 min-w-[80px] border-0 bg-transparent px-1 py-0.5 text-sm text-foreground',
'placeholder:text-muted-foreground',
'focus:outline-none focus:ring-0'
'focus:outline-hidden focus:ring-0'
)}
/>
)}

View File

@@ -108,7 +108,7 @@ export function CopilotPanel({ isOpen, onClose, treeId, sessionId, currentNodeId
</div>
<button
onClick={onClose}
className="p-1.5 rounded-lg hover:bg-[rgba(255,255,255,0.06)] text-muted-foreground hover:text-foreground transition-colors"
className="p-1.5 rounded-lg hover:bg-brand-border text-muted-foreground hover:text-foreground transition-colors"
>
<X size={16} />
</button>
@@ -122,7 +122,7 @@ export function CopilotPanel({ isOpen, onClose, treeId, sessionId, currentNodeId
className={`max-w-[85%] rounded-xl px-3.5 py-2.5 text-[0.8125rem] leading-relaxed ${
msg.role === 'user'
? 'bg-primary/15 text-foreground'
: 'bg-[rgba(255,255,255,0.04)] text-foreground border border-[rgba(255,255,255,0.06)]'
: 'bg-[rgba(255,255,255,0.04)] text-foreground border border-brand-border'
}`}
>
<MarkdownContent content={msg.content} className="text-[0.8125rem] leading-relaxed" />
@@ -131,7 +131,7 @@ export function CopilotPanel({ isOpen, onClose, treeId, sessionId, currentNodeId
))}
{loading && (
<div className="flex justify-start">
<div className="bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] rounded-xl px-3.5 py-2.5">
<div className="bg-[rgba(255,255,255,0.04)] border border-brand-border rounded-xl px-3.5 py-2.5">
<Loader2 size={16} className="animate-spin text-primary" />
</div>
</div>
@@ -140,7 +140,7 @@ export function CopilotPanel({ isOpen, onClose, treeId, sessionId, currentNodeId
{/* Suggested flows */}
{suggestedFlows.length > 0 && (
<div className="space-y-2 pt-2">
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
<span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
Related Flows
</span>
{suggestedFlows.map(flow => (
@@ -162,14 +162,14 @@ export function CopilotPanel({ isOpen, onClose, treeId, sessionId, currentNodeId
onKeyDown={handleKeyDown}
placeholder="Ask about this step..."
rows={1}
className="flex-1 resize-none rounded-xl border bg-card text-foreground text-[0.8125rem] placeholder:text-muted-foreground px-3.5 py-2.5 focus:outline-none focus:border-[rgba(6,182,212,0.3)]"
className="flex-1 resize-none rounded-xl border bg-card text-foreground text-[0.8125rem] placeholder:text-muted-foreground px-3.5 py-2.5 focus:outline-hidden focus:border-[rgba(6,182,212,0.3)]"
style={{ borderColor: 'var(--glass-border)' }}
disabled={loading || initializing}
/>
<button
onClick={handleSend}
disabled={!input.trim() || loading || initializing}
className="bg-gradient-brand text-[#101114] p-2.5 rounded-xl hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-40"
className="bg-gradient-brand text-brand-dark p-2.5 rounded-xl hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-40"
>
<Send size={16} />
</button>

View File

@@ -11,7 +11,7 @@ export function CopilotToggle({ isOpen, onToggle }: CopilotToggleProps) {
return (
<button
onClick={onToggle}
className="fixed bottom-6 right-6 z-40 bg-gradient-brand text-[#101114] p-3.5 rounded-full shadow-lg shadow-primary/30 hover:opacity-90 active:scale-[0.97] transition-all"
className="fixed bottom-6 right-6 z-40 bg-gradient-brand text-brand-dark p-3.5 rounded-full shadow-lg shadow-primary/30 hover:opacity-90 active:scale-[0.97] transition-all"
title="Open AI Copilot"
>
<MessageCircle size={22} />

View File

@@ -21,7 +21,7 @@ export function QuickStats({ stats }: QuickStatsProps) {
className={cn('glass-card p-4 fade-in', i === 0 && 'active-glow')}
style={{ animationDelay: `${50 + i * 30}ms` }}
>
<p className="font-label text-[0.625rem] font-medium uppercase tracking-[0.1em] text-muted-foreground">
<p className="font-label text-[0.625rem] font-medium uppercase tracking-widest text-muted-foreground">
{stat.label}
</p>
<p

View File

@@ -56,7 +56,7 @@ export function WeeklyCalendar({ events = {} }: WeeklyCalendarProps) {
borderBottom: day.isToday ? '2px solid #06b6d4' : '1px solid var(--glass-border)',
}}
>
<span className={`font-label text-[0.625rem] uppercase tracking-[0.1em] ${day.isToday ? 'text-cyan-400' : 'text-muted-foreground'}`}>
<span className={`font-label text-[0.625rem] uppercase tracking-widest ${day.isToday ? 'text-cyan-400' : 'text-muted-foreground'}`}>
{day.label}
</span>
<div className={`text-sm font-heading font-bold ${day.isToday ? 'text-foreground' : 'text-muted-foreground'}`}>

View File

@@ -45,7 +45,7 @@ export function AIPromptDialog({
<div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */}
<div
className="absolute inset-0 bg-black/60 backdrop-blur-sm"
className="absolute inset-0 bg-black/60 backdrop-blur-xs"
onClick={() => !isGenerating && onClose()}
/>
@@ -68,7 +68,7 @@ export function AIPromptDialog({
placeholder={`Example: "A flow for troubleshooting VPN connectivity issues when users can't connect to the corporate network"`}
rows={4}
disabled={isGenerating}
className="w-full rounded-xl border border-border bg-background px-4 py-3 text-sm text-foreground placeholder:text-muted-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-none resize-none disabled:opacity-50"
className="w-full rounded-xl border border-border bg-background px-4 py-3 text-sm text-foreground placeholder:text-muted-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-hidden resize-none disabled:opacity-50"
autoFocus
/>
@@ -83,14 +83,14 @@ export function AIPromptDialog({
<button
onClick={onClose}
disabled={isGenerating}
className="rounded-[10px] bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] px-4 py-2 text-sm text-foreground hover:border-[rgba(255,255,255,0.12)] transition-colors disabled:opacity-50"
className="rounded-[10px] bg-[rgba(255,255,255,0.04)] border border-brand-border px-4 py-2 text-sm text-foreground hover:border-[rgba(255,255,255,0.12)] transition-colors disabled:opacity-50"
>
Cancel
</button>
<button
onClick={handleGenerate}
disabled={!prompt.trim() || isGenerating}
className="flex items-center gap-2 rounded-[10px] bg-gradient-brand px-4 py-2 text-sm font-semibold text-[#101114] shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-50"
className="flex items-center gap-2 rounded-[10px] bg-gradient-brand px-4 py-2 text-sm font-semibold text-brand-dark shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-50"
>
{isGenerating ? (
<>

View File

@@ -50,7 +50,7 @@ export function ChatTab({ messages, input, onInputChange, onSend, isLoading }: C
className={`max-w-[85%] rounded-xl px-3.5 py-2.5 text-[0.8125rem] leading-relaxed ${
msg.role === 'user'
? 'bg-primary/15 text-foreground'
: 'bg-[rgba(255,255,255,0.04)] text-foreground border border-[rgba(255,255,255,0.06)]'
: 'bg-[rgba(255,255,255,0.04)] text-foreground border border-brand-border'
}`}
>
<MarkdownContent content={msg.content} className="text-[0.8125rem] leading-relaxed" />
@@ -59,7 +59,7 @@ export function ChatTab({ messages, input, onInputChange, onSend, isLoading }: C
))}
{isLoading && (
<div className="flex justify-start">
<div className="bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] rounded-xl px-3.5 py-2.5">
<div className="bg-[rgba(255,255,255,0.04)] border border-brand-border rounded-xl px-3.5 py-2.5">
<Loader2 size={16} className="animate-spin text-primary" />
</div>
</div>
@@ -77,14 +77,14 @@ export function ChatTab({ messages, input, onInputChange, onSend, isLoading }: C
onKeyDown={handleKeyDown}
placeholder="Ask AI to help..."
rows={1}
className="flex-1 resize-none rounded-xl border bg-card text-foreground text-[0.8125rem] placeholder:text-muted-foreground px-3.5 py-2.5 focus:outline-none focus:border-[rgba(6,182,212,0.3)]"
className="flex-1 resize-none rounded-xl border bg-card text-foreground text-[0.8125rem] placeholder:text-muted-foreground px-3.5 py-2.5 focus:outline-hidden focus:border-[rgba(6,182,212,0.3)]"
style={{ borderColor: 'var(--glass-border)' }}
disabled={isLoading}
/>
<button
onClick={onSend}
disabled={!input.trim() || isLoading}
className="bg-gradient-brand text-[#101114] p-2.5 rounded-xl hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-40"
className="bg-gradient-brand text-brand-dark p-2.5 rounded-xl hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-40"
>
<Send size={16} />
</button>

View File

@@ -65,7 +65,7 @@ export function EditorAIPanel({
</div>
<button
onClick={onClose}
className="p-1.5 rounded-lg hover:bg-[rgba(255,255,255,0.06)] text-muted-foreground hover:text-foreground transition-colors"
className="p-1.5 rounded-lg hover:bg-brand-border text-muted-foreground hover:text-foreground transition-colors"
>
<X size={16} />
</button>

View File

@@ -29,7 +29,7 @@ export function NodeSummary({ node, flowName, flowType, nodeCount }: NodeSummary
{flowName || 'Untitled Flow'}
</span>
</div>
<div className="mt-1 flex items-center gap-3 font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
<div className="mt-1 flex items-center gap-3 font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
<span>{flowType || 'flow'}</span>
{nodeCount !== undefined && <span>{nodeCount} nodes</span>}
</div>
@@ -44,7 +44,7 @@ export function NodeSummary({ node, flowName, flowType, nodeCount }: NodeSummary
<div className="border-b px-3 py-2.5" style={{ borderColor: 'var(--glass-border)' }}>
<div className="flex items-center gap-2">
<Icon className={`h-3.5 w-3.5 ${colorClass}`} />
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
<span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
{node.type}
</span>
</div>

View File

@@ -28,7 +28,7 @@ export function SuggestionsTab({ suggestions }: SuggestionsTabProps) {
return (
<div key={s.id} className="rounded-lg border border-border bg-[rgba(255,255,255,0.02)] px-3 py-2">
<div className="flex items-center justify-between">
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
<span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
{s.action_type.replace(/_/g, ' ')}
</span>
<span className={`flex items-center gap-1 text-xs ${config.color}`}>
@@ -39,7 +39,7 @@ export function SuggestionsTab({ suggestions }: SuggestionsTabProps) {
{s.target_node_id && (
<p className="mt-1 text-xs text-muted-foreground truncate">Node: {s.target_node_id}</p>
)}
<p className="mt-0.5 font-label text-[0.625rem] text-[#5a6170]">
<p className="mt-0.5 font-label text-[0.625rem] text-brand-text-muted">
{new Date(s.created_at).toLocaleDateString()}
</p>
</div>

View File

@@ -24,7 +24,7 @@ export function GuideCard({ guide }: GuideCardProps) {
<p className="text-xs text-muted-foreground leading-relaxed">
{guide.summary}
</p>
<span className="mt-2 inline-block font-label text-[0.625rem] uppercase tracking-[0.1em] text-primary">
<span className="mt-2 inline-block font-label text-[0.625rem] uppercase tracking-widest text-primary">
{guide.sections.length} {guide.sections.length === 1 ? 'section' : 'sections'}
</span>
</div>

View File

@@ -87,7 +87,7 @@ export function AppLayout() {
}}
/>
<div className={cn('app-shell relative z-[1]', sidebarCollapsed && 'app-shell--collapsed')}>
<div className={cn('app-shell relative z-1', sidebarCollapsed && 'app-shell--collapsed')}>
{/* Top Bar - spans full width */}
<TopBar />
@@ -109,7 +109,7 @@ export function AppLayout() {
{mobileMenuOpen && (
<div className="fixed inset-0 z-50 md:hidden">
<div
className="absolute inset-0 bg-black/80 backdrop-blur-sm animate-fade-in"
className="absolute inset-0 bg-black/80 backdrop-blur-xs animate-fade-in"
onClick={() => setMobileMenuOpen(false)}
aria-hidden="true"
/>

View File

@@ -124,10 +124,10 @@ export function CommandPalette({ open, onClose }: CommandPaletteProps) {
if (!open) return null
return (
<div className="fixed inset-0 z-[100] flex items-start justify-center pt-[20vh]">
<div className="fixed inset-0 z-100 flex items-start justify-center pt-[20vh]">
{/* Backdrop */}
<div
className="absolute inset-0 bg-black/60 backdrop-blur-sm animate-fade-in"
className="absolute inset-0 bg-black/60 backdrop-blur-xs animate-fade-in"
onClick={onClose}
/>
@@ -143,7 +143,7 @@ export function CommandPalette({ open, onClose }: CommandPaletteProps) {
onChange={e => { setQuery(e.target.value); setSelectedIndex(0) }}
onKeyDown={handleKeyDown}
placeholder="Search flows, sessions…"
className="flex-1 bg-transparent text-sm text-foreground placeholder:text-muted-foreground outline-none"
className="flex-1 bg-transparent text-sm text-foreground placeholder:text-muted-foreground outline-hidden"
/>
<kbd className="rounded border border-border bg-background px-1.5 py-0.5 font-label text-[0.625rem] text-muted-foreground">
ESC

View File

@@ -33,7 +33,7 @@ export function EmailVerificationBanner() {
return (
<div className="flex items-center gap-3 border-b border-amber-400/20 bg-amber-400/5 px-4 py-2 text-sm">
<AlertTriangle className="h-4 w-4 flex-shrink-0 text-amber-400" />
<AlertTriangle className="h-4 w-4 shrink-0 text-amber-400" />
<span className="text-amber-200">
Your email is not verified.
</span>

View File

@@ -70,8 +70,8 @@ export function QuickLaunch({ open, onClose }: QuickLaunchProps) {
if (!open) return null
return (
<div className="fixed inset-0 z-[100] flex items-start justify-center pt-[15vh]">
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm animate-fade-in" onClick={onClose} />
<div className="fixed inset-0 z-100 flex items-start justify-center pt-[15vh]">
<div className="absolute inset-0 bg-black/60 backdrop-blur-xs animate-fade-in" onClick={onClose} />
<div ref={containerRef} className="relative w-full max-w-md rounded-xl border border-border bg-card shadow-2xl animate-scale-in">
<div className="flex items-center justify-between border-b border-border px-4 py-3">
<h3 className="text-sm font-heading font-semibold text-foreground">Quick Launch</h3>

View File

@@ -102,7 +102,7 @@ export function AddToFolderMenu({ treeId, onFolderCreated }: AddToFolderMenuProp
<div
className={cn(
'absolute right-0 top-full z-20 mt-1 w-48 rounded-md border border-border',
'bg-card backdrop-blur-sm py-1 shadow-lg'
'bg-card backdrop-blur-xs py-1 shadow-lg'
)}
>
{isLoading ? (

View File

@@ -174,7 +174,7 @@ export function FolderEditModal({
return (
<div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */}
<div className="absolute inset-0 bg-black/80 backdrop-blur-sm" onClick={onClose} />
<div className="absolute inset-0 bg-black/80 backdrop-blur-xs" onClick={onClose} />
{/* Modal */}
<div className="relative z-10 w-full max-w-md bg-card border border-border rounded-2xl p-6 shadow-lg">
@@ -202,7 +202,7 @@ export function FolderEditModal({
className={cn(
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
'bg-card text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'border-border'
)}
autoFocus
@@ -221,7 +221,7 @@ export function FolderEditModal({
className={cn(
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
'bg-card text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'border-border'
)}
>

View File

@@ -162,7 +162,7 @@ function FolderItem({
<div
className={cn(
'absolute right-0 top-full z-10 mt-1 w-40 rounded-md border border-border',
'bg-card backdrop-blur-sm py-1 shadow-lg'
'bg-card backdrop-blur-xs py-1 shadow-lg'
)}
>
<button
@@ -362,7 +362,7 @@ export function FolderSidebar({
{/* Mobile backdrop */}
{mobileOpen && (
<div
className="fixed inset-0 z-40 bg-black/80 backdrop-blur-sm md:hidden"
className="fixed inset-0 z-40 bg-black/80 backdrop-blur-xs md:hidden"
onClick={onMobileClose}
aria-hidden="true"
/>
@@ -461,7 +461,7 @@ export function FolderSidebar({
<div
className={cn(
'fixed z-50 w-44 rounded-md border border-border',
'bg-card backdrop-blur-sm py-1 shadow-lg'
'bg-card backdrop-blur-xs py-1 shadow-lg'
)}
style={{ left: contextMenu.x, top: contextMenu.y }}
onClick={(e) => e.stopPropagation()}

View File

@@ -87,7 +87,7 @@ export function ForkModal({ treeId, treeName, onClose }: ForkModalProps) {
maxLength={255}
className={cn(
'w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
</div>
@@ -105,7 +105,7 @@ export function ForkModal({ treeId, treeName, onClose }: ForkModalProps) {
placeholder="e.g. customizing for a specific client…"
className={cn(
'w-full resize-none rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
</div>

View File

@@ -143,7 +143,7 @@ export function ImportFlowModal({ onClose }: ImportFlowModalProps) {
/>
{parseError && (
<div className="flex items-start gap-2 rounded-lg border border-rose-500/20 bg-rose-500/5 px-3 py-2">
<AlertTriangle className="mt-0.5 h-4 w-4 flex-shrink-0 text-rose-400" />
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-rose-400" />
<p className="text-xs text-rose-400">{parseError}</p>
</div>
)}
@@ -165,7 +165,7 @@ export function ImportFlowModal({ onClose }: ImportFlowModalProps) {
maxLength={255}
className={cn(
'w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
</div>

View File

@@ -114,7 +114,7 @@ export function ShareTreeModal({ tree, isOpen, onClose }: ShareTreeModalProps) {
<div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */}
<div
className="absolute inset-0 bg-black/80 backdrop-blur-sm"
className="absolute inset-0 bg-black/80 backdrop-blur-xs"
onClick={onClose}
/>
@@ -221,7 +221,7 @@ export function ShareTreeModal({ tree, isOpen, onClose }: ShareTreeModalProps) {
type="text"
value={activeShare.share_url}
readOnly
className="flex-1 bg-transparent text-sm text-foreground outline-none"
className="flex-1 bg-transparent text-sm text-foreground outline-hidden"
/>
<button
onClick={handleCopyLink}

View File

@@ -30,7 +30,7 @@ export function SortDropdown({ value, onChange, className }: SortDropdownProps)
onChange={(e) => onChange(e.target.value as SortBy)}
className={cn(
'rounded-md border border-border bg-card px-3 py-1.5 text-sm',
'text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
>
{sortOptions.map((option) => (

View File

@@ -37,20 +37,20 @@ export function TreeListView({
{trees.map((tree) => (
<div
key={tree.id}
className="flex items-center gap-4 bg-card border border-border rounded-2xl p-4 transition-all hover:border-primary/30 hover:shadow-sm"
className="flex items-center gap-4 bg-card border border-border rounded-2xl p-4 transition-all hover:border-primary/30 hover:shadow-xs"
>
{/* Left: Name and Description */}
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<h3 className="font-semibold text-foreground truncate">{tree.name}</h3>
{tree.status === 'draft' && (
<span className="inline-flex items-center gap-1 rounded-full bg-yellow-400/10 px-2 py-0.5 text-xs font-medium text-yellow-400 flex-shrink-0">
<span className="inline-flex items-center gap-1 rounded-full bg-yellow-400/10 px-2 py-0.5 text-xs font-medium text-yellow-400 shrink-0">
<FileText className="h-3 w-3" />
Draft
</span>
)}
{tree.tree_type === 'maintenance' && (
<span className="inline-flex items-center gap-1 rounded-full border border-amber-500/30 bg-amber-500/10 px-2 py-0.5 font-label text-[0.625rem] uppercase tracking-wide text-amber-400 flex-shrink-0">
<span className="inline-flex items-center gap-1 rounded-full border border-amber-500/30 bg-amber-500/10 px-2 py-0.5 font-label text-[0.625rem] uppercase tracking-wide text-amber-400 shrink-0">
<Wrench className="h-3 w-3" />
Maintenance
</span>
@@ -62,11 +62,11 @@ export function TreeListView({
)}
{tree.is_public ? (
<span title="Public tree">
<Globe className="h-3.5 w-3.5 text-muted-foreground flex-shrink-0" />
<Globe className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
</span>
) : (
<span title="Private tree">
<Lock className="h-3.5 w-3.5 text-muted-foreground flex-shrink-0" />
<Lock className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
</span>
)}
</div>
@@ -90,7 +90,7 @@ export function TreeListView({
</div>
{/* Right: Metadata and Actions */}
<div className="flex items-center gap-3 flex-shrink-0">
<div className="flex items-center gap-3 shrink-0">
<div className="hidden sm:flex flex-col items-end text-xs text-muted-foreground">
<span>v{tree.version}</span>
<span>{tree.usage_count} uses</span>

View File

@@ -173,13 +173,13 @@ export function TreeTableView({
{tree.name}
</span>
{tree.status === 'draft' && (
<span className="inline-flex items-center gap-1 rounded-full bg-yellow-400/10 px-2 py-0.5 text-xs font-medium text-yellow-400 flex-shrink-0">
<span className="inline-flex items-center gap-1 rounded-full bg-yellow-400/10 px-2 py-0.5 text-xs font-medium text-yellow-400 shrink-0">
<FileText className="h-3 w-3" />
Draft
</span>
)}
{tree.tree_type === 'maintenance' && (
<span className="inline-flex items-center gap-1 rounded-full border border-amber-500/30 bg-amber-500/10 px-2 py-0.5 font-label text-[0.625rem] uppercase tracking-wide text-amber-400 flex-shrink-0">
<span className="inline-flex items-center gap-1 rounded-full border border-amber-500/30 bg-amber-500/10 px-2 py-0.5 font-label text-[0.625rem] uppercase tracking-wide text-amber-400 shrink-0">
<Wrench className="h-3 w-3" />
Maintenance
</span>
@@ -191,11 +191,11 @@ export function TreeTableView({
)}
{tree.is_public ? (
<span title="Public tree">
<Globe className="h-3.5 w-3.5 text-muted-foreground flex-shrink-0" />
<Globe className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
</span>
) : (
<span title="Private tree">
<Lock className="h-3.5 w-3.5 text-muted-foreground flex-shrink-0" />
<Lock className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
</span>
)}
</div>

View File

@@ -76,7 +76,7 @@ export function BatchLaunchModal({ treeId, treeName, onClose, onLaunched }: Batc
]
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs">
<div className="w-full max-w-lg rounded-xl border border-border bg-card shadow-2xl">
{/* Header */}
<div className="flex items-center justify-between border-b border-border px-6 py-4">
@@ -116,7 +116,7 @@ export function BatchLaunchModal({ treeId, treeName, onClose, onLaunched }: Batc
Server names (one per line)
</label>
<textarea
className="h-40 w-full rounded-lg border border-border bg-card px-3 py-2 text-[0.875rem] text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="h-40 w-full rounded-lg border border-border bg-card px-3 py-2 text-[0.875rem] text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
placeholder={"RDS-01\nRDS-02\nRDS-03"}
value={manualInput}
onChange={e => setManualInput(e.target.value)}

View File

@@ -132,7 +132,7 @@ export function BatchStatusCard({ session, targetLabel, treeId, batchId }: Batch
{isNotStarted && (
<button
onClick={handleStart}
className="flex items-center gap-1.5 rounded-lg bg-gradient-brand px-3 py-1.5 text-[0.8125rem] font-medium text-white shadow-sm shadow-primary/20 hover:opacity-90 transition-opacity"
className="flex items-center gap-1.5 rounded-lg bg-gradient-brand px-3 py-1.5 text-[0.8125rem] font-medium text-white shadow-xs shadow-primary/20 hover:opacity-90 transition-opacity"
>
<Play className="h-3.5 w-3.5" />
Start

View File

@@ -36,13 +36,13 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
value={field.label}
onChange={(e) => onUpdate({ label: e.target.value })}
placeholder="Field label"
className="min-w-0 flex-1 rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="min-w-0 flex-1 rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
<select
value={field.field_type}
onChange={(e) => onUpdate({ field_type: e.target.value as IntakeFieldType })}
className="rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
>
{FIELD_TYPE_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>{opt.label}</option>
@@ -84,7 +84,7 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
value={field.variable_name}
onChange={(e) => onUpdate({ variable_name: e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, '') })}
placeholder="e.g. server_name"
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
<p className="mt-0.5 text-[10px] text-muted-foreground">Used as [VAR:{field.variable_name}]</p>
</div>
@@ -96,7 +96,7 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
value={field.placeholder || ''}
onChange={(e) => onUpdate({ placeholder: e.target.value || undefined })}
placeholder="Hint text"
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>
@@ -107,7 +107,7 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
value={field.help_text || ''}
onChange={(e) => onUpdate({ help_text: e.target.value || undefined })}
placeholder="Description or instructions"
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>
@@ -118,7 +118,7 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
value={field.default_value || ''}
onChange={(e) => onUpdate({ default_value: e.target.value || undefined })}
placeholder="Pre-filled value"
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>
@@ -129,7 +129,7 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
value={field.group_name || ''}
onChange={(e) => onUpdate({ group_name: e.target.value || undefined })}
placeholder="e.g. Network Settings"
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>
@@ -144,7 +144,7 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
}}
placeholder="Option 1&#10;Option 2&#10;Option 3"
rows={3}
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>
)}

View File

@@ -18,7 +18,7 @@ export function IntakeFormBuilder() {
</div>
{intakeForm.length === 0 ? (
<div className="rounded-lg border border-dashed border-border bg-white/[0.02] py-8 text-center">
<div className="rounded-lg border border-dashed border-border bg-white/2 py-8 text-center">
<FileText className="mx-auto mb-2 h-8 w-8 text-muted-foreground" />
<p className="text-sm text-muted-foreground">No intake form fields yet</p>
<p className="mt-1 text-xs text-muted-foreground">

View File

@@ -154,7 +154,7 @@ export function MaintenanceScheduleSection({ treeId, onScheduleLoaded }: Mainten
<select
value={frequency}
onChange={(e) => setFrequency(e.target.value)}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
>
{FREQUENCY_OPTIONS.map(opt => (
<option key={opt.value} value={opt.value}>{opt.label}</option>
@@ -172,7 +172,7 @@ export function MaintenanceScheduleSection({ treeId, onScheduleLoaded }: Mainten
max={23}
value={hour}
onChange={(e) => setHour(Number(e.target.value))}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>
<div>
@@ -183,7 +183,7 @@ export function MaintenanceScheduleSection({ treeId, onScheduleLoaded }: Mainten
max={59}
value={minute}
onChange={(e) => setMinute(Number(e.target.value))}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>
</div>
@@ -194,7 +194,7 @@ export function MaintenanceScheduleSection({ treeId, onScheduleLoaded }: Mainten
<select
value={timezone}
onChange={(e) => setTimezone(e.target.value)}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
>
{TIMEZONE_OPTIONS.map(tz => (
<option key={tz} value={tz}>{tz}</option>
@@ -209,7 +209,7 @@ export function MaintenanceScheduleSection({ treeId, onScheduleLoaded }: Mainten
<select
value={selectedTargetListId}
onChange={(e) => setSelectedTargetListId(e.target.value)}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
>
<option value="">None manual targets only</option>
{targetLists.map(tl => (

View File

@@ -41,7 +41,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
value={step.title}
onChange={(e) => onUpdate({ title: e.target.value })}
placeholder="Section title"
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>
</div>
@@ -74,7 +74,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
type="text"
value={step.title}
onChange={(e) => onUpdate({ title: e.target.value })}
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>
@@ -90,7 +90,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
onChange={(e) => onUpdate({ estimated_minutes: e.target.value ? parseInt(e.target.value) : undefined })}
placeholder="—"
min={1}
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>
@@ -102,7 +102,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
onChange={(e) => onUpdate({ description: e.target.value })}
placeholder="Step instructions. Use [VAR:name] for variables."
rows={4}
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
{availableVariables.length > 0 && (
<div className="mt-1 flex flex-wrap gap-1">
@@ -131,7 +131,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
onChange={(e) => onUpdate({ commands: e.target.value || undefined })}
placeholder="Install-WindowsFeature AD-Domain-Services -IncludeManagementTools"
rows={3}
className="w-full rounded border border-border bg-card px-3 py-2 font-mono text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded border border-border bg-card px-3 py-2 font-mono text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>
@@ -181,7 +181,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
onChange={(e) => onUpdate({ warning_text: e.target.value || undefined })}
placeholder="Caution: This will restart the service..."
rows={2}
className="w-full rounded border border-yellow-400/20 bg-yellow-400/5 px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-yellow-400/30 focus:outline-none focus:ring-1 focus:ring-yellow-400/20"
className="w-full rounded border border-yellow-400/20 bg-yellow-400/5 px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-yellow-400/30 focus:outline-hidden focus:ring-1 focus:ring-yellow-400/20"
/>
</div>
)}
@@ -194,7 +194,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
value={step.expected_outcome || ''}
onChange={(e) => onUpdate({ expected_outcome: e.target.value || undefined })}
placeholder="Server should respond with..."
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>
@@ -210,7 +210,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
value={step.verification_prompt || ''}
onChange={(e) => onUpdate({ verification_prompt: e.target.value || undefined })}
placeholder="Confirm the role was installed"
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>
<div>
@@ -218,7 +218,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
<select
value={step.verification_type || ''}
onChange={(e) => onUpdate({ verification_type: e.target.value as 'checkbox' | 'text_input' || undefined })}
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
>
<option value="">None</option>
<option value="checkbox">Checkbox (confirm done)</option>
@@ -239,7 +239,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
value={step.reference_url || ''}
onChange={(e) => onUpdate({ reference_url: e.target.value || undefined })}
placeholder="https://learn.microsoft.com/..."
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>
<div className="flex items-end pb-1">
@@ -266,7 +266,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
onChange={(e) => onUpdate({
library_visibility: e.target.value === '' ? undefined : e.target.value as 'team' | 'public'
})}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
>
<option value="">Inherit from flow</option>
<option value="team">Team only</option>

View File

@@ -145,7 +145,7 @@ export function StepList({ onStepContextMenu }: StepListProps) {
type="text"
value={step.title}
onChange={(e) => updateStep(step.id, { title: e.target.value })}
className="flex-1 bg-transparent text-sm text-muted-foreground focus:outline-none"
className="flex-1 bg-transparent text-sm text-muted-foreground focus:outline-hidden"
placeholder="Procedure Complete"
/>
<span className="text-[10px] text-muted-foreground">END</span>
@@ -239,7 +239,7 @@ export function StepList({ onStepContextMenu }: StepListProps) {
className={cn(
'group flex flex-col rounded-xl border border-border px-3 py-2.5 transition-colors',
'hover:border-primary/30 hover:bg-accent/50',
isGhost && 'border-l-2 border-dashed !border-l-primary/40 opacity-60'
isGhost && 'border-l-2 border-dashed border-l-primary/40! opacity-60'
)}
onContextMenu={(e) => onStepContextMenu?.(e, step.id)}
>

View File

@@ -71,7 +71,7 @@ export function IntakeFormModal({ isOpen, fields, treeName, onSubmit, onCancel }
const error = errors[field.variable_name]
const baseInputClass = cn(
'w-full rounded-lg border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1',
'w-full rounded-lg border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-hidden focus:ring-1',
error
? 'border-red-400/50 focus:border-red-400 focus:ring-red-400/20'
: 'border-border focus:border-primary focus:ring-primary/20'
@@ -211,7 +211,7 @@ export function IntakeFormModal({ isOpen, fields, treeName, onSubmit, onCancel }
}
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs">
<div className="mx-4 w-full max-w-lg rounded-2xl border border-border bg-[#0a0a0a] shadow-xl">
{/* Header */}
<div className="border-b border-border px-6 py-4">

View File

@@ -154,7 +154,7 @@ export function StepDetail({
{/* Expected outcome */}
{'expected_outcome' in step && step.expected_outcome && (
<div className="rounded-lg border border-border bg-white/[0.02] p-3">
<div className="rounded-lg border border-border bg-white/2 p-3">
<h4 className="mb-1 text-xs font-medium text-muted-foreground">Expected Outcome</h4>
<p className="text-sm text-muted-foreground">{resolve(step.expected_outcome)}</p>
</div>
@@ -162,7 +162,7 @@ export function StepDetail({
{/* Verification */}
{verificationPrompt && (
<div className="rounded-lg border border-border bg-white/[0.02] p-3">
<div className="rounded-lg border border-border bg-white/2 p-3">
<h4 className="mb-2 text-xs font-medium text-muted-foreground">Verification</h4>
{verificationType === 'checkbox' ? (
<label className="flex items-center gap-2 text-sm text-muted-foreground">
@@ -184,7 +184,7 @@ export function StepDetail({
onChange={(e) => onVerificationChange(e.target.value)}
disabled={isCompleted}
placeholder="Enter observed value..."
className="w-full rounded border border-border bg-card px-3 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20 disabled:opacity-50"
className="w-full rounded border border-border bg-card px-3 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20 disabled:opacity-50"
/>
</div>
)}
@@ -200,7 +200,7 @@ export function StepDetail({
onChange={(e) => onNotesChange(e.target.value)}
placeholder="Add notes for this step..."
rows={2}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>
)}

View File

@@ -74,7 +74,7 @@ export function CSATModal({ isOpen, onClose, sessionId }: CSATModalProps) {
placeholder="Any comments? (optional)"
maxLength={500}
rows={3}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20 resize-none"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20 resize-none"
/>
{/* Actions */}

View File

@@ -62,7 +62,7 @@ export function ContinuationModal({
'hover:border-border hover:bg-accent'
)}
>
<div className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-accent">
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-accent">
{nodeTypeIcons[node.type]}
</div>
<div className="min-w-0 flex-1">
@@ -71,7 +71,7 @@ export function ContinuationModal({
{nodeTypeLabels[node.type]}
</p>
</div>
<ArrowRight className="h-4 w-4 flex-shrink-0 text-muted-foreground" />
<ArrowRight className="h-4 w-4 shrink-0 text-muted-foreground" />
</button>
))}
</div>
@@ -98,7 +98,7 @@ export function ContinuationModal({
'hover:border-amber-500 hover:bg-amber-500/10'
)}
>
<div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-amber-500/10">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-amber-500/10">
<GitBranch className="h-5 w-5 text-amber-500" />
</div>
<div className="flex-1">
@@ -111,7 +111,7 @@ export function ContinuationModal({
{/* Warning */}
<div className="mt-3 flex items-start gap-2 rounded-md bg-yellow-400/10 p-3">
<AlertTriangle className="mt-0.5 h-4 w-4 flex-shrink-0 text-yellow-400" />
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-yellow-400" />
<p className="text-sm text-yellow-400">
You'll need to complete this branch manually or mark the issue as resolved.
Custom branches can be saved as a personal tree when your session ends.

View File

@@ -141,7 +141,7 @@ export function ExportPreviewModal({
className={cn(
'h-96 w-full resize-y rounded-md border border-border bg-card p-4',
'font-mono text-sm text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
@@ -153,7 +153,7 @@ export function ExportPreviewModal({
className={cn(
'flex items-center gap-2 rounded-md border border-border px-3 py-2 text-sm font-medium',
'text-muted-foreground hover:bg-accent hover:text-foreground',
'focus:outline-none focus:ring-2 focus:ring-primary/20'
'focus:outline-hidden focus:ring-2 focus:ring-primary/20'
)}
>
{copied ? (
@@ -173,7 +173,7 @@ export function ExportPreviewModal({
onClick={handleDownload}
className={cn(
'flex items-center gap-2 rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-3 py-2 text-sm font-medium',
'hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-primary/20'
'hover:opacity-90 focus:outline-hidden focus:ring-2 focus:ring-primary/20'
)}
>
<Download className="h-4 w-4" />

View File

@@ -83,7 +83,7 @@ export function ForkTreeModal({
<Modal isOpen={isOpen} onClose={onClose} title="Save Custom Tree?" footer={footer}>
<div className="space-y-4">
<div className="flex items-start gap-3 rounded-lg bg-accent/50 p-4">
<div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-accent">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-accent">
<GitFork className="h-5 w-5 text-foreground" />
</div>
<div>
@@ -107,7 +107,7 @@ export function ForkTreeModal({
placeholder="My Custom Tree"
className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20'
'focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20'
)}
/>
</div>
@@ -124,7 +124,7 @@ export function ForkTreeModal({
rows={3}
className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20',
'focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20',
'resize-none'
)}
/>

View File

@@ -33,7 +33,7 @@ export function SaveSessionAsTreeModal({
}
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm">
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs">
<div className="bg-card border border-border w-full max-w-lg rounded-2xl p-6 shadow-lg">
{/* Header */}
<div className="mb-4 flex items-center justify-between">
@@ -70,7 +70,7 @@ export function SaveSessionAsTreeModal({
className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50'
)}
/>
@@ -91,7 +91,7 @@ export function SaveSessionAsTreeModal({
className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50'
)}
/>

View File

@@ -141,7 +141,7 @@ export function ScratchpadSidebar({ sessionId, initialContent, onSave, onOpenCha
{/* Mobile backdrop */}
{!isCollapsed && (
<div
className="fixed inset-0 z-30 bg-black/80 backdrop-blur-sm sm:hidden"
className="fixed inset-0 z-30 bg-black/80 backdrop-blur-xs sm:hidden"
onClick={() => setIsCollapsed(true)}
aria-hidden="true"
/>
@@ -203,7 +203,7 @@ export function ScratchpadSidebar({ sessionId, initialContent, onSave, onOpenCha
className={cn(
'h-full min-h-[200px] w-full resize-none rounded-md border-0 bg-transparent p-0 text-sm',
'text-foreground placeholder:text-muted-foreground',
'focus:outline-none focus:ring-0'
'focus:outline-hidden focus:ring-0'
)}
/>
)}

View File

@@ -102,7 +102,7 @@ export function SessionFilters({ filters, onChange, onClear, trees }: SessionFil
className={cn(
'w-full rounded-md border border-border bg-card py-2 pl-9 pr-3',
'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
</div>
@@ -118,7 +118,7 @@ export function SessionFilters({ filters, onChange, onClear, trees }: SessionFil
className={cn(
'w-full rounded-md border border-border bg-card py-2 pl-9 pr-3',
'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
</div>
@@ -129,7 +129,7 @@ export function SessionFilters({ filters, onChange, onClear, trees }: SessionFil
onChange={(e) => handleFilterChange('treeName', e.target.value)}
className={cn(
'rounded-md border border-border bg-card px-3 py-2',
'text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'sm:min-w-[200px]'
)}
>

View File

@@ -111,7 +111,7 @@ export function SessionOutcomeModal({
className={cn(
'mt-1 block w-full rounded-md border border-border bg-card px-3 py-2',
'text-sm text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
</div>
@@ -126,7 +126,7 @@ export function SessionOutcomeModal({
className={cn(
'mt-1 block w-full rounded-md border border-border bg-card px-3 py-2',
'text-sm text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
</div>

View File

@@ -135,7 +135,7 @@ export function SessionTimeline({
{decisions.map((decision, index) => (
<div key={index} className="ml-1 border-l-2 border-border pl-6">
<div className="relative">
<span className="absolute -left-[1.625rem] top-1 h-2 w-2 rounded-full bg-muted-foreground" />
<span className="absolute -left-6.5 top-1 h-2 w-2 rounded-full bg-muted-foreground" />
<div className="bg-card border border-border rounded-xl p-4">
<div className="flex items-start justify-between gap-2">
<div className="flex-1">

View File

@@ -181,7 +181,7 @@ export function ShareSessionModal({ sessionId, sessionLabel, isOpen, onClose }:
<div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */}
<div
className="absolute inset-0 bg-black/80 backdrop-blur-sm"
className="absolute inset-0 bg-black/80 backdrop-blur-xs"
onClick={onClose}
/>
@@ -265,7 +265,7 @@ export function ShareSessionModal({ sessionId, sessionLabel, isOpen, onClose }:
placeholder="e.g. Training link, Customer escalation"
className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground placeholder-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
maxLength={100}
/>
@@ -299,8 +299,8 @@ export function ShareSessionModal({ sessionId, sessionLabel, isOpen, onClose }:
onChange={(e) => setCustomDatetime(e.target.value)}
className={cn(
'mt-2 w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'[color-scheme:dark]'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'scheme-dark'
)}
/>
)}

View File

@@ -76,7 +76,7 @@ export function SharedSessionTreePreview({
return (
<div className="bg-card border border-border rounded-2xl">
<div className="sticky top-0 z-10 rounded-t-2xl border-b border-border bg-black/80 px-6 py-4 backdrop-blur">
<div className="sticky top-0 z-10 rounded-t-2xl border-b border-border bg-black/80 px-6 py-4 backdrop-blur-sm">
<h3 className="text-sm font-semibold text-foreground">Tree Structure</h3>
</div>
<div className="max-h-[600px] overflow-y-auto py-2">

View File

@@ -77,7 +77,7 @@ export function StepRatingModal({
const getRating = (stepId: string) => ratings.get(stepId)
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm p-4">
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs p-4">
<div className="bg-card border border-border w-full max-w-2xl max-h-[90vh] flex flex-col rounded-2xl shadow-lg">
{/* Header */}
<div className="flex items-center justify-between border-b border-border px-6 py-4">
@@ -174,7 +174,7 @@ export function StepRatingModal({
className={cn(
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:opacity-50'
)}
/>

View File

@@ -21,7 +21,7 @@ export function VariablePromptModal({ prompt, onSubmit, onCancel }: VariableProm
}
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm">
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs">
<div className="bg-card border border-border w-full max-w-md rounded-2xl p-6 shadow-lg">
<h2 className="mb-1 text-lg font-semibold text-foreground">Input Required</h2>
<p className="mb-4 text-sm text-muted-foreground">
@@ -40,7 +40,7 @@ export function VariablePromptModal({ prompt, onSubmit, onCancel }: VariableProm
autoFocus
className={cn(
'w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>

View File

@@ -46,7 +46,7 @@ export function PinnedFlowsSection({ flows, onUnpin }: PinnedFlowsSectionProps)
</button>
<div
className="overflow-hidden transition-[max-height] duration-[250ms] ease-out"
className="overflow-hidden transition-[max-height] duration-250 ease-out"
style={{
maxHeight: collapsed ? 0 : showAll
? `${flows.length * 36 + 40}px`

View File

@@ -65,7 +65,7 @@ export function CustomStepModal({ isOpen, onClose, onInsertStep }: CustomStepMod
}
return (
<div className="fixed inset-0 z-50 flex items-end justify-center bg-black/80 backdrop-blur-sm sm:items-center sm:p-4">
<div className="fixed inset-0 z-50 flex items-end justify-center bg-black/80 backdrop-blur-xs sm:items-center sm:p-4">
<div className="relative flex h-[95vh] w-full max-w-full flex-col border border-border bg-card shadow-lg sm:h-[90vh] sm:max-w-4xl sm:rounded-2xl">
{/* Header */}
<div className="flex items-center justify-between border-b border-border p-4">
@@ -133,7 +133,7 @@ export function CustomStepModal({ isOpen, onClose, onInsertStep }: CustomStepMod
{/* Loading Overlay */}
{isSubmitting && (
<div className="absolute inset-0 flex items-center justify-center bg-black/80 backdrop-blur-sm">
<div className="absolute inset-0 flex items-center justify-center bg-black/80 backdrop-blur-xs">
<div className="flex flex-col items-center gap-3">
<Spinner className="border-t-foreground" />
<p className="text-sm text-muted-foreground">Creating step...</p>

View File

@@ -75,7 +75,7 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
const visibleReviews = showAllReviews ? allTextReviews : allTextReviews.slice(0, 3)
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm">
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs">
<div className="relative flex h-[90vh] w-full max-w-3xl flex-col bg-card border border-border rounded-2xl shadow-lg">
{/* Header */}
<div className="flex items-start justify-between border-b border-border p-6 pb-4">

View File

@@ -179,7 +179,7 @@ export function StepForm({ onSubmit, onCancel, initialData, submitLabel, isSubmi
onChange={(e) => setTitle(e.target.value)}
placeholder="Enter step title"
className={cn(
'w-full rounded-md border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20',
'w-full rounded-md border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20',
errors.title ? 'border-red-400/50' : 'border-border'
)}
/>
@@ -201,7 +201,7 @@ export function StepForm({ onSubmit, onCancel, initialData, submitLabel, isSubmi
placeholder="Describe what to do in this step..."
rows={6}
className={cn(
'w-full rounded-md border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20',
'w-full rounded-md border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20',
errors.instructions ? 'border-red-400/50' : 'border-border'
)}
/>
@@ -221,7 +221,7 @@ export function StepForm({ onSubmit, onCancel, initialData, submitLabel, isSubmi
onChange={(e) => setHelpText(e.target.value)}
placeholder="Additional context or tips..."
rows={3}
className="w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
className="w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
/>
</div>
@@ -296,7 +296,7 @@ export function StepForm({ onSubmit, onCancel, initialData, submitLabel, isSubmi
id="category"
value={categoryId}
onChange={(e) => setCategoryId(e.target.value)}
className="w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
className="w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
>
<option value="">None</option>
{categories.map(cat => (
@@ -318,7 +318,7 @@ export function StepForm({ onSubmit, onCancel, initialData, submitLabel, isSubmi
onChange={(e) => setTagInput(e.target.value)}
onKeyDown={handleTagInputKeyDown}
placeholder="Type tag and press Enter"
className="flex-1 rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
className="flex-1 rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
/>
<button
type="button"
@@ -359,7 +359,7 @@ export function StepForm({ onSubmit, onCancel, initialData, submitLabel, isSubmi
id="visibility"
value={visibility}
onChange={(e) => setVisibility(e.target.value as 'private' | 'team' | 'public')}
className="w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
className="w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
>
<option value="private">Private (only me)</option>
<option value="team">Team (my team members)</option>

View File

@@ -49,7 +49,7 @@ export function StepFormModal({ isOpen, onClose, onSuccess, editingStep }: StepF
} : undefined
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm p-4">
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs p-4">
<div className="relative flex h-[90vh] w-full max-w-2xl flex-col bg-card border border-border rounded-2xl shadow-lg">
{/* Header */}
<div className="flex items-center justify-between border-b border-border p-6 pb-4">

View File

@@ -151,7 +151,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
placeholder="Search steps..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full rounded-md border border-border bg-card py-2 pl-10 pr-4 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
className="w-full rounded-md border border-border bg-card py-2 pl-10 pr-4 text-sm text-foreground placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
/>
</div>
@@ -162,7 +162,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
aria-label="Filter by category"
value={selectedCategoryId || ''}
onChange={(e) => setSelectedCategoryId(e.target.value || undefined)}
className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
>
<option value="">All Categories</option>
{categories.map(cat => (
@@ -175,7 +175,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
aria-label="Filter by step type"
value={selectedStepType || ''}
onChange={(e) => setSelectedStepType((e.target.value as 'decision' | 'action' | 'solution') || undefined)}
className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
>
<option value="">All Types</option>
<option value="decision">Decision</option>
@@ -188,7 +188,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
aria-label="Filter by minimum rating"
value={minRating?.toString() || ''}
onChange={(e) => setMinRating(e.target.value ? Number(e.target.value) : undefined)}
className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
>
<option value="">Any Rating</option>
<option value="4">4+ Stars</option>
@@ -201,7 +201,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
aria-label="Sort steps by"
value={sortBy}
onChange={(e) => setSortBy(e.target.value as 'recent' | 'popular' | 'highest_rated' | 'most_used')}
className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-hidden focus:border-primary focus:ring-1 focus:ring-primary/20"
>
<option value="recent">Most Recent</option>
<option value="popular">Most Popular</option>

View File

@@ -39,7 +39,7 @@ export function AIFixReviewModal({ fixes, onApply, onApplyAll, onClose }: AIFixR
const allHandled = pendingFixes.length === 0
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm p-4">
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs p-4">
<div className="relative flex h-[80vh] w-full max-w-2xl flex-col bg-card border border-border rounded-2xl shadow-lg">
{/* Header */}
<div className="flex items-center justify-between border-b border-border px-6 py-4">
@@ -127,7 +127,7 @@ export function AIFixReviewModal({ fixes, onApply, onApplyAll, onClose }: AIFixR
<div className="mt-3 flex gap-2">
<button
onClick={() => handleApply(fix)}
className="flex items-center gap-1 rounded-md bg-gradient-brand px-3 py-1.5 text-xs font-medium text-white shadow-sm shadow-primary/20 hover:opacity-90"
className="flex items-center gap-1 rounded-md bg-gradient-brand px-3 py-1.5 text-xs font-medium text-white shadow-xs shadow-primary/20 hover:opacity-90"
>
<Check className="h-3 w-3" />
Apply

View File

@@ -140,13 +140,13 @@ function FlowCanvasInner({ selectedNodeId, onNodeSelect, onSelectAnswerType, onN
className="dark bg-accent/30"
>
<Background variant={BackgroundVariant.Dots} gap={20} size={1.5} color="hsl(var(--muted-foreground) / 0.25)" />
<Controls showInteractive={false} className="!bg-card !border-border !shadow-lg" />
<Controls showInteractive={false} className="bg-card! border-border! shadow-lg!" />
{minimapVisible && (
<MiniMap
pannable
zoomable
nodeColor={minimapNodeColor}
className="!bg-card !border-border"
className="bg-card! border-border!"
nodeStrokeWidth={2}
/>
)}

View File

@@ -16,7 +16,7 @@ function FlowCanvasAnswerNodeComponent({ data, selected }: NodeProps) {
return (
<>
<Handle type="target" position={Position.Top} className="!bg-border !w-2 !h-2 !border-0" />
<Handle type="target" position={Position.Top} className="bg-border! w-2! h-2! border-0!" />
<div
className={cn(
@@ -61,7 +61,7 @@ function FlowCanvasAnswerNodeComponent({ data, selected }: NodeProps) {
)}
</div>
<Handle type="source" position={Position.Bottom} className="!bg-border !w-2 !h-2 !border-0" />
<Handle type="source" position={Position.Bottom} className="bg-border! w-2! h-2! border-0!" />
</>
)
}

View File

@@ -62,15 +62,15 @@ function FlowCanvasNodeComponent({ data, selected }: NodeProps) {
return (
<>
{/* Target handle at top */}
<Handle type="target" position={Position.Top} className="!bg-border !w-2 !h-2 !border-0" />
<Handle type="target" position={Position.Top} className="bg-border! w-2! h-2! border-0!" />
<div
onContextMenu={(e) => onContextMenu?.(e, node.id)}
className={cn(
'w-[280px] rounded-xl border border-border bg-card shadow-sm cursor-pointer transition-all',
'w-[280px] rounded-xl border border-border bg-card shadow-xs cursor-pointer transition-all',
config.borderClass,
selected && 'ring-1 ring-primary shadow-md',
isGhost && 'border-dashed !border-primary/40 opacity-60'
isGhost && 'border-dashed border-primary/40! opacity-60'
)}
>
{/* Header */}
@@ -175,7 +175,7 @@ function FlowCanvasNodeComponent({ data, selected }: NodeProps) {
</div>
{/* Source handle at bottom */}
<Handle type="source" position={Position.Bottom} className="!bg-border !w-2 !h-2 !border-0" />
<Handle type="source" position={Position.Bottom} className="bg-border! w-2! h-2! border-0!" />
</>
)
}

View File

@@ -28,7 +28,7 @@ export function MetadataSidePanel({ isOpen, onClose }: MetadataSidePanelProps) {
<>
{/* Backdrop — click to close */}
<div
className="fixed inset-0 z-40 bg-background/40 backdrop-blur-sm"
className="fixed inset-0 z-40 bg-background/40 backdrop-blur-xs"
onClick={onClose}
aria-hidden="true"
/>

View File

@@ -62,7 +62,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
className={cn(
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
'bg-card text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
titleError ? 'border-red-400' : 'border-border'
)}
/>
@@ -107,7 +107,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
className={cn(
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary'
)}
/>
)}
@@ -134,7 +134,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
className={cn(
'block w-full rounded-md border border-border px-3 py-2 font-mono text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary'
)}
/>
)}
@@ -154,7 +154,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
className={cn(
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary'
)}
/>
</div>
@@ -195,7 +195,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
className={cn(
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
'bg-card text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
>
<option value="">Link to existing node...</option>

View File

@@ -104,7 +104,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
className={cn(
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
'bg-card text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
questionError ? 'border-red-400' : 'border-border'
)}
/>
@@ -126,7 +126,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
className={cn(
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary'
)}
/>
</div>
@@ -187,7 +187,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
className={cn(
'block w-full rounded-md border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary',
optionLabelError ? 'border-red-400' : 'border-border'
)}
/>

View File

@@ -59,7 +59,7 @@ export function NodeFormResolution({ node, onUpdate }: NodeFormResolutionProps)
className={cn(
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
'bg-card text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
titleError ? 'border-red-400' : 'border-border'
)}
/>
@@ -103,7 +103,7 @@ Document what was done and the outcome.
className={cn(
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary'
)}
/>
)}
@@ -134,7 +134,7 @@ Document what was done and the outcome.
className={cn(
'block w-full rounded-md border border-border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary'
)}
/>
</div>

View File

@@ -184,7 +184,7 @@ function NodeListItem({
'group flex items-center gap-1 rounded-md px-2 py-1.5 text-sm transition-colors cursor-pointer',
isRootNode
? isSelected
? 'bg-blue-500/20 ring-2 ring-blue-500 shadow-sm'
? 'bg-blue-500/20 ring-2 ring-blue-500 shadow-xs'
: 'bg-blue-500/10 border border-blue-500/30 hover:bg-blue-500/15'
: isSelected
? 'bg-primary/10 ring-1 ring-primary'
@@ -581,7 +581,7 @@ export function NodeList() {
{/* Add Node Type Selector */}
{addingToParent && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm">
<div className="fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-xs">
<div className="w-full max-w-xs rounded-lg border border-border bg-card p-4 shadow-lg">
<h3 className="mb-3 text-sm font-semibold">Select Node Type</h3>
<div className="space-y-2">

View File

@@ -167,7 +167,7 @@ export function NodePicker({
className={cn(
'flex-1 rounded-md border border-border px-2 py-1 text-sm',
'bg-card text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
</div>
@@ -201,7 +201,7 @@ export function NodePicker({
className={cn(
'block w-full rounded-md border px-3 py-2 text-sm',
'bg-card text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
error ? 'border-red-400' : 'border-border'
)}
>

View File

@@ -64,7 +64,7 @@ interface AddNodePickerProps {
function AddNodePicker({ onSelect, onCancel }: AddNodePickerProps) {
return (
<div className="flex items-center gap-2 rounded-xl border border-dashed border-primary/40 bg-card px-3 py-2 shadow-sm">
<div className="flex items-center gap-2 rounded-xl border border-dashed border-primary/40 bg-card px-3 py-2 shadow-xs">
<span className="text-xs text-muted-foreground shrink-0">Add:</span>
<button

View File

@@ -168,7 +168,7 @@ export function TreeCanvasNode({
return (
<div
className={cn(
'relative rounded-xl border border-border bg-card shadow-sm transition-all duration-150',
'relative rounded-xl border border-border bg-card shadow-xs transition-all duration-150',
config.borderClass,
isExpanded && 'ring-1 ring-primary shadow-md',
isSelected && !isExpanded && 'ring-1 ring-primary/50',

View File

@@ -73,7 +73,7 @@ export function TreeMetadataForm() {
className={cn(
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
'bg-card text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
nameError ? 'border-red-400' : 'border-border'
)}
/>
@@ -94,7 +94,7 @@ export function TreeMetadataForm() {
className={cn(
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
'bg-card text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
</div>
@@ -112,7 +112,7 @@ export function TreeMetadataForm() {
className={cn(
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
'bg-card text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
>
<option value="">No category</option>
@@ -134,7 +134,7 @@ export function TreeMetadataForm() {
className={cn(
'block min-w-0 flex-1 rounded-md border border-border px-3 py-2 text-sm',
'bg-card text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
autoFocus
/>

View File

@@ -76,7 +76,7 @@ export function ValidationSummary({ errors, onSelectNode, onFixWithAI, isFixing
'flex items-center gap-1.5 rounded-md px-3 py-1 text-xs font-medium transition-colors',
isFixing
? 'bg-primary/10 text-primary cursor-wait'
: 'bg-gradient-brand text-white shadow-sm shadow-primary/20 hover:opacity-90'
: 'bg-gradient-brand text-white shadow-xs shadow-primary/20 hover:opacity-90'
)}
>
{isFixing ? (
@@ -109,7 +109,7 @@ export function ValidationSummary({ errors, onSelectNode, onFixWithAI, isFixing
: 'cursor-default'
)}
>
<AlertCircle className="mt-0.5 h-4 w-4 flex-shrink-0 text-red-400" />
<AlertCircle className="mt-0.5 h-4 w-4 shrink-0 text-red-400" />
<div className="flex-1">
<p className="text-red-400">{error.message}</p>
{error.nodeId && (
@@ -133,7 +133,7 @@ export function ValidationSummary({ errors, onSelectNode, onFixWithAI, isFixing
: 'cursor-default'
)}
>
<AlertTriangle className="mt-0.5 h-4 w-4 flex-shrink-0 text-yellow-400" />
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-yellow-400" />
<div className="flex-1">
<p className="text-yellow-400">{warning.message}</p>
{warning.nodeId && (

View File

@@ -186,7 +186,7 @@ export function TreePreviewNode({
{/* Solution path indicator - shows when this branch leads to a solution */}
{leadsTosolution && (
<div
className="absolute -top-1.5 -right-1.5 flex items-center justify-center rounded-full bg-green-500/90 p-0.5 shadow-sm"
className="absolute -top-1.5 -right-1.5 flex items-center justify-center rounded-full bg-green-500/90 p-0.5 shadow-xs"
title="This branch leads to a solution"
>
<CheckCircle className="h-3 w-3 text-foreground" />

View File

@@ -3,14 +3,14 @@ import { cn } from '@/lib/utils'
import { Spinner } from '@/components/common/Spinner'
const buttonVariants = cva(
'inline-flex items-center justify-center gap-2 font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 active:scale-[0.97]',
'inline-flex items-center justify-center gap-2 font-medium transition-all duration-200 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 active:scale-[0.97]',
{
variants: {
variant: {
primary:
'bg-gradient-brand text-[#101114] font-semibold shadow-lg shadow-primary/20 hover:opacity-90',
'bg-gradient-brand text-brand-dark font-semibold shadow-lg shadow-primary/20 hover:opacity-90',
secondary:
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-foreground hover:border-[rgba(255,255,255,0.12)] hover:bg-[rgba(255,255,255,0.06)]',
'bg-[rgba(255,255,255,0.04)] border border-brand-border text-foreground hover:border-[rgba(255,255,255,0.12)] hover:bg-brand-border',
destructive:
'bg-red-400/10 text-red-400 border border-red-400/20 hover:bg-red-400/20',
ghost:

View File

@@ -12,7 +12,7 @@ export function Input({ className, error, id, ...props }: InputProps) {
className={cn(
'flex h-9 w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground',
'focus:border-[rgba(6,182,212,0.3)] focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-[rgba(6,182,212,0.3)] focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:cursor-not-allowed disabled:opacity-50',
error && 'border-red-400/50 focus:border-red-400 focus:ring-red-400/20',
className

View File

@@ -6,7 +6,7 @@ export function Skeleton({ className, ...props }: SkeletonProps) {
return (
<div
className={cn(
'animate-pulse rounded-lg bg-[rgba(255,255,255,0.06)]',
'animate-pulse rounded-lg bg-brand-border',
className
)}
{...props}

View File

@@ -12,7 +12,7 @@ export function Textarea({ className, error, id, ...props }: TextareaProps) {
className={cn(
'flex w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'placeholder:text-muted-foreground',
'focus:border-[rgba(6,182,212,0.3)] focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-[rgba(6,182,212,0.3)] focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'disabled:cursor-not-allowed disabled:opacity-50',
error && 'border-red-400/50 focus:border-red-400 focus:ring-red-400/20',
className

View File

@@ -1,6 +1,289 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import 'tailwindcss';
@custom-variant dark (&:is(.dark *));
@theme {
--color-brand-gradient-from: #06b6d4;
--color-brand-gradient-to: #22d3ee;
--color-brand-dark: #101114;
--color-brand-dark-card: #14161a;
--color-brand-dark-surface: #14161a;
--color-brand-text-primary: #f8fafc;
--color-brand-text-secondary: #8891a0;
--color-brand-text-muted: #5a6170;
--color-brand-border: rgba(255, 255, 255, 0.06);
--color-border: hsl(var(--border));
--color-input: hsl(var(--input));
--color-ring: hsl(var(--ring));
--color-background: hsl(var(--background));
--color-foreground: hsl(var(--foreground));
--color-primary: hsl(var(--primary));
--color-primary-foreground: hsl(var(--primary-foreground));
--color-secondary: hsl(var(--secondary));
--color-secondary-foreground: hsl(var(--secondary-foreground));
--color-destructive: hsl(var(--destructive));
--color-destructive-foreground: hsl(var(--destructive-foreground));
--color-muted: hsl(var(--muted));
--color-muted-foreground: hsl(var(--muted-foreground));
--color-accent: hsl(var(--accent));
--color-accent-foreground: hsl(var(--accent-foreground));
--color-popover: hsl(var(--popover));
--color-popover-foreground: hsl(var(--popover-foreground));
--color-card: hsl(var(--card));
--color-card-foreground: hsl(var(--card-foreground));
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
--font-sans:
IBM Plex Sans, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI,
Roboto, sans-serif;
--font-heading: Bricolage Grotesque, system-ui, sans-serif;
--font-label: JetBrains Mono, monospace;
--background-image-gradient-brand: linear-gradient(
135deg,
#06b6d4 0%,
#22d3ee 100%
);
--background-image-gradient-brand-hover: linear-gradient(
135deg,
#0891b2 0%,
#06b6d4 100%
);
}
/*
The default border color has changed to `currentcolor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentcolor);
}
}
@utility animate-fade-in {
animation: fade-in 200ms ease-out;
}
@utility animate-fade-in-up {
animation: fade-in-up 200ms ease-out;
}
@utility animate-slide-in-left {
animation: slide-in-from-left 200ms ease-out;
}
@utility animate-slide-in-bottom {
animation: slide-in-from-bottom 200ms ease-out;
}
@utility animate-scale-in {
animation: scale-in 150ms ease-out;
}
@utility btn-press {
/* Button press feedback */
@apply active:scale-[0.98] transition-transform;
}
@utility text-gradient-brand {
/* Brand gradient text */
@apply bg-gradient-brand bg-clip-text text-transparent;
}
@utility glass-card {
/* Glass card — interactive with hover lift */
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
border: 1px solid var(--glass-border);
border-radius: 16px;
box-shadow: var(--shadow-float);
transition:
transform 200ms var(--ease-out-smooth),
border-color 200ms var(--ease-out-smooth),
box-shadow 200ms var(--ease-out-smooth);
&:hover {
transform: scale(1.02);
border-color: var(--glass-border-hover);
box-shadow: var(--shadow-float-hover);
}
}
@utility glass-card-static {
/* Glass card — static, no hover transform */
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
border: 1px solid var(--glass-border);
border-radius: 16px;
box-shadow: var(--shadow-float);
}
@utility active-glow {
/* Breathing glow for highlighted stat cards */
animation: breatheGlow 3s ease-in-out infinite alternate;
}
@utility rdp-custom {
@apply text-foreground;
& .rdp-month {
@apply w-full;
}
& .rdp-caption {
@apply flex justify-center items-center mb-4;
}
& .rdp-caption_label {
@apply text-sm font-medium;
}
& .rdp-nav {
@apply flex gap-1;
}
& .rdp-nav_button {
@apply h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100;
}
& .rdp-table {
@apply w-full border-collapse;
}
& .rdp-head_cell {
@apply text-muted-foreground font-normal text-xs;
}
& .rdp-cell {
@apply text-center text-sm p-0;
}
& .rdp-day {
@apply h-9 w-9 p-0 font-normal hover:bg-accent rounded-md transition-colors;
}
& .rdp-day_selected {
@apply bg-primary text-primary-foreground hover:bg-primary/90;
}
& .rdp-day_today {
@apply bg-accent text-accent-foreground;
}
& .rdp-day_outside {
@apply text-muted-foreground opacity-50;
}
& .rdp-day_disabled {
@apply text-muted-foreground opacity-50;
}
& .rdp-day_range_middle {
@apply bg-accent text-accent-foreground;
}
& .rdp-day_hidden {
@apply invisible;
}
}
@utility rdp-month {
.rdp-custom & {
@apply w-full;
}
}
@utility rdp-caption {
.rdp-custom & {
@apply flex justify-center items-center mb-4;
}
}
@utility rdp-caption_label {
.rdp-custom & {
@apply text-sm font-medium;
}
}
@utility rdp-nav {
.rdp-custom & {
@apply flex gap-1;
}
}
@utility rdp-nav_button {
.rdp-custom & {
@apply h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100;
}
}
@utility rdp-table {
.rdp-custom & {
@apply w-full border-collapse;
}
}
@utility rdp-head_cell {
.rdp-custom & {
@apply text-muted-foreground font-normal text-xs;
}
}
@utility rdp-cell {
.rdp-custom & {
@apply text-center text-sm p-0;
}
}
@utility rdp-day {
.rdp-custom & {
@apply h-9 w-9 p-0 font-normal hover:bg-accent rounded-md transition-colors;
}
}
@utility rdp-day_selected {
.rdp-custom & {
@apply bg-primary text-primary-foreground hover:bg-primary/90;
}
}
@utility rdp-day_today {
.rdp-custom & {
@apply bg-accent text-accent-foreground;
}
}
@utility rdp-day_outside {
.rdp-custom & {
@apply text-muted-foreground opacity-50;
}
}
@utility rdp-day_disabled {
.rdp-custom & {
@apply text-muted-foreground opacity-50;
}
}
@utility rdp-day_range_middle {
.rdp-custom & {
@apply bg-accent text-accent-foreground;
}
}
@utility rdp-day_hidden {
.rdp-custom & {
@apply invisible;
}
}
@layer base {
:root {
@@ -86,289 +369,233 @@
}
}
/* App Shell Grid Layout */
.app-shell {
display: grid;
grid-template-columns: var(--sidebar-w) 1fr;
grid-template-rows: 56px 1fr;
height: 100vh;
overflow: hidden;
transition: grid-template-columns 200ms ease;
}
.app-shell--collapsed {
grid-template-columns: 56px 1fr;
}
.topbar {
grid-column: 1 / -1;
}
.sidebar {
min-height: 0;
overflow-y: auto;
}
.main-content {
min-height: 0;
min-width: 0;
overflow-y: auto;
}
/* Mobile: single column */
@media (max-width: 767px) {
@layer utilities {
/* App Shell Grid Layout */
.app-shell {
grid-template-columns: 1fr;
display: grid;
grid-template-columns: var(--sidebar-w) 1fr;
grid-template-rows: 56px 1fr;
height: 100vh;
overflow: hidden;
transition: grid-template-columns 200ms ease;
}
}
/* Staggered fade-in for page sections */
.fade-in {
animation: fadeIn 0.3s ease forwards;
}
.app-shell--collapsed {
grid-template-columns: 56px 1fr;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(6px); }
to { opacity: 1; transform: translateY(0); }
}
/* Animations */
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
.topbar {
grid-column: 1 / -1;
}
@keyframes fade-in-up {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
}
.sidebar {
min-height: 0;
overflow-y: auto;
}
@keyframes slide-in-from-left {
from { transform: translateX(-100%); }
to { transform: translateX(0); }
}
.main-content {
min-height: 0;
min-width: 0;
overflow-y: auto;
}
@keyframes slide-in-from-bottom {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
/* Mobile: single column */
@media (max-width: 767px) {
.app-shell {
grid-template-columns: 1fr;
}
}
@keyframes scale-in {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
/* Staggered fade-in for page sections */
.fade-in {
animation: fadeIn 0.3s ease forwards;
}
@keyframes slideDown {
from { transform: translateY(-100%); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(6px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Animations */
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideInRight {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeInRight {
from { transform: translateX(30px); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes slide-in-from-left {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
@keyframes breatheGlow {
from { box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3), 0 0 20px rgba(6, 182, 212, 0.04); }
to { box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3), 0 0 30px rgba(6, 182, 212, 0.12); }
}
@keyframes slide-in-from-bottom {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes bellWobble {
0% { transform: rotate(0deg); }
20% { transform: rotate(8deg); }
40% { transform: rotate(-6deg); }
60% { transform: rotate(4deg); }
80% { transform: rotate(-2deg); }
100% { transform: rotate(0deg); }
@keyframes scale-in {
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes slideDown {
from {
transform: translateY(-100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes slideInRight {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
@keyframes fadeInRight {
from {
transform: translateX(30px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes breatheGlow {
from {
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.3),
0 0 20px rgba(6, 182, 212, 0.04);
}
to {
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.3),
0 0 30px rgba(6, 182, 212, 0.12);
}
}
@keyframes bellWobble {
0% {
transform: rotate(0deg);
}
20% {
transform: rotate(8deg);
}
40% {
transform: rotate(-6deg);
}
60% {
transform: rotate(4deg);
}
80% {
transform: rotate(-2deg);
}
100% {
transform: rotate(0deg);
}
}
}
@layer utilities {
.animate-fade-in {
animation: fade-in 200ms ease-out;
/* Sonner Toast Customization — outside @layer for higher specificity */
[data-sonner-toast] {
background-color: hsl(var(--card)) !important;
color: hsl(var(--card-foreground)) !important;
border: 1px solid hsl(var(--border)) !important;
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.3) !important;
border-radius: 0.75rem;
font-family: 'IBM Plex Sans', system-ui, sans-serif;
}
.animate-fade-in-up {
animation: fade-in-up 200ms ease-out;
[data-sonner-toast] [data-title] {
font-family: 'IBM Plex Sans', system-ui, sans-serif;
font-weight: 600;
}
.animate-slide-in-left {
animation: slide-in-from-left 200ms ease-out;
[data-sonner-toast][data-type='success'] {
border-color: rgba(52, 211, 153, 0.3) !important;
}
[data-sonner-toast][data-type='success'] [data-icon] {
color: #34d399;
}
.animate-slide-in-bottom {
animation: slide-in-from-bottom 200ms ease-out;
[data-sonner-toast][data-type='error'] {
border-color: rgba(248, 113, 113, 0.3) !important;
}
[data-sonner-toast][data-type='error'] [data-icon] {
color: #f87171;
}
.animate-scale-in {
animation: scale-in 150ms ease-out;
[data-sonner-toast][data-type='info'] {
border-color: hsl(var(--border)) !important;
}
[data-sonner-toast][data-type='info'] [data-icon] {
color: hsl(var(--muted-foreground));
}
/* Button press feedback */
.btn-press {
@apply active:scale-[0.98] transition-transform;
[data-sonner-toast][data-type='warning'] {
border-color: rgba(251, 191, 36, 0.3) !important;
}
[data-sonner-toast][data-type='warning'] [data-icon] {
color: #fbbf24;
}
/* Brand gradient text */
.text-gradient-brand {
@apply bg-gradient-brand bg-clip-text text-transparent;
[data-sonner-toast] [data-close-button] {
color: hsl(var(--muted-foreground));
border-radius: 0.375rem;
transition:
color 150ms,
background-color 150ms;
}
[data-sonner-toast] [data-close-button]:hover {
background-color: hsl(var(--accent));
color: hsl(var(--accent-foreground));
}
/* Glass card — interactive with hover lift */
.glass-card {
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
border: 1px solid var(--glass-border);
border-radius: 16px;
box-shadow: var(--shadow-float);
transition: transform 200ms var(--ease-out-smooth),
border-color 200ms var(--ease-out-smooth),
box-shadow 200ms var(--ease-out-smooth);
}
.glass-card:hover {
transform: scale(1.02);
border-color: var(--glass-border-hover);
box-shadow: var(--shadow-float-hover);
}
/* Glass card — static, no hover transform */
.glass-card-static {
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
border: 1px solid var(--glass-border);
border-radius: 16px;
box-shadow: var(--shadow-float);
}
/* Breathing glow for highlighted stat cards */
.active-glow {
animation: breatheGlow 3s ease-in-out infinite alternate;
}
}
/* Sonner Toast Customization — outside @layer for higher specificity */
[data-sonner-toast] {
background-color: hsl(var(--card)) !important;
color: hsl(var(--card-foreground)) !important;
border: 1px solid hsl(var(--border)) !important;
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.3) !important;
border-radius: 0.75rem;
font-family: 'IBM Plex Sans', system-ui, sans-serif;
}
[data-sonner-toast] [data-title] {
font-family: 'IBM Plex Sans', system-ui, sans-serif;
font-weight: 600;
}
[data-sonner-toast][data-type="success"] {
border-color: rgba(52, 211, 153, 0.3) !important;
}
[data-sonner-toast][data-type="success"] [data-icon] {
color: #34d399;
}
[data-sonner-toast][data-type="error"] {
border-color: rgba(248, 113, 113, 0.3) !important;
}
[data-sonner-toast][data-type="error"] [data-icon] {
color: #f87171;
}
[data-sonner-toast][data-type="info"] {
border-color: hsl(var(--border)) !important;
}
[data-sonner-toast][data-type="info"] [data-icon] {
color: hsl(var(--muted-foreground));
}
[data-sonner-toast][data-type="warning"] {
border-color: rgba(251, 191, 36, 0.3) !important;
}
[data-sonner-toast][data-type="warning"] [data-icon] {
color: #fbbf24;
}
[data-sonner-toast] [data-close-button] {
color: hsl(var(--muted-foreground));
border-radius: 0.375rem;
transition: color 150ms, background-color 150ms;
}
[data-sonner-toast] [data-close-button]:hover {
background-color: hsl(var(--accent));
color: hsl(var(--accent-foreground));
}
/* React Day Picker Customization */
@layer components {
.rdp-custom {
@apply text-foreground;
}
.rdp-custom .rdp-month {
@apply w-full;
}
.rdp-custom .rdp-caption {
@apply flex justify-center items-center mb-4;
}
.rdp-custom .rdp-caption_label {
@apply text-sm font-medium;
}
.rdp-custom .rdp-nav {
@apply flex gap-1;
}
.rdp-custom .rdp-nav_button {
@apply h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100;
}
.rdp-custom .rdp-table {
@apply w-full border-collapse;
}
.rdp-custom .rdp-head_cell {
@apply text-muted-foreground font-normal text-xs;
}
.rdp-custom .rdp-cell {
@apply text-center text-sm p-0;
}
.rdp-custom .rdp-day {
@apply h-9 w-9 p-0 font-normal hover:bg-accent rounded-md transition-colors;
}
.rdp-custom .rdp-day_selected {
@apply bg-primary text-primary-foreground hover:bg-primary/90;
}
.rdp-custom .rdp-day_today {
@apply bg-accent text-accent-foreground;
}
.rdp-custom .rdp-day_outside {
@apply text-muted-foreground opacity-50;
}
.rdp-custom .rdp-day_disabled {
@apply text-muted-foreground opacity-50;
}
.rdp-custom .rdp-day_range_middle {
@apply bg-accent text-accent-foreground;
}
.rdp-custom .rdp-day_hidden {
@apply invisible;
}
/* React Day Picker Customization */
}
/* React Flow dark theme overrides */

View File

@@ -189,7 +189,7 @@ export function AccountSettingsPage() {
className={cn(
'flex-1 rounded-md border border-border bg-card px-3 py-2',
'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
autoFocus
onKeyDown={(e) => {
@@ -367,7 +367,7 @@ export function AccountSettingsPage() {
}}
className={cn(
'rounded-md border border-border bg-card px-2 py-0.5 text-xs',
'text-foreground focus:border-primary focus:outline-none'
'text-foreground focus:border-primary focus:outline-hidden'
)}
>
<option value="engineer">engineer</option>
@@ -415,7 +415,7 @@ export function AccountSettingsPage() {
className={cn(
'flex-1 rounded-md border border-border bg-card px-3 py-2',
'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
<select
@@ -423,7 +423,7 @@ export function AccountSettingsPage() {
onChange={(e) => setInviteRole(e.target.value)}
className={cn(
'rounded-md border border-border bg-card px-3 py-2',
'text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
>
<option value="engineer">Engineer</option>
@@ -609,7 +609,7 @@ export function AccountSettingsPage() {
className={cn(
'mt-2 block w-full max-w-xs rounded-xl border border-border bg-card px-3 py-2',
'text-sm text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
>
<option value="markdown">Markdown (.md)</option>

View File

@@ -223,7 +223,7 @@ export default function AssistantChatPage() {
<div className="w-8 h-8 rounded-full bg-primary/15 flex items-center justify-center">
<Sparkles size={14} className="text-primary" />
</div>
<div className="bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] rounded-2xl px-4 py-3">
<div className="bg-[rgba(255,255,255,0.04)] border border-brand-border rounded-2xl px-4 py-3">
<Loader2 size={16} className="animate-spin text-primary" />
</div>
</div>
@@ -242,7 +242,7 @@ export default function AssistantChatPage() {
onKeyDown={handleKeyDown}
placeholder="Ask about IT, networking, troubleshooting..."
rows={3}
className="flex-1 resize-none rounded-xl border bg-card text-foreground text-sm placeholder:text-muted-foreground px-4 py-3 focus:outline-none focus:border-[rgba(6,182,212,0.3)]"
className="flex-1 resize-none rounded-xl border bg-card text-foreground text-sm placeholder:text-muted-foreground px-4 py-3 focus:outline-hidden focus:border-[rgba(6,182,212,0.3)]"
style={{ borderColor: 'var(--glass-border)' }}
disabled={loading}
/>
@@ -250,7 +250,7 @@ export default function AssistantChatPage() {
<button
onClick={handleSend}
disabled={!input.trim() || loading}
className="bg-gradient-brand text-[#101114] p-3 rounded-xl hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-40"
className="bg-gradient-brand text-brand-dark p-3 rounded-xl hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-40"
title="Send message"
>
<Send size={18} />
@@ -286,7 +286,7 @@ export default function AssistantChatPage() {
</p>
<button
onClick={handleNewChat}
className="bg-gradient-brand text-[#101114] font-semibold text-sm rounded-[10px] px-6 py-2.5 hover:opacity-90 active:scale-[0.97] transition-all"
className="bg-gradient-brand text-brand-dark font-semibold text-sm rounded-[10px] px-6 py-2.5 hover:opacity-90 active:scale-[0.97] transition-all"
>
Start a Conversation
</button>

View File

@@ -97,7 +97,7 @@ export function ChangePasswordPage() {
className={cn(
'block w-full rounded-xl border border-border bg-card px-3 py-2',
'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors'
)}
/>
@@ -116,7 +116,7 @@ export function ChangePasswordPage() {
className={cn(
'block w-full rounded-xl border border-border bg-card px-3 py-2',
'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors'
)}
placeholder="At least 10 characters"
@@ -139,7 +139,7 @@ export function ChangePasswordPage() {
className={cn(
'block w-full rounded-xl border border-border bg-card px-3 py-2',
'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors'
)}
/>
@@ -151,7 +151,7 @@ export function ChangePasswordPage() {
className={cn(
'w-full rounded-xl px-4 py-2.5 text-sm font-semibold btn-press',
'bg-gradient-brand text-white shadow-lg shadow-primary/20 hover:opacity-90',
'focus:outline-none focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-black',
'focus:outline-hidden focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-black',
'disabled:cursor-not-allowed disabled:opacity-50',
'transition-all'
)}

View File

@@ -171,7 +171,7 @@ export function FeedbackPage() {
onChange={e => setEmail(e.target.value)}
placeholder="your@email.com"
required
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-none"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-hidden"
/>
<p className="mt-1 text-xs text-muted-foreground">We'll reply to this address if we need more details.</p>
</div>
@@ -193,7 +193,7 @@ export function FeedbackPage() {
onClick={() => setTypeDropdownOpen(!typeDropdownOpen)}
onKeyDown={handleDropdownKeyDown}
className={cn(
"w-full rounded-lg border border-border bg-card px-3 py-2 text-left flex items-center justify-between focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-none",
"w-full rounded-lg border border-border bg-card px-3 py-2 text-left flex items-center justify-between focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-hidden",
feedbackType ? "text-foreground" : "text-muted-foreground"
)}
>
@@ -244,7 +244,7 @@ export function FeedbackPage() {
required
minLength={10}
rows={6}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-none resize-y"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-hidden resize-y"
/>
<p className="mt-1 text-xs text-muted-foreground">
{message.trim().length < 10

View File

@@ -75,7 +75,7 @@ export function ForgotPasswordPage() {
className={cn(
'block w-full rounded-xl border border-border bg-card px-3 py-2',
'text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors'
)}
placeholder="you@example.com"
@@ -88,7 +88,7 @@ export function ForgotPasswordPage() {
className={cn(
'w-full rounded-xl px-4 py-2.5 text-sm font-semibold btn-press',
'bg-gradient-brand text-white shadow-lg shadow-primary/20 hover:opacity-90',
'focus:outline-none focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-background',
'focus:outline-hidden focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-background',
'disabled:cursor-not-allowed disabled:opacity-50',
'transition-all'
)}

View File

@@ -14,7 +14,7 @@ export default function GuideDetailPage() {
<p className="text-sm text-muted-foreground mb-4">The guide you're looking for doesn't exist.</p>
<Link
to="/guides"
className="bg-gradient-brand text-[#101114] font-semibold text-sm rounded-[10px] px-5 py-2 hover:opacity-90 active:scale-[0.97] transition-all"
className="bg-gradient-brand text-brand-dark font-semibold text-sm rounded-[10px] px-5 py-2 hover:opacity-90 active:scale-[0.97] transition-all"
>
Back to Guides
</Link>
@@ -47,10 +47,10 @@ export default function GuideDetailPage() {
</div>
</div>
<div className="flex items-center gap-4 mt-4 pt-4 border-t" style={{ borderColor: 'var(--glass-border)' }}>
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
<span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
{guide.sections.length} {guide.sections.length === 1 ? 'section' : 'sections'}
</span>
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
<span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
{guide.sections.reduce((acc, s) => acc + s.steps.length, 0)} steps
</span>
</div>

View File

@@ -106,7 +106,7 @@ export function LoginPage() {
className={cn(
'block w-full rounded-[10px] border border-border bg-card px-3 py-2.5',
'text-foreground placeholder:text-muted-foreground',
'focus:border-[rgba(6,182,212,0.3)] focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-[rgba(6,182,212,0.3)] focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors'
)}
placeholder="you@example.com"
@@ -127,7 +127,7 @@ export function LoginPage() {
className={cn(
'block w-full rounded-[10px] border border-border bg-card px-3 py-2.5',
'text-foreground placeholder:text-muted-foreground',
'focus:border-[rgba(6,182,212,0.3)] focus:outline-none focus:ring-1 focus:ring-primary/20',
'focus:border-[rgba(6,182,212,0.3)] focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors'
)}
placeholder="••••••••••"
@@ -145,8 +145,8 @@ export function LoginPage() {
disabled={isLoading}
className={cn(
'w-full rounded-[10px] px-4 py-2.5 text-sm font-semibold',
'bg-gradient-brand text-[#101114] shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97]',
'focus:outline-none focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-background',
'bg-gradient-brand text-brand-dark shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97]',
'focus:outline-hidden focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-background',
'disabled:cursor-not-allowed disabled:opacity-50',
'transition-all'
)}

View File

@@ -90,7 +90,7 @@ export default function MyAnalyticsPage() {
<select
value={period}
onChange={(e) => setPeriod(e.target.value as AnalyticsPeriod)}
className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-primary/20"
className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:outline-hidden focus:ring-1 focus:ring-primary/20"
>
{PERIOD_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>
@@ -298,7 +298,7 @@ export default function MyAnalyticsPage() {
<div className="flex items-center justify-between mb-1">
<div className="flex items-center gap-2">
<div
className="h-2.5 w-2.5 rounded-full flex-shrink-0"
className="h-2.5 w-2.5 rounded-full shrink-0"
style={{
backgroundColor:
OUTCOME_COLORS[outcome] ?? '#94a3b8',

View File

@@ -136,7 +136,7 @@ export function MyTreesPage() {
{showCreateMenu && (
<>
<div className="fixed inset-0 z-10" onClick={() => setShowCreateMenu(false)} />
<div className="absolute right-0 z-20 mt-1 w-56 rounded-lg border border-border bg-card p-1 shadow-xl backdrop-blur-sm">
<div className="absolute right-0 z-20 mt-1 w-56 rounded-lg border border-border bg-card p-1 shadow-xl backdrop-blur-xs">
<Link
to="/trees/new"
onClick={() => setShowCreateMenu(false)}

View File

@@ -255,7 +255,7 @@ export function ProceduralEditorPage() {
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="e.g. Domain Controller Build"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>
@@ -266,7 +266,7 @@ export function ProceduralEditorPage() {
onChange={(e) => setDescription(e.target.value)}
placeholder="Brief description of this procedure..."
rows={2}
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
/>
</div>

Some files were not shown because too many files have changed in this diff Show More